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

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