init
This commit is contained in:
39
backend/vendor/symfony/property-info/Extractor/ConstructorArgumentTypeExtractorInterface.php
vendored
Normal file
39
backend/vendor/symfony/property-info/Extractor/ConstructorArgumentTypeExtractorInterface.php
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\PropertyInfo\Extractor;
|
||||
|
||||
use Symfony\Component\PropertyInfo\Type as LegacyType;
|
||||
use Symfony\Component\TypeInfo\Type;
|
||||
|
||||
/**
|
||||
* Infers the constructor argument type.
|
||||
*
|
||||
* @author Dmitrii Poddubnyi <dpoddubny@gmail.com>
|
||||
*/
|
||||
interface ConstructorArgumentTypeExtractorInterface
|
||||
{
|
||||
/**
|
||||
* Gets types of an argument from constructor.
|
||||
*
|
||||
* @deprecated since Symfony 7.3, use "getTypeFromConstructor" instead
|
||||
*
|
||||
* @return LegacyType[]|null
|
||||
*/
|
||||
public function getTypesFromConstructor(string $class, string $property): ?array;
|
||||
|
||||
/**
|
||||
* Gets type of an argument from constructor.
|
||||
*
|
||||
* @param class-string $class
|
||||
*/
|
||||
public function getTypeFromConstructor(string $class, string $property): ?Type;
|
||||
}
|
||||
59
backend/vendor/symfony/property-info/Extractor/ConstructorExtractor.php
vendored
Normal file
59
backend/vendor/symfony/property-info/Extractor/ConstructorExtractor.php
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\PropertyInfo\Extractor;
|
||||
|
||||
use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
|
||||
use Symfony\Component\TypeInfo\Type;
|
||||
|
||||
/**
|
||||
* Extracts the constructor argument type using ConstructorArgumentTypeExtractorInterface implementations.
|
||||
*
|
||||
* @author Dmitrii Poddubnyi <dpoddubny@gmail.com>
|
||||
*/
|
||||
final class ConstructorExtractor implements PropertyTypeExtractorInterface
|
||||
{
|
||||
/**
|
||||
* @param iterable<int, ConstructorArgumentTypeExtractorInterface> $extractors
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly iterable $extractors = [],
|
||||
) {
|
||||
}
|
||||
|
||||
public function getType(string $class, string $property, array $context = []): ?Type
|
||||
{
|
||||
foreach ($this->extractors as $extractor) {
|
||||
if (null !== $value = $extractor->getTypeFromConstructor($class, $property)) {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since Symfony 7.3, use "getType" instead
|
||||
*/
|
||||
public function getTypes(string $class, string $property, array $context = []): ?array
|
||||
{
|
||||
trigger_deprecation('symfony/property-info', '7.3', 'The "%s()" method is deprecated, use "%s::getType()" instead.', __METHOD__, self::class);
|
||||
|
||||
foreach ($this->extractors as $extractor) {
|
||||
$value = $extractor->getTypesFromConstructor($class, $property);
|
||||
if (null !== $value) {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
541
backend/vendor/symfony/property-info/Extractor/PhpDocExtractor.php
vendored
Normal file
541
backend/vendor/symfony/property-info/Extractor/PhpDocExtractor.php
vendored
Normal file
@@ -0,0 +1,541 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\PropertyInfo\Extractor;
|
||||
|
||||
use phpDocumentor\Reflection\DocBlock;
|
||||
use phpDocumentor\Reflection\DocBlock\Tags\InvalidTag;
|
||||
use phpDocumentor\Reflection\DocBlockFactory;
|
||||
use phpDocumentor\Reflection\DocBlockFactoryInterface;
|
||||
use phpDocumentor\Reflection\Types\Context;
|
||||
use phpDocumentor\Reflection\Types\ContextFactory;
|
||||
use Symfony\Component\PropertyInfo\PropertyDescriptionExtractorInterface;
|
||||
use Symfony\Component\PropertyInfo\PropertyDocBlockExtractorInterface;
|
||||
use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
|
||||
use Symfony\Component\PropertyInfo\Type as LegacyType;
|
||||
use Symfony\Component\PropertyInfo\Util\PhpDocTypeHelper;
|
||||
use Symfony\Component\TypeInfo\Exception\LogicException;
|
||||
use Symfony\Component\TypeInfo\Type;
|
||||
use Symfony\Component\TypeInfo\Type\ObjectType;
|
||||
use Symfony\Component\TypeInfo\TypeContext\TypeContextFactory;
|
||||
|
||||
/**
|
||||
* Extracts data using a PHPDoc parser.
|
||||
*
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
class PhpDocExtractor implements PropertyDescriptionExtractorInterface, PropertyTypeExtractorInterface, ConstructorArgumentTypeExtractorInterface, PropertyDocBlockExtractorInterface
|
||||
{
|
||||
public const PROPERTY = 0;
|
||||
public const ACCESSOR = 1;
|
||||
public const MUTATOR = 2;
|
||||
|
||||
/**
|
||||
* @var array<string, array{DocBlock|null, int|null, string|null, string|null}>
|
||||
*/
|
||||
private array $docBlocks = [];
|
||||
|
||||
/**
|
||||
* @var array<string, array{DocBlock, string}|false>
|
||||
*/
|
||||
private array $promotedPropertyDocBlocks = [];
|
||||
|
||||
/**
|
||||
* @var Context[]
|
||||
*/
|
||||
private array $contexts = [];
|
||||
|
||||
private DocBlockFactoryInterface $docBlockFactory;
|
||||
private ContextFactory $contextFactory;
|
||||
private TypeContextFactory $typeContextFactory;
|
||||
private PhpDocTypeHelper $phpDocTypeHelper;
|
||||
private array $mutatorPrefixes;
|
||||
private array $accessorPrefixes;
|
||||
private array $arrayMutatorPrefixes;
|
||||
|
||||
/**
|
||||
* @param string[]|null $mutatorPrefixes
|
||||
* @param string[]|null $accessorPrefixes
|
||||
* @param string[]|null $arrayMutatorPrefixes
|
||||
*/
|
||||
public function __construct(?DocBlockFactoryInterface $docBlockFactory = null, ?array $mutatorPrefixes = null, ?array $accessorPrefixes = null, ?array $arrayMutatorPrefixes = null)
|
||||
{
|
||||
if (!class_exists(DocBlockFactory::class)) {
|
||||
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__));
|
||||
}
|
||||
|
||||
$this->docBlockFactory = $docBlockFactory ?: DocBlockFactory::createInstance();
|
||||
$this->contextFactory = new ContextFactory();
|
||||
$this->typeContextFactory = new TypeContextFactory();
|
||||
$this->phpDocTypeHelper = new PhpDocTypeHelper();
|
||||
$this->mutatorPrefixes = $mutatorPrefixes ?? ReflectionExtractor::$defaultMutatorPrefixes;
|
||||
$this->accessorPrefixes = $accessorPrefixes ?? ReflectionExtractor::$defaultAccessorPrefixes;
|
||||
$this->arrayMutatorPrefixes = $arrayMutatorPrefixes ?? ReflectionExtractor::$defaultArrayMutatorPrefixes;
|
||||
}
|
||||
|
||||
public function getShortDescription(string $class, string $property, array $context = []): ?string
|
||||
{
|
||||
$docBlockData = $this->getPromotedPropertyDocBlockData($class, $property);
|
||||
if ($docBlockData && $shortDescription = $this->getShortDescriptionFromDocBlock($docBlockData[0])) {
|
||||
return $shortDescription;
|
||||
}
|
||||
|
||||
[$docBlock] = $this->findDocBlock($class, $property);
|
||||
if (!$docBlock) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->getShortDescriptionFromDocBlock($docBlock);
|
||||
}
|
||||
|
||||
public function getLongDescription(string $class, string $property, array $context = []): ?string
|
||||
{
|
||||
$docBlockData = $this->getPromotedPropertyDocBlockData($class, $property);
|
||||
if ($docBlockData && '' !== $contents = $docBlockData[0]->getDescription()->render()) {
|
||||
return $contents;
|
||||
}
|
||||
|
||||
[$docBlock] = $this->findDocBlock($class, $property);
|
||||
if (!$docBlock) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$contents = $docBlock->getDescription()->render();
|
||||
|
||||
return '' === $contents ? null : $contents;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since Symfony 7.3, use "getType" instead
|
||||
*/
|
||||
public function getTypes(string $class, string $property, array $context = []): ?array
|
||||
{
|
||||
trigger_deprecation('symfony/property-info', '7.3', 'The "%s()" method is deprecated, use "%s::getType()" instead.', __METHOD__, self::class);
|
||||
|
||||
/** @var DocBlock $docBlock */
|
||||
[$docBlock, $source, $prefix, $declaringClass] = $this->findDocBlock($class, $property);
|
||||
if (!$docBlock) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$tag = match ($source) {
|
||||
self::PROPERTY => 'var',
|
||||
self::ACCESSOR => 'return',
|
||||
self::MUTATOR => 'param',
|
||||
};
|
||||
|
||||
$parentClass = null;
|
||||
$types = [];
|
||||
/** @var DocBlock\Tags\Var_|DocBlock\Tags\Return_|DocBlock\Tags\Param $tag */
|
||||
foreach ($docBlock->getTagsByName($tag) as $tag) {
|
||||
if ($tag && !$tag instanceof InvalidTag && null !== $tag->getType()) {
|
||||
foreach ($this->phpDocTypeHelper->getTypes($tag->getType()) as $type) {
|
||||
switch ($type->getClassName()) {
|
||||
case 'self':
|
||||
$resolvedClass = $declaringClass ?? $class;
|
||||
break;
|
||||
|
||||
case 'static':
|
||||
$resolvedClass = $class;
|
||||
break;
|
||||
|
||||
case 'parent':
|
||||
if (false !== $resolvedClass = $parentClass ??= get_parent_class($declaringClass ?? $class)) {
|
||||
break;
|
||||
}
|
||||
// no break
|
||||
|
||||
default:
|
||||
$types[] = $type;
|
||||
continue 2;
|
||||
}
|
||||
|
||||
$types[] = new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, $type->isNullable(), $resolvedClass, $type->isCollection(), $type->getCollectionKeyTypes(), $type->getCollectionValueTypes());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($types[0])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!\in_array($prefix, $this->arrayMutatorPrefixes, true)) {
|
||||
return $types;
|
||||
}
|
||||
|
||||
return [new LegacyType(LegacyType::BUILTIN_TYPE_ARRAY, false, null, true, new LegacyType(LegacyType::BUILTIN_TYPE_INT), $types[0])];
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since Symfony 7.3, use "getTypeFromConstructor" instead
|
||||
*/
|
||||
public function getTypesFromConstructor(string $class, string $property): ?array
|
||||
{
|
||||
trigger_deprecation('symfony/property-info', '7.3', 'The "%s()" method is deprecated, use "%s::getTypeFromConstructor()" instead.', __METHOD__, self::class);
|
||||
|
||||
$docBlock = $this->getDocBlockFromConstructor($class, $property);
|
||||
|
||||
if (!$docBlock) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$types = [];
|
||||
/** @var DocBlock\Tags\Var_|DocBlock\Tags\Return_|DocBlock\Tags\Param $tag */
|
||||
foreach ($docBlock->getTagsByName('param') as $tag) {
|
||||
if ($tag && null !== $tag->getType()) {
|
||||
$types[] = $this->phpDocTypeHelper->getTypes($tag->getType());
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($types[0]) || [] === $types[0]) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return array_merge([], ...$types);
|
||||
}
|
||||
|
||||
public function getType(string $class, string $property, array $context = []): ?Type
|
||||
{
|
||||
if ([$propertyDocBlock, $propertyDeclaringClass] = $this->getPromotedPropertyDocBlockData($class, $property)) {
|
||||
if ($type = $this->getTypeFromDocBlock($propertyDocBlock, self::PROPERTY, $class, $propertyDeclaringClass, null)) {
|
||||
return $type;
|
||||
}
|
||||
}
|
||||
|
||||
[$docBlock, $source, $prefix, $declaringClass] = $this->findDocBlock($class, $property);
|
||||
if (!$docBlock) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->getTypeFromDocBlock($docBlock, $source, $class, $declaringClass, $prefix);
|
||||
}
|
||||
|
||||
public function getTypeFromConstructor(string $class, string $property): ?Type
|
||||
{
|
||||
if (!$docBlock = $this->getDocBlockFromConstructor($class, $property)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$types = [];
|
||||
/** @var DocBlock\Tags\Var_|DocBlock\Tags\Return_|DocBlock\Tags\Param $tag */
|
||||
foreach ($docBlock->getTagsByName('param') as $tag) {
|
||||
if ($tag instanceof InvalidTag || !$tagType = $tag->getType()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$types[] = $this->phpDocTypeHelper->getType($tagType);
|
||||
}
|
||||
|
||||
return $types[0] ?? null;
|
||||
}
|
||||
|
||||
public function getDocBlock(string $class, string $property): ?DocBlock
|
||||
{
|
||||
return $this->findDocBlock($class, $property)[0];
|
||||
}
|
||||
|
||||
private function getDocBlockFromConstructor(string $class, string $property): ?DocBlock
|
||||
{
|
||||
try {
|
||||
$reflectionClass = new \ReflectionClass($class);
|
||||
} catch (\ReflectionException) {
|
||||
return null;
|
||||
}
|
||||
if (!$reflectionConstructor = $reflectionClass->getConstructor()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
$docBlock = $this->docBlockFactory->create($reflectionConstructor, $this->contextFactory->createFromReflector($reflectionConstructor));
|
||||
|
||||
return $this->filterDocBlockParams($docBlock, $property);
|
||||
} catch (\InvalidArgumentException) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private function filterDocBlockParams(DocBlock $docBlock, string $allowedParam): DocBlock
|
||||
{
|
||||
$tags = array_values(array_filter($docBlock->getTagsByName('param'), static fn ($tag) => $tag instanceof DocBlock\Tags\Param && $allowedParam === $tag->getVariableName()));
|
||||
|
||||
return new DocBlock($docBlock->getSummary(), $docBlock->getDescription(), $tags, $docBlock->getContext(),
|
||||
$docBlock->getLocation(), $docBlock->isTemplateStart(), $docBlock->isTemplateEnd());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{DocBlock|null, int|null, string|null, string|null}
|
||||
*/
|
||||
private function findDocBlock(string $class, string $property): array
|
||||
{
|
||||
$propertyHash = \sprintf('%s::%s', $class, $property);
|
||||
|
||||
if (isset($this->docBlocks[$propertyHash])) {
|
||||
return $this->docBlocks[$propertyHash];
|
||||
}
|
||||
|
||||
try {
|
||||
$reflectionProperty = new \ReflectionProperty($class, $property);
|
||||
} catch (\ReflectionException) {
|
||||
$reflectionProperty = null;
|
||||
}
|
||||
|
||||
$ucFirstProperty = ucfirst($property);
|
||||
|
||||
if ($reflectionProperty?->isPromoted() && $docBlock = $this->getDocBlockFromConstructor($reflectionProperty->class, $property)) {
|
||||
$data = [$docBlock, self::MUTATOR, null, $reflectionProperty->class];
|
||||
} elseif ([$docBlock, $declaringClass] = $this->getDocBlockFromProperty($class, $property)) {
|
||||
$data = [$docBlock, self::PROPERTY, null, $declaringClass];
|
||||
} else {
|
||||
$data = $this->getDocBlockFromMethod($class, $ucFirstProperty, self::ACCESSOR)
|
||||
?? $this->getDocBlockFromMethod($class, $ucFirstProperty, self::MUTATOR)
|
||||
?? [null, null, null, null];
|
||||
}
|
||||
|
||||
return $this->docBlocks[$propertyHash] = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{DocBlock, string}|null
|
||||
*/
|
||||
private function getDocBlockFromProperty(string $class, string $property, ?string $originalClass = null): ?array
|
||||
{
|
||||
$originalClass ??= $class;
|
||||
|
||||
// Use a ReflectionProperty instead of $class to get the parent class if applicable
|
||||
try {
|
||||
$reflectionProperty = new \ReflectionProperty($class, $property);
|
||||
} catch (\ReflectionException) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$reflector = $reflectionProperty->getDeclaringClass();
|
||||
|
||||
foreach ($reflector->getTraits() as $trait) {
|
||||
if ($trait->hasProperty($property)) {
|
||||
return $this->getDocBlockFromProperty($trait->getName(), $property, $reflector->isTrait() ? $originalClass : $reflector->getName());
|
||||
}
|
||||
}
|
||||
|
||||
$context = $this->createFromReflector($reflector);
|
||||
|
||||
try {
|
||||
$declaringClass = $reflector->isTrait() ? $originalClass : $reflector->getName();
|
||||
|
||||
return [$this->docBlockFactory->create($reflectionProperty, $context), $declaringClass];
|
||||
} catch (\InvalidArgumentException) {
|
||||
return null;
|
||||
} catch (\RuntimeException) {
|
||||
// Workaround for phpdocumentor/reflection-docblock < 6 not supporting ?Type<...> syntax
|
||||
if (($rawDoc = $reflectionProperty->getDocComment()) && $docBlock = $this->getNullableGenericDocBlock($rawDoc, $context)) {
|
||||
return [$docBlock, $declaringClass ?? ($reflector->isTrait() ? $originalClass : $reflector->getName())];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{DocBlock, int, ?string, string}|null
|
||||
*/
|
||||
private function getDocBlockFromMethod(string $class, string $ucFirstProperty, int $type, ?string $originalClass = null): ?array
|
||||
{
|
||||
$originalClass ??= $class;
|
||||
$prefixes = self::ACCESSOR === $type ? $this->accessorPrefixes : $this->mutatorPrefixes;
|
||||
$prefix = null;
|
||||
$method = null;
|
||||
|
||||
foreach ($prefixes as $prefix) {
|
||||
$methodName = $prefix.$ucFirstProperty;
|
||||
|
||||
try {
|
||||
$method = new \ReflectionMethod($class, $methodName);
|
||||
if ($method->isStatic()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (self::ACCESSOR === $type && \in_array((string) $method->getReturnType(), ['void', 'never'], true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
(self::ACCESSOR === $type && !$method->getNumberOfRequiredParameters())
|
||||
|| (self::MUTATOR === $type && $method->getNumberOfParameters() >= 1)
|
||||
) {
|
||||
break;
|
||||
}
|
||||
} catch (\ReflectionException) {
|
||||
// Try the next prefix if the method doesn't exist
|
||||
}
|
||||
}
|
||||
|
||||
if (!$method) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$reflector = $method->getDeclaringClass();
|
||||
|
||||
foreach ($reflector->getTraits() as $trait) {
|
||||
if ($trait->hasMethod($methodName)) {
|
||||
return $this->getDocBlockFromMethod($trait->getName(), $ucFirstProperty, $type, $reflector->isTrait() ? $originalClass : $reflector->getName());
|
||||
}
|
||||
}
|
||||
|
||||
$context = $this->createFromReflector($reflector);
|
||||
$prefix = self::ACCESSOR === $type ? null : $prefix;
|
||||
|
||||
try {
|
||||
$declaringClass = $reflector->isTrait() ? $originalClass : $reflector->getName();
|
||||
|
||||
return [$this->docBlockFactory->create($method, $context), $type, $prefix, $declaringClass];
|
||||
} catch (\InvalidArgumentException) {
|
||||
return null;
|
||||
} catch (\RuntimeException) {
|
||||
// Workaround for phpdocumentor/reflection-docblock < 6 not supporting ?Type<...> syntax
|
||||
if (($rawDoc = $method->getDocComment()) && $docBlock = $this->getNullableGenericDocBlock($rawDoc, $context)) {
|
||||
return [$docBlock, $type, $prefix, $declaringClass ?? ($reflector->isTrait() ? $originalClass : $reflector->getName())];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private function getNullableGenericDocBlock(string $rawDoc, Context $context): ?DocBlock
|
||||
{
|
||||
// Converts "?Type<...>" to "Type<...>|null"
|
||||
if ($rawDoc === $processedDoc = preg_replace('/@(var|param|return)\s+\?(\S+)/', '@$1 $2|null', $rawDoc)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return $this->docBlockFactory->create($processedDoc, $context);
|
||||
} catch (\InvalidArgumentException|\RuntimeException) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevents a lot of redundant calls to ContextFactory::createForNamespace().
|
||||
*/
|
||||
private function createFromReflector(\ReflectionClass $reflector): Context
|
||||
{
|
||||
$cacheKey = $reflector->getNamespaceName().':'.$reflector->getFileName();
|
||||
|
||||
return $this->contexts[$cacheKey] ??= $this->contextFactory->createFromReflector($reflector);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{DocBlock, string}|null
|
||||
*/
|
||||
private function getPromotedPropertyDocBlockData(string $class, string $property): ?array
|
||||
{
|
||||
$propertyHash = $class.'::'.$property;
|
||||
|
||||
if (isset($this->promotedPropertyDocBlocks[$propertyHash])) {
|
||||
return false === $this->promotedPropertyDocBlocks[$propertyHash] ? null : $this->promotedPropertyDocBlocks[$propertyHash];
|
||||
}
|
||||
|
||||
try {
|
||||
$reflectionProperty = new \ReflectionProperty($class, $property);
|
||||
} catch (\ReflectionException) {
|
||||
$this->promotedPropertyDocBlocks[$propertyHash] = false;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!$reflectionProperty->isPromoted() || !$data = $this->getDocBlockFromProperty($class, $property)) {
|
||||
$this->promotedPropertyDocBlocks[$propertyHash] = false;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->promotedPropertyDocBlocks[$propertyHash] = $data;
|
||||
}
|
||||
|
||||
private function getTypeFromDocBlock(DocBlock $docBlock, int $source, string $class, ?string $declaringClass, ?string $prefix): ?Type
|
||||
{
|
||||
$tag = match ($source) {
|
||||
self::PROPERTY => 'var',
|
||||
self::ACCESSOR => 'return',
|
||||
self::MUTATOR => 'param',
|
||||
};
|
||||
|
||||
$types = [];
|
||||
$typeContext = $this->typeContextFactory->createFromClassName($class, $declaringClass ?? $class);
|
||||
|
||||
/** @var DocBlock\Tags\Var_|DocBlock\Tags\Return_|DocBlock\Tags\Param $tag */
|
||||
foreach ($docBlock->getTagsByName($tag) as $tag) {
|
||||
if ($tag instanceof InvalidTag || !$tagType = $tag->getType()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$type = $this->phpDocTypeHelper->getType($tagType);
|
||||
|
||||
if (!$type instanceof ObjectType) {
|
||||
$types[] = $type;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$normalizedClassName = match ($type->getClassName()) {
|
||||
'self' => $typeContext->getDeclaringClass(),
|
||||
'static' => $typeContext->getCalledClass(),
|
||||
default => $type->getClassName(),
|
||||
};
|
||||
|
||||
if ('parent' === $normalizedClassName) {
|
||||
try {
|
||||
$normalizedClassName = $typeContext->getParentClass();
|
||||
} catch (LogicException) {
|
||||
// if there is no parent for the current class, we keep the "parent" raw string
|
||||
}
|
||||
}
|
||||
|
||||
$types[] = $type->isNullable() ? Type::nullable(Type::object($normalizedClassName)) : Type::object($normalizedClassName);
|
||||
}
|
||||
|
||||
if (!$type = $types[0] ?? null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (self::MUTATOR !== $source || !\in_array($prefix, $this->arrayMutatorPrefixes, true)) {
|
||||
return $type;
|
||||
}
|
||||
|
||||
return Type::list($type);
|
||||
}
|
||||
|
||||
private function getShortDescriptionFromDocBlock(DocBlock $docBlock): ?string
|
||||
{
|
||||
if ($shortDescription = $docBlock->getSummary()) {
|
||||
return $shortDescription;
|
||||
}
|
||||
|
||||
foreach ($docBlock->getTagsByName('var') as $var) {
|
||||
if ($var && !$var instanceof InvalidTag && $varDescription = $var->getDescription()->render()) {
|
||||
return $varDescription;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($docBlock->getTagsByName('param') as $param) {
|
||||
if (!$param instanceof DocBlock\Tags\Param) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($paramDescription = $param->getDescription()?->render()) {
|
||||
return $paramDescription;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
585
backend/vendor/symfony/property-info/Extractor/PhpStanExtractor.php
vendored
Normal file
585
backend/vendor/symfony/property-info/Extractor/PhpStanExtractor.php
vendored
Normal file
@@ -0,0 +1,585 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\PropertyInfo\Extractor;
|
||||
|
||||
use phpDocumentor\Reflection\Types\ContextFactory;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\InvalidTagValueNode;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocChildNode;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocNode;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTextNode;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode;
|
||||
use PHPStan\PhpDocParser\Lexer\Lexer;
|
||||
use PHPStan\PhpDocParser\Parser\ConstExprParser;
|
||||
use PHPStan\PhpDocParser\Parser\PhpDocParser;
|
||||
use PHPStan\PhpDocParser\Parser\TokenIterator;
|
||||
use PHPStan\PhpDocParser\Parser\TypeParser;
|
||||
use PHPStan\PhpDocParser\ParserConfig;
|
||||
use Symfony\Component\PropertyInfo\PropertyDescriptionExtractorInterface;
|
||||
use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
|
||||
use Symfony\Component\PropertyInfo\Type as LegacyType;
|
||||
use Symfony\Component\PropertyInfo\Util\PhpStanTypeHelper;
|
||||
use Symfony\Component\TypeInfo\Exception\UnsupportedException;
|
||||
use Symfony\Component\TypeInfo\Type;
|
||||
use Symfony\Component\TypeInfo\TypeContext\TypeContext;
|
||||
use Symfony\Component\TypeInfo\TypeContext\TypeContextFactory;
|
||||
use Symfony\Component\TypeInfo\TypeResolver\StringTypeResolver;
|
||||
|
||||
/**
|
||||
* Extracts data using PHPStan parser.
|
||||
*
|
||||
* @author Baptiste Leduc <baptiste.leduc@gmail.com>
|
||||
*/
|
||||
final class PhpStanExtractor implements PropertyDescriptionExtractorInterface, PropertyTypeExtractorInterface, ConstructorArgumentTypeExtractorInterface
|
||||
{
|
||||
private const PROPERTY = 0;
|
||||
private const ACCESSOR = 1;
|
||||
private const MUTATOR = 2;
|
||||
|
||||
private PhpDocParser $phpDocParser;
|
||||
private Lexer $lexer;
|
||||
|
||||
private StringTypeResolver $stringTypeResolver;
|
||||
private TypeContextFactory $typeContextFactory;
|
||||
|
||||
/** @var array<string, array{PhpDocNode|null, int|null, string|null, string|null}> */
|
||||
private array $docBlocks = [];
|
||||
private PhpStanTypeHelper $phpStanTypeHelper;
|
||||
private array $mutatorPrefixes;
|
||||
private array $accessorPrefixes;
|
||||
private array $arrayMutatorPrefixes;
|
||||
|
||||
/** @var array<string, TypeContext> */
|
||||
private array $contexts = [];
|
||||
|
||||
/**
|
||||
* @param list<string>|null $mutatorPrefixes
|
||||
* @param list<string>|null $accessorPrefixes
|
||||
* @param list<string>|null $arrayMutatorPrefixes
|
||||
*/
|
||||
public function __construct(?array $mutatorPrefixes = null, ?array $accessorPrefixes = null, ?array $arrayMutatorPrefixes = null, private bool $allowPrivateAccess = true)
|
||||
{
|
||||
if (!class_exists(ContextFactory::class)) {
|
||||
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__));
|
||||
}
|
||||
|
||||
if (!class_exists(PhpDocParser::class)) {
|
||||
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__));
|
||||
}
|
||||
|
||||
$this->phpStanTypeHelper = new PhpStanTypeHelper();
|
||||
$this->mutatorPrefixes = $mutatorPrefixes ?? ReflectionExtractor::$defaultMutatorPrefixes;
|
||||
$this->accessorPrefixes = $accessorPrefixes ?? ReflectionExtractor::$defaultAccessorPrefixes;
|
||||
$this->arrayMutatorPrefixes = $arrayMutatorPrefixes ?? ReflectionExtractor::$defaultArrayMutatorPrefixes;
|
||||
|
||||
if (class_exists(ParserConfig::class)) {
|
||||
$parserConfig = new ParserConfig([]);
|
||||
$this->phpDocParser = new PhpDocParser($parserConfig, new TypeParser($parserConfig, new ConstExprParser($parserConfig)), new ConstExprParser($parserConfig));
|
||||
$this->lexer = new Lexer($parserConfig);
|
||||
} else {
|
||||
$this->phpDocParser = new PhpDocParser(new TypeParser(new ConstExprParser()), new ConstExprParser());
|
||||
$this->lexer = new Lexer();
|
||||
}
|
||||
$this->stringTypeResolver = new StringTypeResolver();
|
||||
$this->typeContextFactory = new TypeContextFactory($this->stringTypeResolver);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since Symfony 7.3, use "getType" instead
|
||||
*/
|
||||
public function getTypes(string $class, string $property, array $context = []): ?array
|
||||
{
|
||||
trigger_deprecation('symfony/property-info', '7.3', 'The "%s()" method is deprecated, use "%s::getType()" instead.', __METHOD__, self::class);
|
||||
|
||||
/** @var PhpDocNode|null $docNode */
|
||||
[$docNode, $source, $prefix, $declaringClass] = $this->getDocBlock($class, $property);
|
||||
if (null === $docNode) {
|
||||
return null;
|
||||
}
|
||||
|
||||
switch ($source) {
|
||||
case self::PROPERTY:
|
||||
$tag = '@var';
|
||||
break;
|
||||
|
||||
case self::ACCESSOR:
|
||||
$tag = '@return';
|
||||
break;
|
||||
|
||||
case self::MUTATOR:
|
||||
$tag = '@param';
|
||||
break;
|
||||
}
|
||||
|
||||
$parentClass = null;
|
||||
$types = [];
|
||||
foreach ($docNode->getTagsByName($tag) as $tagDocNode) {
|
||||
if ($tagDocNode->value instanceof InvalidTagValueNode) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
$tagDocNode->value instanceof ParamTagValueNode
|
||||
&& null === $prefix
|
||||
&& $tagDocNode->value->parameterName !== '$'.$property
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$typeContext = $this->contexts[$class.'/'.$declaringClass] ??= $this->typeContextFactory->createFromClassName($class, $declaringClass);
|
||||
|
||||
foreach ($this->phpStanTypeHelper->getTypes($tagDocNode->value, $typeContext) as $type) {
|
||||
switch ($type->getClassName()) {
|
||||
case 'self':
|
||||
case 'static':
|
||||
$resolvedClass = $class;
|
||||
break;
|
||||
|
||||
case 'parent':
|
||||
if (false !== $resolvedClass = $parentClass ??= get_parent_class($class)) {
|
||||
break;
|
||||
}
|
||||
// no break
|
||||
|
||||
default:
|
||||
$types[] = $type;
|
||||
continue 2;
|
||||
}
|
||||
|
||||
$types[] = new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, $type->isNullable(), $resolvedClass, $type->isCollection(), $type->getCollectionKeyTypes(), $type->getCollectionValueTypes());
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($types[0])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!\in_array($prefix, $this->arrayMutatorPrefixes, true)) {
|
||||
return $types;
|
||||
}
|
||||
|
||||
return [new LegacyType(LegacyType::BUILTIN_TYPE_ARRAY, false, null, true, new LegacyType(LegacyType::BUILTIN_TYPE_INT), $types[0])];
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since Symfony 7.3, use "getTypeFromConstructor" instead
|
||||
*
|
||||
* @return LegacyType[]|null
|
||||
*/
|
||||
public function getTypesFromConstructor(string $class, string $property): ?array
|
||||
{
|
||||
trigger_deprecation('symfony/property-info', '7.3', 'The "%s()" method is deprecated, use "%s::getTypeFromConstructor()" instead.', __METHOD__, self::class);
|
||||
|
||||
if (null === $tagDocNode = $this->getDocBlockFromConstructor($class, $property)) {
|
||||
return $this->getTypes($class, $property);
|
||||
}
|
||||
|
||||
$types = [];
|
||||
foreach ($this->phpStanTypeHelper->getTypes($tagDocNode, $this->typeContextFactory->createFromClassName($class)) as $type) {
|
||||
$types[] = $type;
|
||||
}
|
||||
|
||||
if (!isset($types[0])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $types;
|
||||
}
|
||||
|
||||
public function getType(string $class, string $property, array $context = []): ?Type
|
||||
{
|
||||
/** @var PhpDocNode|null $docNode */
|
||||
[$docNode, $source, $prefix, $declaringClass] = $this->getDocBlock($class, $property);
|
||||
|
||||
if (null === $docNode) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$typeContext = $this->typeContextFactory->createFromClassName($class, $declaringClass);
|
||||
|
||||
$tag = match ($source) {
|
||||
self::PROPERTY => '@var',
|
||||
self::ACCESSOR => '@return',
|
||||
self::MUTATOR => '@param',
|
||||
default => 'invalid',
|
||||
};
|
||||
|
||||
$types = [];
|
||||
|
||||
foreach ($docNode->getTagsByName($tag) as $tagDocNode) {
|
||||
if (!$tagDocNode->value instanceof ParamTagValueNode && !$tagDocNode->value instanceof ReturnTagValueNode && !$tagDocNode->value instanceof VarTagValueNode) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($tagDocNode->value instanceof ParamTagValueNode && null === $prefix && $tagDocNode->value->parameterName !== '$'.$property) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
$types[] = $this->stringTypeResolver->resolve((string) $tagDocNode->value->type, $typeContext);
|
||||
} catch (UnsupportedException) {
|
||||
}
|
||||
}
|
||||
|
||||
if (!$type = $types[0] ?? null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!\in_array($prefix, $this->arrayMutatorPrefixes, true)) {
|
||||
return $type;
|
||||
}
|
||||
|
||||
return Type::list($type);
|
||||
}
|
||||
|
||||
public function getTypeFromConstructor(string $class, string $property): ?Type
|
||||
{
|
||||
$declaringClass = $class;
|
||||
if (!$tagDocNode = $this->getDocBlockFromConstructor($declaringClass, $property)) {
|
||||
return $this->getType($class, $property);
|
||||
}
|
||||
|
||||
$typeContext = $this->typeContextFactory->createFromClassName($class, $declaringClass);
|
||||
|
||||
return $this->stringTypeResolver->resolve((string) $tagDocNode->type, $typeContext);
|
||||
}
|
||||
|
||||
public function getShortDescription(string $class, string $property, array $context = []): ?string
|
||||
{
|
||||
/** @var PhpDocNode|null $docNode */
|
||||
[$docNode, $constructorDocNode] = $this->getDocBlockFromProperty($class, $property);
|
||||
if (null === $docNode && null === $constructorDocNode) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($docNode && $shortDescription = $this->getShortDescriptionFromDocNode($docNode, $property)) {
|
||||
return $shortDescription;
|
||||
}
|
||||
|
||||
if ($constructorDocNode) {
|
||||
return $this->getShortDescriptionFromDocNode($constructorDocNode, $property);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getLongDescription(string $class, string $property, array $context = []): ?string
|
||||
{
|
||||
/** @var PhpDocNode|null $docNode */
|
||||
[$docNode, $constructorDocNode] = $this->getDocBlockFromProperty($class, $property);
|
||||
if (null === $docNode && null === $constructorDocNode) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($docNode && $longDescription = $this->getDescriptionsFromDocNode($docNode)[1]) {
|
||||
return $longDescription;
|
||||
}
|
||||
|
||||
return $constructorDocNode ? $this->getDescriptionsFromDocNode($constructorDocNode)[1] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* A docblock is split into a template marker, a short description, an optional long description and a tags section.
|
||||
*
|
||||
* - The template marker is either empty, #@+ or #@-.
|
||||
* - The short description is started from a non-tag character, and until one or multiple newlines.
|
||||
* - The long description (optional) is started from a non-tag character, and until a new line is encountered followed by a tag.
|
||||
* - Tags, and the remaining characters
|
||||
*
|
||||
* This method returns the short and the long descriptions.
|
||||
*
|
||||
* @return array{0: ?string, 1: ?string}
|
||||
*/
|
||||
private function getDescriptionsFromDocNode(PhpDocNode $docNode): array
|
||||
{
|
||||
$isTemplateMarker = static fn (PhpDocChildNode $node): bool => $node instanceof PhpDocTextNode && ('#@+' === $node->text || '#@-' === $node->text);
|
||||
|
||||
$shortDescription = '';
|
||||
$longDescription = '';
|
||||
$shortDescriptionCompleted = false;
|
||||
|
||||
// BC layer for phpstan/phpdoc-parser < 2.0
|
||||
if (!class_exists(ParserConfig::class)) {
|
||||
$isNewLine = static fn (PhpDocChildNode $node): bool => $node instanceof PhpDocTextNode && '' === $node->text;
|
||||
|
||||
foreach ($docNode->children as $child) {
|
||||
if (!$child instanceof PhpDocTextNode) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ($isTemplateMarker($child)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($isNewLine($child) && !$shortDescriptionCompleted) {
|
||||
if ($shortDescription) {
|
||||
$shortDescriptionCompleted = true;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$shortDescriptionCompleted) {
|
||||
$shortDescription = \sprintf("%s\n%s", $shortDescription, $child->text);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$longDescription = \sprintf("%s\n%s", $longDescription, $child->text);
|
||||
}
|
||||
} else {
|
||||
foreach ($docNode->children as $child) {
|
||||
if (!$child instanceof PhpDocTextNode) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ($isTemplateMarker($child)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (explode("\n", $child->text) as $line) {
|
||||
if ('' === $line && !$shortDescriptionCompleted) {
|
||||
if ($shortDescription) {
|
||||
$shortDescriptionCompleted = true;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$shortDescriptionCompleted) {
|
||||
$shortDescription = \sprintf("%s\n%s", $shortDescription, $line);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$longDescription = \sprintf("%s\n%s", $longDescription, $line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$shortDescription = trim(preg_replace('/^#@[+-]{1}/m', '', $shortDescription), "\n");
|
||||
$longDescription = trim($longDescription, "\n");
|
||||
|
||||
return [
|
||||
$shortDescription ?: null,
|
||||
$longDescription ?: null,
|
||||
];
|
||||
}
|
||||
|
||||
private function getShortDescriptionFromDocNode(PhpDocNode $docNode, string $property): ?string
|
||||
{
|
||||
if ($shortDescription = $this->getDescriptionsFromDocNode($docNode)[0]) {
|
||||
return $shortDescription;
|
||||
}
|
||||
|
||||
foreach ($docNode->getVarTagValues() as $var) {
|
||||
if (!$var->description) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (null !== $var->variableName && '' !== $var->variableName && '$'.$property !== $var->variableName) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return $var->description;
|
||||
}
|
||||
|
||||
foreach ($docNode->getTagsByName('@param') as $tagNode) {
|
||||
if (!$tagNode instanceof PhpDocTagNode || !$tagNode->value instanceof ParamTagValueNode) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ('$'.$property !== $tagNode->value->parameterName) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($tagNode->value->description) {
|
||||
return $tagNode->value->description;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function getDocBlockFromConstructor(string &$class, string $property): ?ParamTagValueNode
|
||||
{
|
||||
try {
|
||||
$reflectionClass = new \ReflectionClass($class);
|
||||
} catch (\ReflectionException) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (null === $reflectionConstructor = $reflectionClass->getConstructor()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!$rawDocNode = $reflectionConstructor->getDocComment()) {
|
||||
return null;
|
||||
}
|
||||
$class = $reflectionConstructor->class;
|
||||
|
||||
$phpDocNode = $this->getPhpDocNode($rawDocNode);
|
||||
|
||||
return $this->filterDocBlockParams($phpDocNode, $property);
|
||||
}
|
||||
|
||||
private function filterDocBlockParams(PhpDocNode $docNode, string $allowedParam): ?ParamTagValueNode
|
||||
{
|
||||
$tags = array_values(array_filter($docNode->getTagsByName('@param'), fn ($tagNode) => $tagNode instanceof PhpDocTagNode && ('$'.$allowedParam) === $tagNode->value->parameterName));
|
||||
|
||||
if (!$tags) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $tags[0]->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{PhpDocNode|null, int|null, string|null, string|null}
|
||||
*/
|
||||
private function getDocBlock(string $class, string $property): array
|
||||
{
|
||||
$propertyHash = $class.'::'.$property;
|
||||
|
||||
if (isset($this->docBlocks[$propertyHash])) {
|
||||
return $this->docBlocks[$propertyHash];
|
||||
}
|
||||
|
||||
$ucFirstProperty = ucfirst($property);
|
||||
|
||||
if ([$docBlock, $constructorDocBlock, $source, $declaringClass] = $this->getDocBlockFromProperty($class, $property)) {
|
||||
if (!$docBlock?->getTagsByName('@var') && $constructorDocBlock) {
|
||||
$docBlock = $constructorDocBlock;
|
||||
}
|
||||
|
||||
$data = [$docBlock, $source, null, $declaringClass];
|
||||
} elseif ([$docBlock, $_, $declaringClass] = $this->getDocBlockFromMethod($class, $ucFirstProperty, self::ACCESSOR)) {
|
||||
$data = [$docBlock, self::ACCESSOR, null, $declaringClass];
|
||||
} elseif ([$docBlock, $prefix, $declaringClass] = $this->getDocBlockFromMethod($class, $ucFirstProperty, self::MUTATOR)) {
|
||||
$data = [$docBlock, self::MUTATOR, $prefix, $declaringClass];
|
||||
} else {
|
||||
$data = [null, null, null, null];
|
||||
}
|
||||
|
||||
return $this->docBlocks[$propertyHash] = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{?PhpDocNode, ?PhpDocNode, int, string}|null
|
||||
*/
|
||||
private function getDocBlockFromProperty(string $class, string $property): ?array
|
||||
{
|
||||
// Use a ReflectionProperty instead of $class to get the parent class if applicable
|
||||
try {
|
||||
$reflectionProperty = new \ReflectionProperty($class, $property);
|
||||
} catch (\ReflectionException) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!$this->canAccessMemberBasedOnItsVisibility($reflectionProperty)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$reflector = $reflectionProperty->getDeclaringClass();
|
||||
|
||||
foreach ($reflector->getTraits() as $trait) {
|
||||
if ($trait->hasProperty($property)) {
|
||||
return $this->getDocBlockFromProperty($trait->getName(), $property);
|
||||
}
|
||||
}
|
||||
|
||||
$rawDocNode = $reflectionProperty->getDocComment();
|
||||
$phpDocNode = $rawDocNode ? $this->getPhpDocNode($rawDocNode) : null;
|
||||
|
||||
$constructorPhpDocNode = null;
|
||||
if ($reflectionProperty->isPromoted()) {
|
||||
$constructorRawDocNode = (new \ReflectionMethod($class, '__construct'))->getDocComment();
|
||||
$constructorPhpDocNode = $constructorRawDocNode ? $this->getPhpDocNode($constructorRawDocNode) : null;
|
||||
}
|
||||
|
||||
$source = self::PROPERTY;
|
||||
if (!$phpDocNode?->getTagsByName('@var') && $constructorPhpDocNode) {
|
||||
$source = self::MUTATOR;
|
||||
}
|
||||
|
||||
if (!$phpDocNode && !$constructorPhpDocNode) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [$phpDocNode, $constructorPhpDocNode, $source, $reflectionProperty->class];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{PhpDocNode, string, string}|null
|
||||
*/
|
||||
private function getDocBlockFromMethod(string $class, string $ucFirstProperty, int $type): ?array
|
||||
{
|
||||
$prefixes = self::ACCESSOR === $type ? $this->accessorPrefixes : $this->mutatorPrefixes;
|
||||
$prefix = null;
|
||||
$method = null;
|
||||
|
||||
foreach ($prefixes as $prefix) {
|
||||
$methodName = $prefix.$ucFirstProperty;
|
||||
|
||||
try {
|
||||
$method = new \ReflectionMethod($class, $methodName);
|
||||
if ($method->isStatic()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (self::ACCESSOR === $type && \in_array((string) $method->getReturnType(), ['void', 'never'], true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
(
|
||||
(self::ACCESSOR === $type && !$method->getNumberOfRequiredParameters())
|
||||
|| (self::MUTATOR === $type && $method->getNumberOfParameters() >= 1)
|
||||
)
|
||||
&& $this->canAccessMemberBasedOnItsVisibility($method)
|
||||
) {
|
||||
break;
|
||||
}
|
||||
} catch (\ReflectionException) {
|
||||
// Try the next prefix if the method doesn't exist
|
||||
}
|
||||
}
|
||||
|
||||
if (!$method) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (null === $rawDocNode = $method->getDocComment() ?: null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$phpDocNode = $this->getPhpDocNode($rawDocNode);
|
||||
|
||||
return [$phpDocNode, $prefix, $method->class];
|
||||
}
|
||||
|
||||
private function getPhpDocNode(string $rawDocNode): PhpDocNode
|
||||
{
|
||||
$tokens = new TokenIterator($this->lexer->tokenize($rawDocNode));
|
||||
$phpDocNode = $this->phpDocParser->parse($tokens);
|
||||
$tokens->consumeTokenType(Lexer::TOKEN_END);
|
||||
|
||||
return $phpDocNode;
|
||||
}
|
||||
|
||||
private function canAccessMemberBasedOnItsVisibility(\ReflectionProperty|\ReflectionMethod $member): bool
|
||||
{
|
||||
return $this->allowPrivateAccess || $member->isPublic();
|
||||
}
|
||||
}
|
||||
1064
backend/vendor/symfony/property-info/Extractor/ReflectionExtractor.php
vendored
Normal file
1064
backend/vendor/symfony/property-info/Extractor/ReflectionExtractor.php
vendored
Normal file
File diff suppressed because it is too large
Load Diff
52
backend/vendor/symfony/property-info/Extractor/SerializerExtractor.php
vendored
Normal file
52
backend/vendor/symfony/property-info/Extractor/SerializerExtractor.php
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\PropertyInfo\Extractor;
|
||||
|
||||
use Symfony\Component\PropertyInfo\PropertyListExtractorInterface;
|
||||
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface;
|
||||
|
||||
/**
|
||||
* Lists available properties using Symfony Serializer Component metadata.
|
||||
*
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
class SerializerExtractor implements PropertyListExtractorInterface
|
||||
{
|
||||
public function __construct(
|
||||
private readonly ClassMetadataFactoryInterface $classMetadataFactory,
|
||||
) {
|
||||
}
|
||||
|
||||
public function getProperties(string $class, array $context = []): ?array
|
||||
{
|
||||
if (!\array_key_exists('serializer_groups', $context) || (null !== $context['serializer_groups'] && !\is_array($context['serializer_groups']))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!$this->classMetadataFactory->hasMetadataFor($class)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$properties = [];
|
||||
$serializerClassMetadata = $this->classMetadataFactory->getMetadataFor($class);
|
||||
|
||||
foreach ($serializerClassMetadata->getAttributesMetadata() as $serializerAttributeMetadata) {
|
||||
if (!$serializerAttributeMetadata->isIgnored() && (null === $context['serializer_groups'] || \in_array('*', $context['serializer_groups'], true) || array_intersect($serializerAttributeMetadata->getGroups(), $context['serializer_groups']))) {
|
||||
$properties[] = $serializerAttributeMetadata->getName();
|
||||
}
|
||||
}
|
||||
|
||||
return $properties;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user