vendor/shopware/core/Framework/Compatibility/AnnotationReader.php line 207

Open in your IDE?
  1. <?php
  2. namespace Shopware\Core\Framework\Compatibility;
  3. use Doctrine\Common\Annotations\Annotation\IgnoreAnnotation;
  4. use Doctrine\Common\Annotations\Annotation\Target;
  5. use Doctrine\Common\Annotations\AnnotationException;
  6. use Doctrine\Common\Annotations\ImplicitlyIgnoredAnnotationNames;
  7. use Doctrine\Common\Annotations\PhpParser;
  8. use Doctrine\Common\Annotations\Reader;
  9. use Doctrine\Common\Annotations\Annotation as Annotation;
  10. use ReflectionClass;
  11. use ReflectionFunction;
  12. use ReflectionMethod;
  13. use ReflectionProperty;
  14. use function array_merge;
  15. use function class_exists;
  16. use function extension_loaded;
  17. use function ini_get;
  18. /**
  19.  * @deprecated tag:v6.5.0 - Remove compatibility bridge to make parameters case insensitive
  20.  * @see https://github.com/doctrine/annotations/issues/421
  21.  */
  22. class AnnotationReader implements Reader
  23. {
  24.     /**
  25.      * Global map for imports.
  26.      *
  27.      * @var array<string, class-string>
  28.      */
  29.     private static $globalImports = [
  30.         'ignoreannotation' => Annotation\IgnoreAnnotation::class,
  31.     ];
  32.     /**
  33.      * A list with annotations that are not causing exceptions when not resolved to an annotation class.
  34.      *
  35.      * The names are case sensitive.
  36.      *
  37.      * @var array<string, true>
  38.      */
  39.     private static $globalIgnoredNames ImplicitlyIgnoredAnnotationNames::LIST;
  40.     /**
  41.      * A list with annotations that are not causing exceptions when not resolved to an annotation class.
  42.      *
  43.      * The names are case sensitive.
  44.      *
  45.      * @var array<string, true>
  46.      */
  47.     private static $globalIgnoredNamespaces = [];
  48.     /**
  49.      * Add a new annotation to the globally ignored annotation names with regard to exception handling.
  50.      *
  51.      * @param string $name
  52.      */
  53.     public static function addGlobalIgnoredName($name)
  54.     {
  55.         self::$globalIgnoredNames[$name] = true;
  56.     }
  57.     /**
  58.      * Add a new annotation to the globally ignored annotation namespaces with regard to exception handling.
  59.      *
  60.      * @param string $namespace
  61.      */
  62.     public static function addGlobalIgnoredNamespace($namespace)
  63.     {
  64.         self::$globalIgnoredNamespaces[$namespace] = true;
  65.     }
  66.     /**
  67.      * Annotations parser.
  68.      *
  69.      * @var DocParser
  70.      */
  71.     private $parser;
  72.     /**
  73.      * Annotations parser used to collect parsing metadata.
  74.      *
  75.      * @var DocParser
  76.      */
  77.     private $preParser;
  78.     /**
  79.      * PHP parser used to collect imports.
  80.      *
  81.      * @var PhpParser
  82.      */
  83.     private $phpParser;
  84.     /**
  85.      * In-memory cache mechanism to store imported annotations per class.
  86.      *
  87.      * @var array<'class'|'function', array<string, array<string, class-string>>>
  88.      */
  89.     private $imports = [];
  90.     /**
  91.      * In-memory cache mechanism to store ignored annotations per class.
  92.      *
  93.      * @var array<'class'|'function', array<string, array<string, true>>>
  94.      */
  95.     private $ignoredAnnotationNames = [];
  96.     /**
  97.      * @internal
  98.      * Initializes a new AnnotationReader.
  99.      *
  100.      * @throws AnnotationException
  101.      */
  102.     public function __construct(?DocParser $parser null)
  103.     {
  104.         if (
  105.             extension_loaded('Zend Optimizer+') && (ini_get('zend_optimizerplus.save_comments') === '0' ||
  106.                 ini_get('opcache.save_comments') === '0')
  107.         ) {
  108.             throw AnnotationException::optimizerPlusSaveComments();
  109.         }
  110.         if (extension_loaded('Zend OPcache') && ini_get('opcache.save_comments') === 0) {
  111.             throw AnnotationException::optimizerPlusSaveComments();
  112.         }
  113.         // Make sure that the IgnoreAnnotation annotation is loaded
  114.         class_exists(IgnoreAnnotation::class);
  115.         $this->parser $parser ?: new DocParser();
  116.         $this->preParser = new DocParser();
  117.         $this->preParser->setImports(self::$globalImports);
  118.         $this->preParser->setIgnoreNotImportedAnnotations(true);
  119.         $this->preParser->setIgnoredAnnotationNames(self::$globalIgnoredNames);
  120.         $this->phpParser = new PhpParser();
  121.     }
  122.     /**
  123.      * @return array<object>
  124.      */
  125.     public function getClassAnnotations(ReflectionClass $class)
  126.     {
  127.         $this->parser->setTarget(Target::TARGET_CLASS);
  128.         $this->parser->setImports($this->getImports($class));
  129.         $this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class));
  130.         $this->parser->setIgnoredAnnotationNamespaces(self::$globalIgnoredNamespaces);
  131.         return $this->parser->parse($class->getDocComment(), 'class ' $class->getName());
  132.     }
  133.     /**
  134.      * @return object|null
  135.      */
  136.     public function getClassAnnotation(ReflectionClass $class$annotationName)
  137.     {
  138.         $annotations $this->getClassAnnotations($class);
  139.         foreach ($annotations as $annotation) {
  140.             if ($annotation instanceof $annotationName) {
  141.                 return $annotation;
  142.             }
  143.         }
  144.         return null;
  145.     }
  146.     /**
  147.      * @return array<object>
  148.      */
  149.     public function getPropertyAnnotations(ReflectionProperty $property)
  150.     {
  151.         $class   $property->getDeclaringClass();
  152.         $context 'property ' $class->getName() . '::$' $property->getName();
  153.         $this->parser->setTarget(Target::TARGET_PROPERTY);
  154.         $this->parser->setImports($this->getPropertyImports($property));
  155.         $this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class));
  156.         $this->parser->setIgnoredAnnotationNamespaces(self::$globalIgnoredNamespaces);
  157.         return $this->parser->parse($property->getDocComment(), $context);
  158.     }
  159.     /**
  160.      * @return object|null
  161.      */
  162.     public function getPropertyAnnotation(ReflectionProperty $property$annotationName)
  163.     {
  164.         $annotations $this->getPropertyAnnotations($property);
  165.         foreach ($annotations as $annotation) {
  166.             if ($annotation instanceof $annotationName) {
  167.                 return $annotation;
  168.             }
  169.         }
  170.         return null;
  171.     }
  172.     /**
  173.      * @return object|null
  174.      */
  175.     public function getMethodAnnotations(ReflectionMethod $method)
  176.     {
  177.         $class   $method->getDeclaringClass();
  178.         $context 'method ' $class->getName() . '::' $method->getName() . '()';
  179.         $this->parser->setTarget(Target::TARGET_METHOD);
  180.         $this->parser->setImports($this->getMethodImports($method));
  181.         $this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class));
  182.         $this->parser->setIgnoredAnnotationNamespaces(self::$globalIgnoredNamespaces);
  183.         return $this->parser->parse($method->getDocComment(), $context);
  184.     }
  185.     /**
  186.      * @return array<object>
  187.      */
  188.     public function getMethodAnnotation(ReflectionMethod $method$annotationName)
  189.     {
  190.         $annotations $this->getMethodAnnotations($method);
  191.         foreach ($annotations as $annotation) {
  192.             if ($annotation instanceof $annotationName) {
  193.                 return $annotation;
  194.             }
  195.         }
  196.         return null;
  197.     }
  198.     /**
  199.      * Gets the annotations applied to a function.
  200.      *
  201.      * @phpstan-return list<object> An array of Annotations.
  202.      */
  203.     public function getFunctionAnnotations(ReflectionFunction $function): array
  204.     {
  205.         $context 'function ' $function->getName();
  206.         $this->parser->setTarget(Target::TARGET_FUNCTION);
  207.         $this->parser->setImports($this->getImports($function));
  208.         $this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($function));
  209.         $this->parser->setIgnoredAnnotationNamespaces(self::$globalIgnoredNamespaces);
  210.         return $this->parser->parse($function->getDocComment(), $context);
  211.     }
  212.     /**
  213.      * Gets a function annotation.
  214.      *
  215.      * @return object|null The Annotation or NULL, if the requested annotation does not exist.
  216.      */
  217.     public function getFunctionAnnotation(ReflectionFunction $functionstring $annotationName)
  218.     {
  219.         $annotations $this->getFunctionAnnotations($function);
  220.         foreach ($annotations as $annotation) {
  221.             if ($annotation instanceof $annotationName) {
  222.                 return $annotation;
  223.             }
  224.         }
  225.         return null;
  226.     }
  227.     /**
  228.      * Returns the ignored annotations for the given class or function.
  229.      *
  230.      * @param ReflectionClass|ReflectionFunction $reflection
  231.      *
  232.      * @return array<string, true>
  233.      */
  234.     private function getIgnoredAnnotationNames($reflection): array
  235.     {
  236.         $type $reflection instanceof ReflectionClass 'class' 'function';
  237.         $name $reflection->getName();
  238.         if (isset($this->ignoredAnnotationNames[$type][$name])) {
  239.             return $this->ignoredAnnotationNames[$type][$name];
  240.         }
  241.         $this->collectParsingMetadata($reflection);
  242.         return $this->ignoredAnnotationNames[$type][$name];
  243.     }
  244.     /**
  245.      * Retrieves imports for a class or a function.
  246.      *
  247.      * @param ReflectionClass|ReflectionFunction $reflection
  248.      *
  249.      * @return array<string, class-string>
  250.      */
  251.     private function getImports($reflection): array
  252.     {
  253.         $type $reflection instanceof ReflectionClass 'class' 'function';
  254.         $name $reflection->getName();
  255.         if (isset($this->imports[$type][$name])) {
  256.             return $this->imports[$type][$name];
  257.         }
  258.         $this->collectParsingMetadata($reflection);
  259.         return $this->imports[$type][$name];
  260.     }
  261.     /**
  262.      * Retrieves imports for methods.
  263.      *
  264.      * @return array<string, class-string>
  265.      */
  266.     private function getMethodImports(ReflectionMethod $method)
  267.     {
  268.         $class        $method->getDeclaringClass();
  269.         $classImports $this->getImports($class);
  270.         $traitImports = [];
  271.         foreach ($class->getTraits() as $trait) {
  272.             if (
  273.                 ! $trait->hasMethod($method->getName())
  274.                 || $trait->getFileName() !== $method->getFileName()
  275.             ) {
  276.                 continue;
  277.             }
  278.             $traitImports array_merge($traitImports$this->phpParser->parseUseStatements($trait));
  279.         }
  280.         return array_merge($classImports$traitImports);
  281.     }
  282.     /**
  283.      * Retrieves imports for properties.
  284.      *
  285.      * @return array<string, class-string>
  286.      */
  287.     private function getPropertyImports(ReflectionProperty $property)
  288.     {
  289.         $class        $property->getDeclaringClass();
  290.         $classImports $this->getImports($class);
  291.         $traitImports = [];
  292.         foreach ($class->getTraits() as $trait) {
  293.             if (! $trait->hasProperty($property->getName())) {
  294.                 continue;
  295.             }
  296.             $traitImports array_merge($traitImports$this->phpParser->parseUseStatements($trait));
  297.         }
  298.         return array_merge($classImports$traitImports);
  299.     }
  300.     /**
  301.      * Collects parsing metadata for a given class or function.
  302.      *
  303.      * @param ReflectionClass|ReflectionFunction $reflection
  304.      */
  305.     private function collectParsingMetadata($reflection): void
  306.     {
  307.         $type $reflection instanceof ReflectionClass 'class' 'function';
  308.         $name $reflection->getName();
  309.         $ignoredAnnotationNames self::$globalIgnoredNames;
  310.         $annotations            $this->preParser->parse($reflection->getDocComment(), $type ' ' $name);
  311.         foreach ($annotations as $annotation) {
  312.             if (! ($annotation instanceof IgnoreAnnotation)) {
  313.                 continue;
  314.             }
  315.             foreach ($annotation->names as $annot) {
  316.                 $ignoredAnnotationNames[$annot] = true;
  317.             }
  318.         }
  319.         $this->imports[$type][$name] = array_merge(
  320.             self::$globalImports,
  321.             $this->phpParser->parseUseStatements($reflection),
  322.             [
  323.                 '__NAMESPACE__' => $reflection->getNamespaceName(),
  324.                 'self' => $name,
  325.             ]
  326.         );
  327.         $this->ignoredAnnotationNames[$type][$name] = $ignoredAnnotationNames;
  328.     }
  329. }