* This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA **/ namespace Addendum; use \ReflectionClass; use \ReflectionMethod; use \ReflectionProperty; require_once(dirname(__FILE__).'/annotations/annotation_parser.php'); class Annotation { public $value; public final function __construct($data, $target) { $reflection = new ReflectionClass($this); foreach($data as $key => $value) { if($reflection->hasProperty($key)) { $this->$key = $value; } else { $class = $reflection->getName(); trigger_error("Property '$key' not defined for annotation '$class'"); } } $this->checkTargetConstraints($target); $this->checkConstraints($target); } private function checkTargetConstraints($target) { $reflection = new ReflectionAnnotatedClass($this); if($reflection->hasAnnotation('Target')) { $value = $reflection->getAnnotation('Target')->value; $values = is_array($value) ? $value : array($value); foreach($values as $value) { if($value == 'class' && $target instanceof ReflectionClass) return; if($value == 'method' && $target instanceof ReflectionMethod) return; if($value == 'property' && $target instanceof ReflectionProperty) return; } trigger_error("Annotation '".get_class($this)."' not allowed on ".$this->createName($target), E_USER_ERROR); } } private function createName($target) { if($target instanceof ReflectionMethod) { return $target->getDeclaringClass()->getName().'::'.$target->getName(); } elseif($target instanceof ReflectionProperty) { return $target->getDeclaringClass()->getName().'::$'.$target->getName(); } else { return $target->getName(); } } protected function checkConstraints($target) {} } class Target extends Annotation {} class AnnotationsBuilder { private static $cache = array(); public function build($targetReflection) { $data = $this->parse($targetReflection); $annotations = array(); foreach($data as $class => $parameters) { if(is_subclass_of($class, '\Addendum\Annotation')) { foreach($parameters as $params) { $annotationReflection = new ReflectionClass($class); $annotations[$class][] = $annotationReflection->newInstance($params, $targetReflection); } } } return $annotations; } private function parse($reflection) { $key = $this->createName($reflection); if(!isset(self::$cache[$key])) { $parser = new AnnotationsMatcher; $parser->matches($this->getDocComment($reflection), $data); self::$cache[$key] = $data; } return self::$cache[$key]; } private function createName($target) { if($target instanceof ReflectionMethod) { return $target->getDeclaringClass()->getName().'::'.$target->getName(); } elseif($target instanceof ReflectionProperty) { return $target->getDeclaringClass()->getName().'::$'.$target->getName(); } else { return $target->getName(); } } protected function getDocComment($reflection) { return Addendum::getDocComment($reflection); } public static function clearCache() { self::$cache = array(); } } class ReflectionAnnotatedClass extends ReflectionClass { private $annotations; public function __construct($class) { parent::__construct($class); $this->annotations = $this->createAnnotationBuilder()->build($this); } public function hasAnnotation($annotation) { return isset($this->annotations[$annotation]); } public function getAnnotation($annotation) { return $this->hasAnnotation($annotation) ? end($this->annotations[$annotation]) : false; } public function getAnnotations() { $result = array(); foreach($this->annotations as $instances) { $result[] = end($instances); } return $result; } public function getAllAnnotations($restriction = false) { $result = array(); foreach($this->annotations as $class => $instances) { if(!$restriction || $restriction == $class) { $result = array_merge($result, $instances); } } return $result; } public function getConstructor() { return $this->createReflectionAnnotatedMethod(parent::getConstructor()); } public function getMethod($name) { return $this->createReflectionAnnotatedMethod(parent::getMethod($name)); } public function getMethods($filter = -1) { $result = array(); foreach(parent::getMethods($filter) as $method) { $result[] = $this->createReflectionAnnotatedMethod($method); } return $result; } public function getProperty($name) { return $this->createReflectionAnnotatedProperty(parent::getProperty($name)); } public function getProperties($filter = -1) { $result = array(); foreach(parent::getProperties($filter) as $property) { $result[] = $this->createReflectionAnnotatedProperty($property); } return $result; } public function getInterfaces() { $result = array(); foreach(parent::getInterfaces() as $interface) { $result[] = $this->createReflectionAnnotatedClass($interface); } return $result; } public function getParentClass() { $class = parent::getParentClass(); return $this->createReflectionAnnotatedClass($class); } protected function createAnnotationBuilder() { return new AnnotationsBuilder(); } private function createReflectionAnnotatedClass($class) { return ($class !== false) ? new ReflectionAnnotatedClass($class->getName()) : false; } private function createReflectionAnnotatedMethod($method) { return ($method !== null) ? new ReflectionAnnotatedMethod($this->getName(), $method->getName()) : null; } private function createReflectionAnnotatedProperty($property) { return ($property !== null) ? new ReflectionAnnotatedProperty($this->getName(), $property->getName()) : null; } } class ReflectionAnnotatedMethod extends ReflectionMethod { private $annotations; public function __construct($class, $name) { parent::__construct($class, $name); $this->annotations = $this->createAnnotationBuilder()->build($this); } public function hasAnnotation($annotation) { return isset($this->annotations[$annotation]); } public function getAnnotation($annotation) { return ($this->hasAnnotation($annotation)) ? end($this->annotations[$annotation]) : false; } public function getAnnotations() { $result = array(); foreach($this->annotations as $instances) { $result[] = end($instances); } return $result; } public function getAllAnnotations($restriction = false) { $result = array(); foreach($this->annotations as $class => $instances) { if(!$restriction || $restriction == $class) { $result = array_merge($result, $instances); } } return $result; } public function getDeclaringClass() { $class = parent::getDeclaringClass(); return new ReflectionAnnotatedClass($class->getName()); } protected function createAnnotationBuilder() { return new AnnotationsBuilder(); } } class ReflectionAnnotatedProperty extends ReflectionProperty { private $annotations; public function __construct($class, $name) { parent::__construct($class, $name); $this->annotations = $this->createAnnotationBuilder()->build($this); } public function hasAnnotation($annotation) { return isset($this->annotations[$annotation]); } public function getAnnotation($annotation) { return ($this->hasAnnotation($annotation)) ? end($this->annotations[$annotation]) : false; } public function getAnnotations() { $result = array(); foreach($this->annotations as $instances) { $result[] = end($instances); } return $result; } public function getAllAnnotations($restriction = false) { $result = array(); foreach($this->annotations as $class => $instances) { if(!$restriction || $restriction == $class) { $result = array_merge($result, $instances); } } return $result; } public function getDeclaringClass() { $class = parent::getDeclaringClass(); return new ReflectionAnnotatedClass($class->getName()); } protected function createAnnotationBuilder() { return new AnnotationsBuilder(); } } class Addendum { private static $rawMode; private static $ignored; public static function getDocComment($reflection) { if(self::checkRawDocCommentParsingNeeded()) { $docComment = new DocComment(); return $docComment->get($reflection); } else { return $reflection->getDocComment(); } } /** Raw mode test */ private static function checkRawDocCommentParsingNeeded() { if(self::$rawMode === null) { $reflection = new ReflectionClass('\Addendum\Addendum'); $method = $reflection->getMethod('checkRawDocCommentParsingNeeded'); self::setRawMode($method->getDocComment() === false); } return self::$rawMode; } public static function setRawMode($enabled = true) { if($enabled) { require_once(dirname(__FILE__).'/annotations/doc_comment.php'); } self::$rawMode = $enabled; } public static function ignore(array $annotations) { self::$ignored = array_combine($annotations, array_fill(0, count($annotations), true)); } public static function ignores($annotation) { return isset(self::$ignored[$annotation]); } }