PluginProbe
WindPress – Tailwind CSS integration for WordPress / 3.0.5
WindPress – Tailwind CSS integration for WordPress v3.0.5
3.2.90 3.2.89 3.2.88 3.2.87 3.2.86 3.2.85 3.2.84 3.2.83 3.2.82 3.2.81 trunk 3.0.0 3.0.1 3.0.10 3.0.11 3.0.12 3.0.13 3.0.14 3.0.15 3.0.16 3.0.17 3.0.2 3.0.3 3.0.4 3.0.5 All 144 releases
windpress / vendor / symfony / property-info / Extractor / PhpStanExtractor.php

PhpStanExtractor.php in WindPress – Tailwind CSS integration for WordPress 3.0.5, at vendor/symfony/property-info/Extractor/PhpStanExtractor.php

242 lines 10.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /*
4 * This file is part of the Symfony package.
5 *
6 * (c) Fabien Potencier <fabien@symfony.com>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11 namespace WindPressPackages\Symfony\Component\PropertyInfo\Extractor;
12
13 use WindPressPackages\phpDocumentor\Reflection\Types\ContextFactory;
14 use WindPressPackages\PHPStan\PhpDocParser\Ast\PhpDoc\InvalidTagValueNode;
15 use WindPressPackages\PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode;
16 use WindPressPackages\PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocNode;
17 use WindPressPackages\PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
18 use WindPressPackages\PHPStan\PhpDocParser\Lexer\Lexer;
19 use WindPressPackages\PHPStan\PhpDocParser\Parser\ConstExprParser;
20 use WindPressPackages\PHPStan\PhpDocParser\Parser\PhpDocParser;
21 use WindPressPackages\PHPStan\PhpDocParser\Parser\TokenIterator;
22 use WindPressPackages\PHPStan\PhpDocParser\Parser\TypeParser;
23 use WindPressPackages\Symfony\Component\PropertyInfo\PhpStan\NameScopeFactory;
24 use WindPressPackages\Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
25 use WindPressPackages\Symfony\Component\PropertyInfo\Type;
26 use WindPressPackages\Symfony\Component\PropertyInfo\Util\PhpStanTypeHelper;
27 /**
28 * Extracts data using PHPStan parser.
29 *
30 * @author Baptiste Leduc <baptiste.leduc@gmail.com>
31 */
32 final class PhpStanExtractor implements PropertyTypeExtractorInterface, ConstructorArgumentTypeExtractorInterface
33 {
34 private const PROPERTY = 0;
35 private const ACCESSOR = 1;
36 private const MUTATOR = 2;
37 /** @var PhpDocParser */
38 private $phpDocParser;
39 /** @var Lexer */
40 private $lexer;
41 /** @var NameScopeFactory */
42 private $nameScopeFactory;
43 /** @var array<string, array{PhpDocNode|null, int|null, string|null, string|null}> */
44 private $docBlocks = [];
45 private $phpStanTypeHelper;
46 private $mutatorPrefixes;
47 private $accessorPrefixes;
48 private $arrayMutatorPrefixes;
49 /**
50 * @param list<string>|null $mutatorPrefixes
51 * @param list<string>|null $accessorPrefixes
52 * @param list<string>|null $arrayMutatorPrefixes
53 */
54 public function __construct(?array $mutatorPrefixes = null, ?array $accessorPrefixes = null, ?array $arrayMutatorPrefixes = null)
55 {
56 if (!\class_exists(ContextFactory::class)) {
57 throw new \LogicException(\sprintf('Unable to use the "%s" class as the "phpdocumentor/type-resolver" package is not installed. Try running composer require "phpdocumentor/type-resolver".', __CLASS__));
58 }
59 if (!\class_exists(PhpDocParser::class)) {
60 throw new \LogicException(\sprintf('Unable to use the "%s" class as the "phpstan/phpdoc-parser" package is not installed. Try running composer require "phpstan/phpdoc-parser".', __CLASS__));
61 }
62 $this->phpStanTypeHelper = new PhpStanTypeHelper();
63 $this->mutatorPrefixes = $mutatorPrefixes ?? ReflectionExtractor::$defaultMutatorPrefixes;
64 $this->accessorPrefixes = $accessorPrefixes ?? ReflectionExtractor::$defaultAccessorPrefixes;
65 $this->arrayMutatorPrefixes = $arrayMutatorPrefixes ?? ReflectionExtractor::$defaultArrayMutatorPrefixes;
66 $this->phpDocParser = new PhpDocParser(new TypeParser(new ConstExprParser()), new ConstExprParser());
67 $this->lexer = new Lexer();
68 $this->nameScopeFactory = new NameScopeFactory();
69 }
70 public function getTypes(string $class, string $property, array $context = []) : ?array
71 {
72 /** @var PhpDocNode|null $docNode */
73 [$docNode, $source, $prefix, $declaringClass] = $this->getDocBlock($class, $property);
74 $nameScope = $this->nameScopeFactory->create($class, $declaringClass);
75 if (null === $docNode) {
76 return null;
77 }
78 switch ($source) {
79 case self::PROPERTY:
80 $tag = '@var';
81 break;
82 case self::ACCESSOR:
83 $tag = '@return';
84 break;
85 case self::MUTATOR:
86 $tag = '@param';
87 break;
88 }
89 $parentClass = null;
90 $types = [];
91 foreach ($docNode->getTagsByName($tag) as $tagDocNode) {
92 if ($tagDocNode->value instanceof InvalidTagValueNode) {
93 continue;
94 }
95 foreach ($this->phpStanTypeHelper->getTypes($tagDocNode->value, $nameScope) as $type) {
96 switch ($type->getClassName()) {
97 case 'self':
98 case 'static':
99 $resolvedClass = $class;
100 break;
101 case 'parent':
102 if (\false !== ($resolvedClass = $parentClass ?? ($parentClass = \get_parent_class($class)))) {
103 break;
104 }
105 // no break
106 default:
107 $types[] = $type;
108 continue 2;
109 }
110 $types[] = new Type(Type::BUILTIN_TYPE_OBJECT, $type->isNullable(), $resolvedClass, $type->isCollection(), $type->getCollectionKeyTypes(), $type->getCollectionValueTypes());
111 }
112 }
113 if (!isset($types[0])) {
114 return null;
115 }
116 if (!\in_array($prefix, $this->arrayMutatorPrefixes, \true)) {
117 return $types;
118 }
119 return [new Type(Type::BUILTIN_TYPE_ARRAY, \false, null, \true, new Type(Type::BUILTIN_TYPE_INT), $types[0])];
120 }
121 public function getTypesFromConstructor(string $class, string $property) : ?array
122 {
123 if (null === ($tagDocNode = $this->getDocBlockFromConstructor($class, $property))) {
124 return null;
125 }
126 $types = [];
127 foreach ($this->phpStanTypeHelper->getTypes($tagDocNode, $this->nameScopeFactory->create($class)) as $type) {
128 $types[] = $type;
129 }
130 if (!isset($types[0])) {
131 return null;
132 }
133 return $types;
134 }
135 private function getDocBlockFromConstructor(string $class, string $property) : ?ParamTagValueNode
136 {
137 try {
138 $reflectionClass = new \ReflectionClass($class);
139 } catch (\ReflectionException $e) {
140 return null;
141 }
142 if (null === ($reflectionConstructor = $reflectionClass->getConstructor())) {
143 return null;
144 }
145 if (!($rawDocNode = $reflectionConstructor->getDocComment())) {
146 return null;
147 }
148 $tokens = new TokenIterator($this->lexer->tokenize($rawDocNode));
149 $phpDocNode = $this->phpDocParser->parse($tokens);
150 $tokens->consumeTokenType(Lexer::TOKEN_END);
151 return $this->filterDocBlockParams($phpDocNode, $property);
152 }
153 private function filterDocBlockParams(PhpDocNode $docNode, string $allowedParam) : ?ParamTagValueNode
154 {
155 $tags = \array_values(\array_filter($docNode->getTagsByName('@param'), function ($tagNode) use($allowedParam) {
156 return $tagNode instanceof PhpDocTagNode && '$' . $allowedParam === $tagNode->value->parameterName;
157 }));
158 if (!$tags) {
159 return null;
160 }
161 return $tags[0]->value;
162 }
163 /**
164 * @return array{PhpDocNode|null, int|null, string|null, string|null}
165 */
166 private function getDocBlock(string $class, string $property) : array
167 {
168 $propertyHash = $class . '::' . $property;
169 if (isset($this->docBlocks[$propertyHash])) {
170 return $this->docBlocks[$propertyHash];
171 }
172 $ucFirstProperty = \ucfirst($property);
173 if ([$docBlock, $declaringClass] = $this->getDocBlockFromProperty($class, $property)) {
174 $data = [$docBlock, self::PROPERTY, null, $declaringClass];
175 } elseif ([$docBlock, $_, $declaringClass] = $this->getDocBlockFromMethod($class, $ucFirstProperty, self::ACCESSOR)) {
176 $data = [$docBlock, self::ACCESSOR, null, $declaringClass];
177 } elseif ([$docBlock, $prefix, $declaringClass] = $this->getDocBlockFromMethod($class, $ucFirstProperty, self::MUTATOR)) {
178 $data = [$docBlock, self::MUTATOR, $prefix, $declaringClass];
179 } else {
180 $data = [null, null, null, null];
181 }
182 return $this->docBlocks[$propertyHash] = $data;
183 }
184 /**
185 * @return array{PhpDocNode, string}|null
186 */
187 private function getDocBlockFromProperty(string $class, string $property) : ?array
188 {
189 // Use a ReflectionProperty instead of $class to get the parent class if applicable
190 try {
191 $reflectionProperty = new \ReflectionProperty($class, $property);
192 } catch (\ReflectionException $e) {
193 return null;
194 }
195 $reflector = $reflectionProperty->getDeclaringClass();
196 foreach ($reflector->getTraits() as $trait) {
197 if ($trait->hasProperty($property)) {
198 return $this->getDocBlockFromProperty($trait->getName(), $property);
199 }
200 }
201 if (null === ($rawDocNode = $reflectionProperty->getDocComment() ?: null)) {
202 return null;
203 }
204 $tokens = new TokenIterator($this->lexer->tokenize($rawDocNode));
205 $phpDocNode = $this->phpDocParser->parse($tokens);
206 $tokens->consumeTokenType(Lexer::TOKEN_END);
207 return [$phpDocNode, $reflectionProperty->class];
208 }
209 /**
210 * @return array{PhpDocNode, string, string}|null
211 */
212 private function getDocBlockFromMethod(string $class, string $ucFirstProperty, int $type) : ?array
213 {
214 $prefixes = self::ACCESSOR === $type ? $this->accessorPrefixes : $this->mutatorPrefixes;
215 $prefix = null;
216 foreach ($prefixes as $prefix) {
217 $methodName = $prefix . $ucFirstProperty;
218 try {
219 $reflectionMethod = new \ReflectionMethod($class, $methodName);
220 if ($reflectionMethod->isStatic()) {
221 continue;
222 }
223 if (self::ACCESSOR === $type && 0 === $reflectionMethod->getNumberOfRequiredParameters() || self::MUTATOR === $type && $reflectionMethod->getNumberOfParameters() >= 1) {
224 break;
225 }
226 } catch (\ReflectionException $e) {
227 // Try the next prefix if the method doesn't exist
228 }
229 }
230 if (!isset($reflectionMethod)) {
231 return null;
232 }
233 if (null === ($rawDocNode = $reflectionMethod->getDocComment() ?: null)) {
234 return null;
235 }
236 $tokens = new TokenIterator($this->lexer->tokenize($rawDocNode));
237 $phpDocNode = $this->phpDocParser->parse($tokens);
238 $tokens->consumeTokenType(Lexer::TOKEN_END);
239 return [$phpDocNode, $prefix, $reflectionMethod->class];
240 }
241 }
242