PluginProbe
WindPress – Tailwind CSS integration for WordPress / 3.0.2
WindPress – Tailwind CSS integration for WordPress v3.0.2
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 / PhpDocExtractor.php

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

296 lines 12.0 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\DocBlock;
14 use WindPressPackages\phpDocumentor\Reflection\DocBlock\Tags\InvalidTag;
15 use WindPressPackages\phpDocumentor\Reflection\DocBlockFactory;
16 use WindPressPackages\phpDocumentor\Reflection\DocBlockFactoryInterface;
17 use WindPressPackages\phpDocumentor\Reflection\Types\Context;
18 use WindPressPackages\phpDocumentor\Reflection\Types\ContextFactory;
19 use WindPressPackages\Symfony\Component\PropertyInfo\PropertyDescriptionExtractorInterface;
20 use WindPressPackages\Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
21 use WindPressPackages\Symfony\Component\PropertyInfo\Type;
22 use WindPressPackages\Symfony\Component\PropertyInfo\Util\PhpDocTypeHelper;
23 /**
24 * Extracts data using a PHPDoc parser.
25 *
26 * @author Kévin Dunglas <dunglas@gmail.com>
27 *
28 * @final
29 */
30 class PhpDocExtractor implements PropertyDescriptionExtractorInterface, PropertyTypeExtractorInterface, ConstructorArgumentTypeExtractorInterface
31 {
32 public const PROPERTY = 0;
33 public const ACCESSOR = 1;
34 public const MUTATOR = 2;
35 /**
36 * @var array<string, array{DocBlock|null, int|null, string|null}>
37 */
38 private $docBlocks = [];
39 /**
40 * @var Context[]
41 */
42 private $contexts = [];
43 private $docBlockFactory;
44 private $contextFactory;
45 private $phpDocTypeHelper;
46 private $mutatorPrefixes;
47 private $accessorPrefixes;
48 private $arrayMutatorPrefixes;
49 /**
50 * @param string[]|null $mutatorPrefixes
51 * @param string[]|null $accessorPrefixes
52 * @param string[]|null $arrayMutatorPrefixes
53 */
54 public function __construct(?DocBlockFactoryInterface $docBlockFactory = null, ?array $mutatorPrefixes = null, ?array $accessorPrefixes = null, ?array $arrayMutatorPrefixes = null)
55 {
56 if (!class_exists(DocBlockFactory::class)) {
57 throw new \LogicException(sprintf('Unable to use the "%s" class as the "phpdocumentor/reflection-docblock" package is not installed. Try running composer require "phpdocumentor/reflection-docblock".', __CLASS__));
58 }
59 $this->docBlockFactory = $docBlockFactory ?: DocBlockFactory::createInstance();
60 $this->contextFactory = new ContextFactory();
61 $this->phpDocTypeHelper = new PhpDocTypeHelper();
62 $this->mutatorPrefixes = $mutatorPrefixes ?? ReflectionExtractor::$defaultMutatorPrefixes;
63 $this->accessorPrefixes = $accessorPrefixes ?? ReflectionExtractor::$defaultAccessorPrefixes;
64 $this->arrayMutatorPrefixes = $arrayMutatorPrefixes ?? ReflectionExtractor::$defaultArrayMutatorPrefixes;
65 }
66 /**
67 * {@inheritdoc}
68 */
69 public function getShortDescription(string $class, string $property, array $context = []): ?string
70 {
71 /** @var $docBlock DocBlock */
72 [$docBlock] = $this->getDocBlock($class, $property);
73 if (!$docBlock) {
74 return null;
75 }
76 $shortDescription = $docBlock->getSummary();
77 if (!empty($shortDescription)) {
78 return $shortDescription;
79 }
80 foreach ($docBlock->getTagsByName('var') as $var) {
81 if ($var && !$var instanceof InvalidTag) {
82 $varDescription = $var->getDescription()->render();
83 if (!empty($varDescription)) {
84 return $varDescription;
85 }
86 }
87 }
88 return null;
89 }
90 /**
91 * {@inheritdoc}
92 */
93 public function getLongDescription(string $class, string $property, array $context = []): ?string
94 {
95 /** @var $docBlock DocBlock */
96 [$docBlock] = $this->getDocBlock($class, $property);
97 if (!$docBlock) {
98 return null;
99 }
100 $contents = $docBlock->getDescription()->render();
101 return ('' === $contents) ? null : $contents;
102 }
103 /**
104 * {@inheritdoc}
105 */
106 public function getTypes(string $class, string $property, array $context = []): ?array
107 {
108 /** @var $docBlock DocBlock */
109 [$docBlock, $source, $prefix] = $this->getDocBlock($class, $property);
110 if (!$docBlock) {
111 return null;
112 }
113 switch ($source) {
114 case self::PROPERTY:
115 $tag = 'var';
116 break;
117 case self::ACCESSOR:
118 $tag = 'return';
119 break;
120 case self::MUTATOR:
121 $tag = 'param';
122 break;
123 }
124 $parentClass = null;
125 $types = [];
126 /** @var DocBlock\Tags\Var_|DocBlock\Tags\Return_|DocBlock\Tags\Param $tag */
127 foreach ($docBlock->getTagsByName($tag) as $tag) {
128 if ($tag && !$tag instanceof InvalidTag && null !== $tag->getType()) {
129 foreach ($this->phpDocTypeHelper->getTypes($tag->getType()) as $type) {
130 switch ($type->getClassName()) {
131 case 'self':
132 case 'static':
133 $resolvedClass = $class;
134 break;
135 case 'parent':
136 if (\false !== $resolvedClass = $parentClass ?? $parentClass = get_parent_class($class)) {
137 break;
138 }
139 // no break
140 default:
141 $types[] = $type;
142 continue 2;
143 }
144 $types[] = new Type(Type::BUILTIN_TYPE_OBJECT, $type->isNullable(), $resolvedClass, $type->isCollection(), $type->getCollectionKeyTypes(), $type->getCollectionValueTypes());
145 }
146 }
147 }
148 if (!isset($types[0])) {
149 return null;
150 }
151 if (!\in_array($prefix, $this->arrayMutatorPrefixes)) {
152 return $types;
153 }
154 return [new Type(Type::BUILTIN_TYPE_ARRAY, \false, null, \true, new Type(Type::BUILTIN_TYPE_INT), $types[0])];
155 }
156 /**
157 * {@inheritdoc}
158 */
159 public function getTypesFromConstructor(string $class, string $property): ?array
160 {
161 $docBlock = $this->getDocBlockFromConstructor($class, $property);
162 if (!$docBlock) {
163 return null;
164 }
165 $types = [];
166 /** @var DocBlock\Tags\Var_|DocBlock\Tags\Return_|DocBlock\Tags\Param $tag */
167 foreach ($docBlock->getTagsByName('param') as $tag) {
168 if ($tag && null !== $tag->getType()) {
169 $types[] = $this->phpDocTypeHelper->getTypes($tag->getType());
170 }
171 }
172 if (!isset($types[0]) || [] === $types[0]) {
173 return null;
174 }
175 return array_merge([], ...$types);
176 }
177 private function getDocBlockFromConstructor(string $class, string $property): ?DocBlock
178 {
179 try {
180 $reflectionClass = new \ReflectionClass($class);
181 } catch (\ReflectionException $e) {
182 return null;
183 }
184 $reflectionConstructor = $reflectionClass->getConstructor();
185 if (!$reflectionConstructor) {
186 return null;
187 }
188 try {
189 $docBlock = $this->docBlockFactory->create($reflectionConstructor, $this->contextFactory->createFromReflector($reflectionConstructor));
190 return $this->filterDocBlockParams($docBlock, $property);
191 } catch (\InvalidArgumentException $e) {
192 return null;
193 }
194 }
195 private function filterDocBlockParams(DocBlock $docBlock, string $allowedParam): DocBlock
196 {
197 $tags = array_values(array_filter($docBlock->getTagsByName('param'), function ($tag) use ($allowedParam) {
198 return $tag instanceof DocBlock\Tags\Param && $allowedParam === $tag->getVariableName();
199 }));
200 return new DocBlock($docBlock->getSummary(), $docBlock->getDescription(), $tags, $docBlock->getContext(), $docBlock->getLocation(), $docBlock->isTemplateStart(), $docBlock->isTemplateEnd());
201 }
202 /**
203 * @return array{DocBlock|null, int|null, string|null}
204 */
205 private function getDocBlock(string $class, string $property): array
206 {
207 $propertyHash = sprintf('%s::%s', $class, $property);
208 if (isset($this->docBlocks[$propertyHash])) {
209 return $this->docBlocks[$propertyHash];
210 }
211 $ucFirstProperty = ucfirst($property);
212 switch (\true) {
213 case $docBlock = $this->getDocBlockFromProperty($class, $property):
214 $data = [$docBlock, self::PROPERTY, null];
215 break;
216 case [$docBlock] = $this->getDocBlockFromMethod($class, $ucFirstProperty, self::ACCESSOR):
217 $data = [$docBlock, self::ACCESSOR, null];
218 break;
219 case [$docBlock, $prefix] = $this->getDocBlockFromMethod($class, $ucFirstProperty, self::MUTATOR):
220 $data = [$docBlock, self::MUTATOR, $prefix];
221 break;
222 default:
223 $data = [null, null, null];
224 }
225 return $this->docBlocks[$propertyHash] = $data;
226 }
227 private function getDocBlockFromProperty(string $class, string $property): ?DocBlock
228 {
229 // Use a ReflectionProperty instead of $class to get the parent class if applicable
230 try {
231 $reflectionProperty = new \ReflectionProperty($class, $property);
232 } catch (\ReflectionException $e) {
233 return null;
234 }
235 $reflector = $reflectionProperty->getDeclaringClass();
236 foreach ($reflector->getTraits() as $trait) {
237 if ($trait->hasProperty($property)) {
238 return $this->getDocBlockFromProperty($trait->getName(), $property);
239 }
240 }
241 try {
242 return $this->docBlockFactory->create($reflectionProperty, $this->createFromReflector($reflector));
243 } catch (\InvalidArgumentException|\RuntimeException $e) {
244 return null;
245 }
246 }
247 /**
248 * @return array{DocBlock, string}|null
249 */
250 private function getDocBlockFromMethod(string $class, string $ucFirstProperty, int $type): ?array
251 {
252 $prefixes = (self::ACCESSOR === $type) ? $this->accessorPrefixes : $this->mutatorPrefixes;
253 $prefix = null;
254 foreach ($prefixes as $prefix) {
255 $methodName = $prefix . $ucFirstProperty;
256 try {
257 $reflectionMethod = new \ReflectionMethod($class, $methodName);
258 if ($reflectionMethod->isStatic()) {
259 continue;
260 }
261 if (self::ACCESSOR === $type && 0 === $reflectionMethod->getNumberOfRequiredParameters() || self::MUTATOR === $type && $reflectionMethod->getNumberOfParameters() >= 1) {
262 break;
263 }
264 } catch (\ReflectionException $e) {
265 // Try the next prefix if the method doesn't exist
266 }
267 }
268 if (!isset($reflectionMethod)) {
269 return null;
270 }
271 $reflector = $reflectionMethod->getDeclaringClass();
272 foreach ($reflector->getTraits() as $trait) {
273 if ($trait->hasMethod($methodName)) {
274 return $this->getDocBlockFromMethod($trait->getName(), $ucFirstProperty, $type);
275 }
276 }
277 try {
278 return [$this->docBlockFactory->create($reflectionMethod, $this->createFromReflector($reflector)), $prefix];
279 } catch (\InvalidArgumentException|\RuntimeException $e) {
280 return null;
281 }
282 }
283 /**
284 * Prevents a lot of redundant calls to ContextFactory::createForNamespace().
285 */
286 private function createFromReflector(\ReflectionClass $reflector): Context
287 {
288 $cacheKey = $reflector->getNamespaceName() . ':' . $reflector->getFileName();
289 if (isset($this->contexts[$cacheKey])) {
290 return $this->contexts[$cacheKey];
291 }
292 $this->contexts[$cacheKey] = $this->contextFactory->createFromReflector($reflector);
293 return $this->contexts[$cacheKey];
294 }
295 }
296