PluginProbe
WindPress – Tailwind CSS integration for WordPress / 3.2.84
WindPress – Tailwind CSS integration for WordPress v3.2.84
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.2.84, at vendor/symfony/property-info/Extractor/PhpStanExtractor.php

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