Revert "[TASK] Avoid slow array functions in loops"
[Packages/TYPO3.CMS.git] / typo3 / sysext / extbase / Classes / Reflection / PropertyInfo / Extractor / PhpDocPropertyTypeExtractor.php
1 <?php
2 declare(strict_types = 1);
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 namespace TYPO3\CMS\Extbase\Reflection\PropertyInfo\Extractor;
18
19 use phpDocumentor\Reflection\DocBlock;
20 use phpDocumentor\Reflection\DocBlockFactory;
21 use phpDocumentor\Reflection\Types\Context;
22 use phpDocumentor\Reflection\Types\ContextFactory;
23 use ReflectionProperty;
24 use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
25 use Symfony\Component\PropertyInfo\Type;
26 use Symfony\Component\PropertyInfo\Util\PhpDocTypeHelper;
27 use TYPO3\CMS\Extbase\Reflection\DocBlock\Tags\Var_;
28
29 /**
30 * @internal this is a concrete TYPO3 implementation and solely used for EXT:extbase and not part of TYPO3's Core API.
31 */
32 class PhpDocPropertyTypeExtractor implements PropertyTypeExtractorInterface
33 {
34 /**
35 * @var DocBlockFactory
36 */
37 private $docBlockFactory;
38
39 /**
40 * @var ContextFactory
41 */
42 private $contextFactory;
43
44 /**
45 * @var PhpDocTypeHelper
46 */
47 private $phpDocTypeHelper;
48
49 /**
50 * @var array
51 */
52 private static $reflectionContextCache = [];
53
54 public function __construct()
55 {
56 $this->docBlockFactory = DocBlockFactory::createInstance();
57 $this->docBlockFactory->registerTagHandler('var', Var_::class);
58
59 $this->contextFactory = new ContextFactory();
60 $this->phpDocTypeHelper = new PhpDocTypeHelper();
61 }
62
63 /**
64 * @param string $class
65 * @param string $property
66 * @param array $context
67 *
68 * @return Type[]|null
69 */
70 public function getTypes($class, $property, array $context = []): ?array
71 {
72 try {
73 $reflectionProperty = $context['reflectionProperty'] ?? new \ReflectionProperty($class, $property);
74 } catch (\ReflectionException $e) {
75 return null;
76 }
77
78 if (!isset(static::$reflectionContextCache[$class])) {
79 static::$reflectionContextCache[$class] = $this->contextFactory->createFromReflector(
80 $reflectionProperty->getDeclaringClass()
81 );
82 }
83
84 $docBlock = $this->getDocBlockFromProperty(
85 $reflectionProperty,
86 static::$reflectionContextCache[$class]
87 );
88
89 if ($docBlock === null) {
90 return null;
91 }
92
93 $types = [];
94 /** @var Var_ $tag */
95 foreach ($docBlock->getTagsByName('var') as $tag) {
96 if ($tag && null !== $tag->getType()) {
97 $types = array_merge($types, $this->phpDocTypeHelper->getTypes($tag->getType()));
98 }
99 }
100
101 if (!isset($types[0])) {
102 return null;
103 }
104
105 return $types;
106 }
107
108 /**
109 * @param ReflectionProperty $reflectionProperty
110 * @param Context $context
111 * @return DocBlock|null
112 */
113 private function getDocBlockFromProperty(ReflectionProperty $reflectionProperty, Context $context): ?DocBlock
114 {
115 try {
116 return $this->docBlockFactory->create($reflectionProperty->getDocComment(), $context);
117 } catch (\InvalidArgumentException $e) {
118 return null;
119 }
120 }
121 }