Viewing file: AnnotationReader.php (11.24 KB) -rwxrwxrwx Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
<?php
namespace Doctrine\Common\Annotations;
use Doctrine\Common\Annotations\Annotation\IgnoreAnnotation; use Doctrine\Common\Annotations\Annotation\Target; use ReflectionClass; use ReflectionFunction; use ReflectionMethod; use ReflectionProperty;
use function array_merge; use function class_exists; use function extension_loaded; use function ini_get;
/** * A reader for docblock annotations. */ class AnnotationReader implements Reader { /** * Global map for imports. * * @var array<string, class-string> */ private static $globalImports = [ 'ignoreannotation' => Annotation\IgnoreAnnotation::class, ];
/** * A list with annotations that are not causing exceptions when not resolved to an annotation class. * * The names are case sensitive. * * @var array<string, true> */ private static $globalIgnoredNames = ImplicitlyIgnoredAnnotationNames::LIST;
/** * A list with annotations that are not causing exceptions when not resolved to an annotation class. * * The names are case sensitive. * * @var array<string, true> */ private static $globalIgnoredNamespaces = [];
/** * Add a new annotation to the globally ignored annotation names with regard to exception handling. * * @param string $name */ public static function addGlobalIgnoredName($name) { self::$globalIgnoredNames[$name] = true; }
/** * Add a new annotation to the globally ignored annotation namespaces with regard to exception handling. * * @param string $namespace */ public static function addGlobalIgnoredNamespace($namespace) { self::$globalIgnoredNamespaces[$namespace] = true; }
/** * Annotations parser. * * @var DocParser */ private $parser;
/** * Annotations parser used to collect parsing metadata. * * @var DocParser */ private $preParser;
/** * PHP parser used to collect imports. * * @var PhpParser */ private $phpParser;
/** * In-memory cache mechanism to store imported annotations per class. * * @psalm-var array<'class'|'function', array<string, array<string, class-string>>> */ private $imports = [];
/** * In-memory cache mechanism to store ignored annotations per class. * * @psalm-var array<'class'|'function', array<string, array<string, true>>> */ private $ignoredAnnotationNames = [];
/** * Initializes a new AnnotationReader. * * @throws AnnotationException */ public function __construct(?DocParser $parser = null) { if ( extension_loaded('Zend Optimizer+') && (ini_get('zend_optimizerplus.save_comments') === '0' || ini_get('opcache.save_comments') === '0') ) { throw AnnotationException::optimizerPlusSaveComments(); }
if (extension_loaded('Zend OPcache') && ini_get('opcache.save_comments') === 0) { throw AnnotationException::optimizerPlusSaveComments(); }
// Make sure that the IgnoreAnnotation annotation is loaded class_exists(IgnoreAnnotation::class);
$this->parser = $parser ?: new DocParser();
$this->preParser = new DocParser();
$this->preParser->setImports(self::$globalImports); $this->preParser->setIgnoreNotImportedAnnotations(true); $this->preParser->setIgnoredAnnotationNames(self::$globalIgnoredNames);
$this->phpParser = new PhpParser(); }
/** * {@inheritDoc} */ public function getClassAnnotations(ReflectionClass $class) { $this->parser->setTarget(Target::TARGET_CLASS); $this->parser->setImports($this->getImports($class)); $this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class)); $this->parser->setIgnoredAnnotationNamespaces(self::$globalIgnoredNamespaces);
return $this->parser->parse($class->getDocComment(), 'class ' . $class->getName()); }
/** * {@inheritDoc} */ public function getClassAnnotation(ReflectionClass $class, $annotationName) { $annotations = $this->getClassAnnotations($class);
foreach ($annotations as $annotation) { if ($annotation instanceof $annotationName) { return $annotation; } }
return null; }
/** * {@inheritDoc} */ public function getPropertyAnnotations(ReflectionProperty $property) { $class = $property->getDeclaringClass(); $context = 'property ' . $class->getName() . '::$' . $property->getName();
$this->parser->setTarget(Target::TARGET_PROPERTY); $this->parser->setImports($this->getPropertyImports($property)); $this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class)); $this->parser->setIgnoredAnnotationNamespaces(self::$globalIgnoredNamespaces);
return $this->parser->parse($property->getDocComment(), $context); }
/** * {@inheritDoc} */ public function getPropertyAnnotation(ReflectionProperty $property, $annotationName) { $annotations = $this->getPropertyAnnotations($property);
foreach ($annotations as $annotation) { if ($annotation instanceof $annotationName) { return $annotation; } }
return null; }
/** * {@inheritDoc} */ public function getMethodAnnotations(ReflectionMethod $method) { $class = $method->getDeclaringClass(); $context = 'method ' . $class->getName() . '::' . $method->getName() . '()';
$this->parser->setTarget(Target::TARGET_METHOD); $this->parser->setImports($this->getMethodImports($method)); $this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class)); $this->parser->setIgnoredAnnotationNamespaces(self::$globalIgnoredNamespaces);
return $this->parser->parse($method->getDocComment(), $context); }
/** * {@inheritDoc} */ public function getMethodAnnotation(ReflectionMethod $method, $annotationName) { $annotations = $this->getMethodAnnotations($method);
foreach ($annotations as $annotation) { if ($annotation instanceof $annotationName) { return $annotation; } }
return null; }
/** * Gets the annotations applied to a function. * * @phpstan-return list<object> An array of Annotations. */ public function getFunctionAnnotations(ReflectionFunction $function): array { $context = 'function ' . $function->getName();
$this->parser->setTarget(Target::TARGET_FUNCTION); $this->parser->setImports($this->getImports($function)); $this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($function)); $this->parser->setIgnoredAnnotationNamespaces(self::$globalIgnoredNamespaces);
return $this->parser->parse($function->getDocComment(), $context); }
/** * Gets a function annotation. * * @return object|null The Annotation or NULL, if the requested annotation does not exist. */ public function getFunctionAnnotation(ReflectionFunction $function, string $annotationName) { $annotations = $this->getFunctionAnnotations($function);
foreach ($annotations as $annotation) { if ($annotation instanceof $annotationName) { return $annotation; } }
return null; }
/** * Returns the ignored annotations for the given class or function. * * @param ReflectionClass|ReflectionFunction $reflection * * @return array<string, true> */ private function getIgnoredAnnotationNames($reflection): array { $type = $reflection instanceof ReflectionClass ? 'class' : 'function'; $name = $reflection->getName();
if (isset($this->ignoredAnnotationNames[$type][$name])) { return $this->ignoredAnnotationNames[$type][$name]; }
$this->collectParsingMetadata($reflection);
return $this->ignoredAnnotationNames[$type][$name]; }
/** * Retrieves imports for a class or a function. * * @param ReflectionClass|ReflectionFunction $reflection * * @return array<string, class-string> */ private function getImports($reflection): array { $type = $reflection instanceof ReflectionClass ? 'class' : 'function'; $name = $reflection->getName();
if (isset($this->imports[$type][$name])) { return $this->imports[$type][$name]; }
$this->collectParsingMetadata($reflection);
return $this->imports[$type][$name]; }
/** * Retrieves imports for methods. * * @return array<string, class-string> */ private function getMethodImports(ReflectionMethod $method) { $class = $method->getDeclaringClass(); $classImports = $this->getImports($class);
$traitImports = [];
foreach ($class->getTraits() as $trait) { if ( ! $trait->hasMethod($method->getName()) || $trait->getFileName() !== $method->getFileName() ) { continue; }
$traitImports = array_merge($traitImports, $this->phpParser->parseUseStatements($trait)); }
return array_merge($classImports, $traitImports); }
/** * Retrieves imports for properties. * * @return array<string, class-string> */ private function getPropertyImports(ReflectionProperty $property) { $class = $property->getDeclaringClass(); $classImports = $this->getImports($class);
$traitImports = [];
foreach ($class->getTraits() as $trait) { if (! $trait->hasProperty($property->getName())) { continue; }
$traitImports = array_merge($traitImports, $this->phpParser->parseUseStatements($trait)); }
return array_merge($classImports, $traitImports); }
/** * Collects parsing metadata for a given class or function. * * @param ReflectionClass|ReflectionFunction $reflection */ private function collectParsingMetadata($reflection): void { $type = $reflection instanceof ReflectionClass ? 'class' : 'function'; $name = $reflection->getName();
$ignoredAnnotationNames = self::$globalIgnoredNames; $annotations = $this->preParser->parse($reflection->getDocComment(), $type . ' ' . $name);
foreach ($annotations as $annotation) { if (! ($annotation instanceof IgnoreAnnotation)) { continue; }
foreach ($annotation->names as $annot) { $ignoredAnnotationNames[$annot] = true; } }
$this->imports[$type][$name] = array_merge( self::$globalImports, $this->phpParser->parseUseStatements($reflection), [ '__NAMESPACE__' => $reflection->getNamespaceName(), 'self' => $name, ] );
$this->ignoredAnnotationNames[$type][$name] = $ignoredAnnotationNames; } }
|