PHP でアノテーション 2

PHP でアノテーション - devworksの続き。

/*
 * @Required
 *
**/
public $var;

と書いておけば、「annotation」ディレクトリの「Required.php」を include_once して new Required() します。
構想としては、上記のように書いておけば Action 実行前に必須チェックを行う。SAStruts みたいな感じですね。次回はアノテーションを取得して Validation を行うところをやろうと思います。
…先にフロントコントローラから作ればよかったな…。
以下ソース。S2PHP5 のソースを参考に作ってます。感謝感謝。

<?php
include_once 'AnnotationFactory.php';
class AnnotationReader {
    /**
     * ReflectionClass
     * @var ReflectionClass
     */
    private $clazz;
    public function __construct($name) {
        $this->clazz = new ReflectionClass($name);
    }
    public function getClassAnnotation() {
        $comment = $this->getDocComment($this->clazz->getDocComment());
        return $this->getAnnotation($comment);
    }

    public function getMethodAnnotation($name) {
        $method = $clazz->getMethod($name);
        $comment = $this->getDocComment($method->getDocComment());
        return $this->getAnnotation($comment);
    }

    public function getFieldAnnotation($name) {
        $field = $clazz->getProperty($name);
        $comment = $this->getDocComment($field->getDocComment());
        return $this->getAnnotation($comment);
    }

    private function getDocComment($docComment) {
        return preg_split("/[\n\r]/", $docComment, -1, PREG_SPLIT_NO_EMPTY);
    }

    private function getAnnotation($comments) {
        $annoObjects = array();
        foreach ($comments as $line) {
            $line = $this->_removeCommentSlashAster($line);
            if (preg_match("/^@\w+$/",$line) or
                preg_match("/^@\w+\s*\(/",$line)) {
                if (preg_match("/^@(\w+)$/",$line,$matches)) {
                    $annoObj = AnnotationFactory::create($matches[1]);
                    $annoObjects[get_class($annoObj)] = $annoObj;
                }
                if (preg_match("/^@(\w+)\s*\((.*)\)/",$line,$matches)) {
                    $annotationType = $matches[1];
                    $items = preg_split("/,/", $matches[2]);
                    $args = array();
                    $argType = array();
                    foreach ($items as $item) {
                        if (preg_match("/^(.+?)=(.+)/s",$item,$matches)) {
                            $key = $this->_removeQuote($matches[1]);
                            $val = $this->_removeQuote($matches[2]);
                            if ($key == "") {
                                // Error
                            }
                            $args[$key] = $val;
                            $argType = AnnotationFactory::ARGS_TYPE_HASH;
                        } else {
                            $item = $this->_removeQuote($item);
                            $args[] = $item;
                            echo count($args);
                            echo '<br />';
                            $argType = AnnotationFactory::ARGS_TYPE_ARRAY;
                        }
                    }
                    $annoObj = AnnotationFactory::create($annotationType, $args, $argType);
                    $annoObjects[get_class($annoObj)] = $annoObj;
                }
            }
        }
        return $annoObjects;
    }

    private function _removeQuote($str) {
        $str = trim($str);
        $str = preg_replace("/^[\"']/",'',$str);
        $str = preg_replace("/[\"']$/",'',$str);
        return trim($str);
    }

    private function _removeCommentSlashAster($line) {
        $line = trim($line);
        $line = preg_replace("/^\/\*\*/","",$line);
        $line = preg_replace("/\*\/$/","",$line);
        $line = preg_replace("/^\*/","",$line);
        return trim($line);
    }
}
class AnnotationFactory {
    const ARGS_TYPE_ARRAY = 1;
    const ARGS_TYPE_HASH = 2;

    private function __construct() {
    }

    public static function create($name, $args = array(), $argType = array()) {
        $annoObj = new $name();
        if (count($args) == 0) {
            return $annoObj;
        }
        if ($argType == self::ARGS_TYPE_ARRAY) {
            if (count($args) == 1) {
                $annoObj->value = $args[0];
            } else {
                $annoObj->value = $args;
            }
            return $annoObj;
        }
        foreach ($args as $arg=>$val) {
            $annoObj->$arg = $val;
        }
        return $annoObj;
    }

}
function __autoload($class) {
    $dir = dirname(__FILE__);
    $file = $dir . DIRECTORY_SEPARATOR . 'annotation' . DIRECTORY_SEPARATOR . $class . '.php';
    if (is_file($file) && is_readable($file)) {
        include_once $file;
        if (class_exists($class, false)) {
            return true;
        }
    }
    return false;
}
?>