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

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