PluginProbe
WindPress – Tailwind CSS integration for WordPress / 3.0.0
WindPress – Tailwind CSS integration for WordPress v3.0.0
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 / ReflectionExtractor.php

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

708 lines 32.8 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\Symfony\Component\PropertyInfo\PropertyAccessExtractorInterface;
14 use WindPressPackages\Symfony\Component\PropertyInfo\PropertyInitializableExtractorInterface;
15 use WindPressPackages\Symfony\Component\PropertyInfo\PropertyListExtractorInterface;
16 use WindPressPackages\Symfony\Component\PropertyInfo\PropertyReadInfo;
17 use WindPressPackages\Symfony\Component\PropertyInfo\PropertyReadInfoExtractorInterface;
18 use WindPressPackages\Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
19 use WindPressPackages\Symfony\Component\PropertyInfo\PropertyWriteInfo;
20 use WindPressPackages\Symfony\Component\PropertyInfo\PropertyWriteInfoExtractorInterface;
21 use WindPressPackages\Symfony\Component\PropertyInfo\Type;
22 use WindPressPackages\Symfony\Component\String\Inflector\EnglishInflector;
23 use WindPressPackages\Symfony\Component\String\Inflector\InflectorInterface;
24 /**
25 * Extracts data using the reflection API.
26 *
27 * @author Kévin Dunglas <dunglas@gmail.com>
28 *
29 * @final
30 */
31 class ReflectionExtractor implements PropertyListExtractorInterface, PropertyTypeExtractorInterface, PropertyAccessExtractorInterface, PropertyInitializableExtractorInterface, PropertyReadInfoExtractorInterface, PropertyWriteInfoExtractorInterface, ConstructorArgumentTypeExtractorInterface
32 {
33 /**
34 * @internal
35 */
36 public static $defaultMutatorPrefixes = ['add', 'remove', 'set'];
37 /**
38 * @internal
39 */
40 public static $defaultAccessorPrefixes = ['get', 'is', 'has', 'can'];
41 /**
42 * @internal
43 */
44 public static $defaultArrayMutatorPrefixes = ['add', 'remove'];
45 public const ALLOW_PRIVATE = 1;
46 public const ALLOW_PROTECTED = 2;
47 public const ALLOW_PUBLIC = 4;
48 /** @var int Allow none of the magic methods */
49 public const DISALLOW_MAGIC_METHODS = 0;
50 /** @var int Allow magic __get methods */
51 public const ALLOW_MAGIC_GET = 1 << 0;
52 /** @var int Allow magic __set methods */
53 public const ALLOW_MAGIC_SET = 1 << 1;
54 /** @var int Allow magic __call methods */
55 public const ALLOW_MAGIC_CALL = 1 << 2;
56 private const MAP_TYPES = ['integer' => Type::BUILTIN_TYPE_INT, 'boolean' => Type::BUILTIN_TYPE_BOOL, 'double' => Type::BUILTIN_TYPE_FLOAT];
57 private $mutatorPrefixes;
58 private $accessorPrefixes;
59 private $arrayMutatorPrefixes;
60 private $enableConstructorExtraction;
61 private $methodReflectionFlags;
62 private $magicMethodsFlags;
63 private $propertyReflectionFlags;
64 private $inflector;
65 private $arrayMutatorPrefixesFirst;
66 private $arrayMutatorPrefixesLast;
67 /**
68 * @param string[]|null $mutatorPrefixes
69 * @param string[]|null $accessorPrefixes
70 * @param string[]|null $arrayMutatorPrefixes
71 */
72 public function __construct(?array $mutatorPrefixes = null, ?array $accessorPrefixes = null, ?array $arrayMutatorPrefixes = null, bool $enableConstructorExtraction = \true, int $accessFlags = self::ALLOW_PUBLIC, ?InflectorInterface $inflector = null, int $magicMethodsFlags = self::ALLOW_MAGIC_GET | self::ALLOW_MAGIC_SET)
73 {
74 $this->mutatorPrefixes = $mutatorPrefixes ?? self::$defaultMutatorPrefixes;
75 $this->accessorPrefixes = $accessorPrefixes ?? self::$defaultAccessorPrefixes;
76 $this->arrayMutatorPrefixes = $arrayMutatorPrefixes ?? self::$defaultArrayMutatorPrefixes;
77 $this->enableConstructorExtraction = $enableConstructorExtraction;
78 $this->methodReflectionFlags = $this->getMethodsFlags($accessFlags);
79 $this->propertyReflectionFlags = $this->getPropertyFlags($accessFlags);
80 $this->magicMethodsFlags = $magicMethodsFlags;
81 $this->inflector = $inflector ?? new EnglishInflector();
82 $this->arrayMutatorPrefixesFirst = array_merge($this->arrayMutatorPrefixes, array_diff($this->mutatorPrefixes, $this->arrayMutatorPrefixes));
83 $this->arrayMutatorPrefixesLast = array_reverse($this->arrayMutatorPrefixesFirst);
84 }
85 /**
86 * {@inheritdoc}
87 */
88 public function getProperties(string $class, array $context = []): ?array
89 {
90 try {
91 $reflectionClass = new \ReflectionClass($class);
92 } catch (\ReflectionException $e) {
93 return null;
94 }
95 $reflectionProperties = $reflectionClass->getProperties();
96 $properties = [];
97 foreach ($reflectionProperties as $reflectionProperty) {
98 if ($reflectionProperty->getModifiers() & $this->propertyReflectionFlags) {
99 $properties[$reflectionProperty->name] = $reflectionProperty->name;
100 }
101 }
102 foreach ($reflectionClass->getMethods($this->methodReflectionFlags) as $reflectionMethod) {
103 if ($reflectionMethod->isStatic()) {
104 continue;
105 }
106 $propertyName = $this->getPropertyName($reflectionMethod->name, $reflectionProperties);
107 if (!$propertyName || isset($properties[$propertyName])) {
108 continue;
109 }
110 if ($reflectionClass->hasProperty($lowerCasedPropertyName = lcfirst($propertyName)) || !$reflectionClass->hasProperty($propertyName) && !preg_match('/^[A-Z]{2,}/', $propertyName)) {
111 $propertyName = $lowerCasedPropertyName;
112 }
113 $properties[$propertyName] = $propertyName;
114 }
115 return $properties ? array_values($properties) : null;
116 }
117 /**
118 * {@inheritdoc}
119 */
120 public function getTypes(string $class, string $property, array $context = []): ?array
121 {
122 if ($fromMutator = $this->extractFromMutator($class, $property)) {
123 return $fromMutator;
124 }
125 if ($fromAccessor = $this->extractFromAccessor($class, $property)) {
126 return $fromAccessor;
127 }
128 if (($context['enable_constructor_extraction'] ?? $this->enableConstructorExtraction) && $fromConstructor = $this->extractFromConstructor($class, $property)) {
129 return $fromConstructor;
130 }
131 if ($fromPropertyDeclaration = $this->extractFromPropertyDeclaration($class, $property)) {
132 return $fromPropertyDeclaration;
133 }
134 return null;
135 }
136 /**
137 * {@inheritdoc}
138 */
139 public function getTypesFromConstructor(string $class, string $property): ?array
140 {
141 try {
142 $reflection = new \ReflectionClass($class);
143 } catch (\ReflectionException $e) {
144 return null;
145 }
146 if (!$reflectionConstructor = $reflection->getConstructor()) {
147 return null;
148 }
149 if (!$reflectionParameter = $this->getReflectionParameterFromConstructor($property, $reflectionConstructor)) {
150 return null;
151 }
152 if (!$reflectionType = $reflectionParameter->getType()) {
153 return null;
154 }
155 if (!$types = $this->extractFromReflectionType($reflectionType, $reflectionConstructor->getDeclaringClass())) {
156 return null;
157 }
158 return $types;
159 }
160 private function getReflectionParameterFromConstructor(string $property, \ReflectionMethod $reflectionConstructor): ?\ReflectionParameter
161 {
162 $reflectionParameter = null;
163 foreach ($reflectionConstructor->getParameters() as $reflectionParameter) {
164 if ($reflectionParameter->getName() === $property) {
165 return $reflectionParameter;
166 }
167 }
168 return null;
169 }
170 /**
171 * {@inheritdoc}
172 */
173 public function isReadable(string $class, string $property, array $context = []): ?bool
174 {
175 if ($this->isAllowedProperty($class, $property)) {
176 return \true;
177 }
178 return null !== $this->getReadInfo($class, $property, $context);
179 }
180 /**
181 * {@inheritdoc}
182 */
183 public function isWritable(string $class, string $property, array $context = []): ?bool
184 {
185 if ($this->isAllowedProperty($class, $property, \true)) {
186 return \true;
187 }
188 [$reflectionMethod] = $this->getMutatorMethod($class, $property);
189 return null !== $reflectionMethod;
190 }
191 /**
192 * {@inheritdoc}
193 */
194 public function isInitializable(string $class, string $property, array $context = []): ?bool
195 {
196 try {
197 $reflectionClass = new \ReflectionClass($class);
198 } catch (\ReflectionException $e) {
199 return null;
200 }
201 if (!$reflectionClass->isInstantiable()) {
202 return \false;
203 }
204 if ($constructor = $reflectionClass->getConstructor()) {
205 foreach ($constructor->getParameters() as $parameter) {
206 if ($property === $parameter->name) {
207 return \true;
208 }
209 }
210 } elseif ($parentClass = $reflectionClass->getParentClass()) {
211 return $this->isInitializable($parentClass->getName(), $property);
212 }
213 return \false;
214 }
215 /**
216 * {@inheritdoc}
217 */
218 public function getReadInfo(string $class, string $property, array $context = []): ?PropertyReadInfo
219 {
220 try {
221 $reflClass = new \ReflectionClass($class);
222 } catch (\ReflectionException $e) {
223 return null;
224 }
225 $allowGetterSetter = $context['enable_getter_setter_extraction'] ?? \false;
226 $magicMethods = $context['enable_magic_methods_extraction'] ?? $this->magicMethodsFlags;
227 $allowMagicCall = (bool) ($magicMethods & self::ALLOW_MAGIC_CALL);
228 $allowMagicGet = (bool) ($magicMethods & self::ALLOW_MAGIC_GET);
229 if (isset($context['enable_magic_call_extraction'])) {
230 trigger_deprecation('symfony/property-info', '5.2', 'Using the "enable_magic_call_extraction" context option in "%s()" is deprecated. Use "enable_magic_methods_extraction" instead.', __METHOD__);
231 $allowMagicCall = $context['enable_magic_call_extraction'] ?? \false;
232 }
233 $hasProperty = $reflClass->hasProperty($property);
234 $camelProp = $this->camelize($property);
235 $getsetter = lcfirst($camelProp);
236 // jQuery style, e.g. read: last(), write: last($item)
237 foreach ($this->accessorPrefixes as $prefix) {
238 $methodName = $prefix . $camelProp;
239 if ($reflClass->hasMethod($methodName) && $reflClass->getMethod($methodName)->getModifiers() & $this->methodReflectionFlags && !$reflClass->getMethod($methodName)->getNumberOfRequiredParameters()) {
240 $method = $reflClass->getMethod($methodName);
241 return new PropertyReadInfo(PropertyReadInfo::TYPE_METHOD, $methodName, $this->getReadVisiblityForMethod($method), $method->isStatic(), \false);
242 }
243 }
244 if ($allowGetterSetter && $reflClass->hasMethod($getsetter) && $reflClass->getMethod($getsetter)->getModifiers() & $this->methodReflectionFlags) {
245 $method = $reflClass->getMethod($getsetter);
246 return new PropertyReadInfo(PropertyReadInfo::TYPE_METHOD, $getsetter, $this->getReadVisiblityForMethod($method), $method->isStatic(), \false);
247 }
248 if ($allowMagicGet && $reflClass->hasMethod('__get') && $reflClass->getMethod('__get')->getModifiers() & $this->methodReflectionFlags) {
249 return new PropertyReadInfo(PropertyReadInfo::TYPE_PROPERTY, $property, PropertyReadInfo::VISIBILITY_PUBLIC, \false, \false);
250 }
251 if ($hasProperty && $reflClass->getProperty($property)->getModifiers() & $this->propertyReflectionFlags) {
252 $reflProperty = $reflClass->getProperty($property);
253 return new PropertyReadInfo(PropertyReadInfo::TYPE_PROPERTY, $property, $this->getReadVisiblityForProperty($reflProperty), $reflProperty->isStatic(), \true);
254 }
255 if ($allowMagicCall && $reflClass->hasMethod('__call') && $reflClass->getMethod('__call')->getModifiers() & $this->methodReflectionFlags) {
256 return new PropertyReadInfo(PropertyReadInfo::TYPE_METHOD, 'get' . $camelProp, PropertyReadInfo::VISIBILITY_PUBLIC, \false, \false);
257 }
258 return null;
259 }
260 /**
261 * {@inheritdoc}
262 */
263 public function getWriteInfo(string $class, string $property, array $context = []): ?PropertyWriteInfo
264 {
265 try {
266 $reflClass = new \ReflectionClass($class);
267 } catch (\ReflectionException $e) {
268 return null;
269 }
270 $allowGetterSetter = $context['enable_getter_setter_extraction'] ?? \false;
271 $magicMethods = $context['enable_magic_methods_extraction'] ?? $this->magicMethodsFlags;
272 $allowMagicCall = (bool) ($magicMethods & self::ALLOW_MAGIC_CALL);
273 $allowMagicSet = (bool) ($magicMethods & self::ALLOW_MAGIC_SET);
274 if (isset($context['enable_magic_call_extraction'])) {
275 trigger_deprecation('symfony/property-info', '5.2', 'Using the "enable_magic_call_extraction" context option in "%s()" is deprecated. Use "enable_magic_methods_extraction" instead.', __METHOD__);
276 $allowMagicCall = $context['enable_magic_call_extraction'] ?? \false;
277 }
278 $allowConstruct = $context['enable_constructor_extraction'] ?? $this->enableConstructorExtraction;
279 $allowAdderRemover = $context['enable_adder_remover_extraction'] ?? \true;
280 $camelized = $this->camelize($property);
281 $constructor = $reflClass->getConstructor();
282 $singulars = $this->inflector->singularize($camelized);
283 $errors = [];
284 if (null !== $constructor && $allowConstruct) {
285 foreach ($constructor->getParameters() as $parameter) {
286 if ($parameter->getName() === $property) {
287 return new PropertyWriteInfo(PropertyWriteInfo::TYPE_CONSTRUCTOR, $property);
288 }
289 }
290 }
291 [$adderAccessName, $removerAccessName, $adderAndRemoverErrors] = $this->findAdderAndRemover($reflClass, $singulars);
292 if ($allowAdderRemover && null !== $adderAccessName && null !== $removerAccessName) {
293 $adderMethod = $reflClass->getMethod($adderAccessName);
294 $removerMethod = $reflClass->getMethod($removerAccessName);
295 $mutator = new PropertyWriteInfo(PropertyWriteInfo::TYPE_ADDER_AND_REMOVER);
296 $mutator->setAdderInfo(new PropertyWriteInfo(PropertyWriteInfo::TYPE_METHOD, $adderAccessName, $this->getWriteVisiblityForMethod($adderMethod), $adderMethod->isStatic()));
297 $mutator->setRemoverInfo(new PropertyWriteInfo(PropertyWriteInfo::TYPE_METHOD, $removerAccessName, $this->getWriteVisiblityForMethod($removerMethod), $removerMethod->isStatic()));
298 return $mutator;
299 }
300 $errors[] = $adderAndRemoverErrors;
301 foreach ($this->mutatorPrefixes as $mutatorPrefix) {
302 $methodName = $mutatorPrefix . $camelized;
303 [$accessible, $methodAccessibleErrors] = $this->isMethodAccessible($reflClass, $methodName, 1);
304 if (!$accessible) {
305 $errors[] = $methodAccessibleErrors;
306 continue;
307 }
308 $method = $reflClass->getMethod($methodName);
309 if (!\in_array($mutatorPrefix, $this->arrayMutatorPrefixes, \true)) {
310 return new PropertyWriteInfo(PropertyWriteInfo::TYPE_METHOD, $methodName, $this->getWriteVisiblityForMethod($method), $method->isStatic());
311 }
312 }
313 $getsetter = lcfirst($camelized);
314 if ($allowGetterSetter) {
315 [$accessible, $methodAccessibleErrors] = $this->isMethodAccessible($reflClass, $getsetter, 1);
316 if ($accessible) {
317 $method = $reflClass->getMethod($getsetter);
318 return new PropertyWriteInfo(PropertyWriteInfo::TYPE_METHOD, $getsetter, $this->getWriteVisiblityForMethod($method), $method->isStatic());
319 }
320 $errors[] = $methodAccessibleErrors;
321 }
322 if ($reflClass->hasProperty($property) && $reflClass->getProperty($property)->getModifiers() & $this->propertyReflectionFlags) {
323 $reflProperty = $reflClass->getProperty($property);
324 if (\PHP_VERSION_ID < 80100 || !$reflProperty->isReadOnly()) {
325 return new PropertyWriteInfo(PropertyWriteInfo::TYPE_PROPERTY, $property, $this->getWriteVisiblityForProperty($reflProperty), $reflProperty->isStatic());
326 }
327 $errors[] = [sprintf('The property "%s" in class "%s" is a promoted readonly property.', $property, $reflClass->getName())];
328 $allowMagicSet = $allowMagicCall = \false;
329 }
330 if ($allowMagicSet) {
331 [$accessible, $methodAccessibleErrors] = $this->isMethodAccessible($reflClass, '__set', 2);
332 if ($accessible) {
333 return new PropertyWriteInfo(PropertyWriteInfo::TYPE_PROPERTY, $property, PropertyWriteInfo::VISIBILITY_PUBLIC, \false);
334 }
335 $errors[] = $methodAccessibleErrors;
336 }
337 if ($allowMagicCall) {
338 [$accessible, $methodAccessibleErrors] = $this->isMethodAccessible($reflClass, '__call', 2);
339 if ($accessible) {
340 return new PropertyWriteInfo(PropertyWriteInfo::TYPE_METHOD, 'set' . $camelized, PropertyWriteInfo::VISIBILITY_PUBLIC, \false);
341 }
342 $errors[] = $methodAccessibleErrors;
343 }
344 if (!$allowAdderRemover && null !== $adderAccessName && null !== $removerAccessName) {
345 $errors[] = [sprintf('The property "%s" in class "%s" can be defined with the methods "%s()" but ' . 'the new value must be an array or an instance of \Traversable', $property, $reflClass->getName(), implode('()", "', [$adderAccessName, $removerAccessName]))];
346 }
347 $noneProperty = new PropertyWriteInfo();
348 $noneProperty->setErrors(array_merge([], ...$errors));
349 return $noneProperty;
350 }
351 /**
352 * @return Type[]|null
353 */
354 private function extractFromMutator(string $class, string $property): ?array
355 {
356 [$reflectionMethod, $prefix] = $this->getMutatorMethod($class, $property);
357 if (null === $reflectionMethod) {
358 return null;
359 }
360 $reflectionParameters = $reflectionMethod->getParameters();
361 $reflectionParameter = $reflectionParameters[0];
362 if (!$reflectionType = $reflectionParameter->getType()) {
363 return null;
364 }
365 $type = $this->extractFromReflectionType($reflectionType, $reflectionMethod->getDeclaringClass());
366 if (1 === \count($type) && \in_array($prefix, $this->arrayMutatorPrefixes)) {
367 $type = [new Type(Type::BUILTIN_TYPE_ARRAY, $this->isNullableProperty($class, $property), null, \true, new Type(Type::BUILTIN_TYPE_INT), $type[0])];
368 }
369 return $type;
370 }
371 /**
372 * Tries to extract type information from accessors.
373 *
374 * @return Type[]|null
375 */
376 private function extractFromAccessor(string $class, string $property): ?array
377 {
378 [$reflectionMethod, $prefix] = $this->getAccessorMethod($class, $property);
379 if (null === $reflectionMethod) {
380 return null;
381 }
382 if ($reflectionType = $reflectionMethod->getReturnType()) {
383 return $this->extractFromReflectionType($reflectionType, $reflectionMethod->getDeclaringClass());
384 }
385 if (\in_array($prefix, ['is', 'can', 'has'])) {
386 return [new Type(Type::BUILTIN_TYPE_BOOL)];
387 }
388 return null;
389 }
390 /**
391 * Tries to extract type information from constructor.
392 *
393 * @return Type[]|null
394 */
395 private function extractFromConstructor(string $class, string $property): ?array
396 {
397 try {
398 $reflectionClass = new \ReflectionClass($class);
399 } catch (\ReflectionException $e) {
400 return null;
401 }
402 $constructor = $reflectionClass->getConstructor();
403 if (!$constructor) {
404 return null;
405 }
406 foreach ($constructor->getParameters() as $parameter) {
407 if ($property !== $parameter->name) {
408 continue;
409 }
410 $reflectionType = $parameter->getType();
411 return $reflectionType ? $this->extractFromReflectionType($reflectionType, $constructor->getDeclaringClass()) : null;
412 }
413 if ($parentClass = $reflectionClass->getParentClass()) {
414 return $this->extractFromConstructor($parentClass->getName(), $property);
415 }
416 return null;
417 }
418 private function extractFromPropertyDeclaration(string $class, string $property): ?array
419 {
420 try {
421 $reflectionClass = new \ReflectionClass($class);
422 if (\PHP_VERSION_ID >= 70400) {
423 $reflectionProperty = $reflectionClass->getProperty($property);
424 $reflectionPropertyType = $reflectionProperty->getType();
425 if (null !== $reflectionPropertyType && $types = $this->extractFromReflectionType($reflectionPropertyType, $reflectionProperty->getDeclaringClass())) {
426 return $types;
427 }
428 }
429 } catch (\ReflectionException $e) {
430 return null;
431 }
432 $defaultValue = $reflectionClass->getDefaultProperties()[$property] ?? null;
433 if (null === $defaultValue) {
434 return null;
435 }
436 $type = \gettype($defaultValue);
437 $type = static::MAP_TYPES[$type] ?? $type;
438 return [new Type($type, $this->isNullableProperty($class, $property), null, Type::BUILTIN_TYPE_ARRAY === $type)];
439 }
440 private function extractFromReflectionType(\ReflectionType $reflectionType, \ReflectionClass $declaringClass): array
441 {
442 $types = [];
443 $nullable = $reflectionType->allowsNull();
444 foreach (($reflectionType instanceof \ReflectionUnionType || $reflectionType instanceof \ReflectionIntersectionType) ? $reflectionType->getTypes() : [$reflectionType] as $type) {
445 if (!$type instanceof \ReflectionNamedType) {
446 // Nested composite types are not supported yet.
447 return [];
448 }
449 $phpTypeOrClass = $type->getName();
450 if ('null' === $phpTypeOrClass || 'mixed' === $phpTypeOrClass || 'never' === $phpTypeOrClass) {
451 continue;
452 }
453 if (Type::BUILTIN_TYPE_ARRAY === $phpTypeOrClass) {
454 $types[] = new Type(Type::BUILTIN_TYPE_ARRAY, $nullable, null, \true);
455 } elseif ('void' === $phpTypeOrClass) {
456 $types[] = new Type(Type::BUILTIN_TYPE_NULL, $nullable);
457 } elseif ($type->isBuiltin()) {
458 $types[] = new Type($phpTypeOrClass, $nullable);
459 } else {
460 $types[] = new Type(Type::BUILTIN_TYPE_OBJECT, $nullable, $this->resolveTypeName($phpTypeOrClass, $declaringClass));
461 }
462 }
463 return $types;
464 }
465 private function resolveTypeName(string $name, \ReflectionClass $declaringClass): string
466 {
467 if ('self' === $lcName = strtolower($name)) {
468 return $declaringClass->name;
469 }
470 if ('parent' === $lcName && $parent = $declaringClass->getParentClass()) {
471 return $parent->name;
472 }
473 return $name;
474 }
475 private function isNullableProperty(string $class, string $property): bool
476 {
477 try {
478 $reflectionProperty = new \ReflectionProperty($class, $property);
479 if (\PHP_VERSION_ID >= 70400) {
480 $reflectionPropertyType = $reflectionProperty->getType();
481 return null !== $reflectionPropertyType && $reflectionPropertyType->allowsNull();
482 }
483 return \false;
484 } catch (\ReflectionException $e) {
485 // Return false if the property doesn't exist
486 }
487 return \false;
488 }
489 private function isAllowedProperty(string $class, string $property, bool $writeAccessRequired = \false): bool
490 {
491 try {
492 $reflectionProperty = new \ReflectionProperty($class, $property);
493 if (\PHP_VERSION_ID >= 80100 && $writeAccessRequired && $reflectionProperty->isReadOnly()) {
494 return \false;
495 }
496 return (bool) ($reflectionProperty->getModifiers() & $this->propertyReflectionFlags);
497 } catch (\ReflectionException $e) {
498 // Return false if the property doesn't exist
499 }
500 return \false;
501 }
502 /**
503 * Gets the accessor method.
504 *
505 * Returns an array with a the instance of \ReflectionMethod as first key
506 * and the prefix of the method as second or null if not found.
507 */
508 private function getAccessorMethod(string $class, string $property): ?array
509 {
510 $ucProperty = ucfirst($property);
511 foreach ($this->accessorPrefixes as $prefix) {
512 try {
513 $reflectionMethod = new \ReflectionMethod($class, $prefix . $ucProperty);
514 if ($reflectionMethod->isStatic()) {
515 continue;
516 }
517 if (0 === $reflectionMethod->getNumberOfRequiredParameters()) {
518 return [$reflectionMethod, $prefix];
519 }
520 } catch (\ReflectionException $e) {
521 // Return null if the property doesn't exist
522 }
523 }
524 return null;
525 }
526 /**
527 * Returns an array with a the instance of \ReflectionMethod as first key
528 * and the prefix of the method as second or null if not found.
529 */
530 private function getMutatorMethod(string $class, string $property): ?array
531 {
532 $ucProperty = ucfirst($property);
533 $ucSingulars = $this->inflector->singularize($ucProperty);
534 $mutatorPrefixes = \in_array($ucProperty, $ucSingulars, \true) ? $this->arrayMutatorPrefixesLast : $this->arrayMutatorPrefixesFirst;
535 foreach ($mutatorPrefixes as $prefix) {
536 $names = [$ucProperty];
537 if (\in_array($prefix, $this->arrayMutatorPrefixes)) {
538 $names = array_merge($names, $ucSingulars);
539 }
540 foreach ($names as $name) {
541 try {
542 $reflectionMethod = new \ReflectionMethod($class, $prefix . $name);
543 if ($reflectionMethod->isStatic()) {
544 continue;
545 }
546 // Parameter can be optional to allow things like: method(?array $foo = null)
547 if ($reflectionMethod->getNumberOfParameters() >= 1) {
548 return [$reflectionMethod, $prefix];
549 }
550 } catch (\ReflectionException $e) {
551 // Try the next prefix if the method doesn't exist
552 }
553 }
554 }
555 return null;
556 }
557 private function getPropertyName(string $methodName, array $reflectionProperties): ?string
558 {
559 $pattern = implode('|', array_merge($this->accessorPrefixes, $this->mutatorPrefixes));
560 if ('' !== $pattern && preg_match('/^(' . $pattern . ')(.+)$/i', $methodName, $matches)) {
561 if (!\in_array($matches[1], $this->arrayMutatorPrefixes)) {
562 return $matches[2];
563 }
564 foreach ($reflectionProperties as $reflectionProperty) {
565 foreach ($this->inflector->singularize($reflectionProperty->name) as $name) {
566 if (strtolower($name) === strtolower($matches[2])) {
567 return $reflectionProperty->name;
568 }
569 }
570 }
571 return $matches[2];
572 }
573 return null;
574 }
575 /**
576 * Searches for add and remove methods.
577 *
578 * @param \ReflectionClass $reflClass The reflection class for the given object
579 * @param array $singulars The singular form of the property name or null
580 *
581 * @return array An array containing the adder and remover when found and errors
582 */
583 private function findAdderAndRemover(\ReflectionClass $reflClass, array $singulars): array
584 {
585 if (!\is_array($this->arrayMutatorPrefixes) && 2 !== \count($this->arrayMutatorPrefixes)) {
586 return [null, null, []];
587 }
588 [$addPrefix, $removePrefix] = $this->arrayMutatorPrefixes;
589 $errors = [];
590 foreach ($singulars as $singular) {
591 $addMethod = $addPrefix . $singular;
592 $removeMethod = $removePrefix . $singular;
593 [$addMethodFound, $addMethodAccessibleErrors] = $this->isMethodAccessible($reflClass, $addMethod, 1);
594 [$removeMethodFound, $removeMethodAccessibleErrors] = $this->isMethodAccessible($reflClass, $removeMethod, 1);
595 $errors[] = $addMethodAccessibleErrors;
596 $errors[] = $removeMethodAccessibleErrors;
597 if ($addMethodFound && $removeMethodFound) {
598 return [$addMethod, $removeMethod, []];
599 }
600 if ($addMethodFound && !$removeMethodFound) {
601 $errors[] = [sprintf('The add method "%s" in class "%s" was found, but the corresponding remove method "%s" was not found', $addMethod, $reflClass->getName(), $removeMethod)];
602 } elseif (!$addMethodFound && $removeMethodFound) {
603 $errors[] = [sprintf('The remove method "%s" in class "%s" was found, but the corresponding add method "%s" was not found', $removeMethod, $reflClass->getName(), $addMethod)];
604 }
605 }
606 return [null, null, array_merge([], ...$errors)];
607 }
608 /**
609 * Returns whether a method is public and has the number of required parameters and errors.
610 */
611 private function isMethodAccessible(\ReflectionClass $class, string $methodName, int $parameters): array
612 {
613 $errors = [];
614 if ($class->hasMethod($methodName)) {
615 $method = $class->getMethod($methodName);
616 if (\ReflectionMethod::IS_PUBLIC === $this->methodReflectionFlags && !$method->isPublic()) {
617 $errors[] = sprintf('The method "%s" in class "%s" was found but does not have public access.', $methodName, $class->getName());
618 } elseif ($method->getNumberOfRequiredParameters() > $parameters || $method->getNumberOfParameters() < $parameters) {
619 $errors[] = sprintf('The method "%s" in class "%s" requires %d arguments, but should accept only %d.', $methodName, $class->getName(), $method->getNumberOfRequiredParameters(), $parameters);
620 } else {
621 return [\true, $errors];
622 }
623 }
624 return [\false, $errors];
625 }
626 /**
627 * Camelizes a given string.
628 */
629 private function camelize(string $string): string
630 {
631 return str_replace(' ', '', ucwords(str_replace('_', ' ', $string)));
632 }
633 /**
634 * Return allowed reflection method flags.
635 */
636 private function getMethodsFlags(int $accessFlags): int
637 {
638 $methodFlags = 0;
639 if ($accessFlags & self::ALLOW_PUBLIC) {
640 $methodFlags |= \ReflectionMethod::IS_PUBLIC;
641 }
642 if ($accessFlags & self::ALLOW_PRIVATE) {
643 $methodFlags |= \ReflectionMethod::IS_PRIVATE;
644 }
645 if ($accessFlags & self::ALLOW_PROTECTED) {
646 $methodFlags |= \ReflectionMethod::IS_PROTECTED;
647 }
648 return $methodFlags;
649 }
650 /**
651 * Return allowed reflection property flags.
652 */
653 private function getPropertyFlags(int $accessFlags): int
654 {
655 $propertyFlags = 0;
656 if ($accessFlags & self::ALLOW_PUBLIC) {
657 $propertyFlags |= \ReflectionProperty::IS_PUBLIC;
658 }
659 if ($accessFlags & self::ALLOW_PRIVATE) {
660 $propertyFlags |= \ReflectionProperty::IS_PRIVATE;
661 }
662 if ($accessFlags & self::ALLOW_PROTECTED) {
663 $propertyFlags |= \ReflectionProperty::IS_PROTECTED;
664 }
665 return $propertyFlags;
666 }
667 private function getReadVisiblityForProperty(\ReflectionProperty $reflectionProperty): string
668 {
669 if ($reflectionProperty->isPrivate()) {
670 return PropertyReadInfo::VISIBILITY_PRIVATE;
671 }
672 if ($reflectionProperty->isProtected()) {
673 return PropertyReadInfo::VISIBILITY_PROTECTED;
674 }
675 return PropertyReadInfo::VISIBILITY_PUBLIC;
676 }
677 private function getReadVisiblityForMethod(\ReflectionMethod $reflectionMethod): string
678 {
679 if ($reflectionMethod->isPrivate()) {
680 return PropertyReadInfo::VISIBILITY_PRIVATE;
681 }
682 if ($reflectionMethod->isProtected()) {
683 return PropertyReadInfo::VISIBILITY_PROTECTED;
684 }
685 return PropertyReadInfo::VISIBILITY_PUBLIC;
686 }
687 private function getWriteVisiblityForProperty(\ReflectionProperty $reflectionProperty): string
688 {
689 if ($reflectionProperty->isPrivate()) {
690 return PropertyWriteInfo::VISIBILITY_PRIVATE;
691 }
692 if ($reflectionProperty->isProtected()) {
693 return PropertyWriteInfo::VISIBILITY_PROTECTED;
694 }
695 return PropertyWriteInfo::VISIBILITY_PUBLIC;
696 }
697 private function getWriteVisiblityForMethod(\ReflectionMethod $reflectionMethod): string
698 {
699 if ($reflectionMethod->isPrivate()) {
700 return PropertyWriteInfo::VISIBILITY_PRIVATE;
701 }
702 if ($reflectionMethod->isProtected()) {
703 return PropertyWriteInfo::VISIBILITY_PROTECTED;
704 }
705 return PropertyWriteInfo::VISIBILITY_PUBLIC;
706 }
707 }
708