init
This commit is contained in:
73
backend/vendor/symfony/property-info/CHANGELOG.md
vendored
Normal file
73
backend/vendor/symfony/property-info/CHANGELOG.md
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
CHANGELOG
|
||||
=========
|
||||
|
||||
7.3
|
||||
---
|
||||
|
||||
* Add support for `non-positive-int`, `non-negative-int` and `non-zero-int` PHPStan types to `PhpStanExtractor`
|
||||
* Add `PropertyDescriptionExtractorInterface` to `PhpStanExtractor`
|
||||
* Deprecate the `Type` class, use `Symfony\Component\TypeInfo\Type` class from `symfony/type-info` instead
|
||||
* Deprecate the `PropertyTypeExtractorInterface::getTypes()` method, use `PropertyTypeExtractorInterface::getType()` instead
|
||||
* Deprecate the `ConstructorArgumentTypeExtractorInterface::getTypesFromConstructor()` method, use `ConstructorArgumentTypeExtractorInterface::getTypeFromConstructor()` instead
|
||||
|
||||
7.1
|
||||
---
|
||||
|
||||
* Introduce `PropertyDocBlockExtractorInterface` to extract a property's doc block
|
||||
* Restrict access to `PhpStanExtractor` based on visibility
|
||||
* Add `PropertyTypeExtractorInterface::getType()` as experimental
|
||||
|
||||
6.4
|
||||
---
|
||||
|
||||
* Make properties writable when a setter in camelCase exists, similar to the camelCase getter
|
||||
|
||||
6.1
|
||||
---
|
||||
|
||||
* Add support for phpDocumentor and PHPStan pseudo-types
|
||||
* Add PHP 8.0 promoted properties `@param` mutation support to `PhpDocExtractor`
|
||||
* Add PHP 8.0 promoted properties `@param` mutation support to `PhpStanExtractor`
|
||||
|
||||
6.0
|
||||
---
|
||||
|
||||
* Remove the `Type::getCollectionKeyType()` and `Type::getCollectionValueType()` methods, use `Type::getCollectionKeyTypes()` and `Type::getCollectionValueTypes()` instead
|
||||
* Remove the `enable_magic_call_extraction` context option in `ReflectionExtractor::getWriteInfo()` and `ReflectionExtractor::getReadInfo()` in favor of `enable_magic_methods_extraction`
|
||||
|
||||
5.4
|
||||
---
|
||||
|
||||
* Add PhpStanExtractor
|
||||
|
||||
5.3
|
||||
---
|
||||
|
||||
* Add support for multiple types for collection keys & values
|
||||
* Deprecate the `Type::getCollectionKeyType()` and `Type::getCollectionValueType()` methods, use `Type::getCollectionKeyTypes()` and `Type::getCollectionValueTypes()` instead
|
||||
|
||||
5.2.0
|
||||
-----
|
||||
|
||||
* deprecated the `enable_magic_call_extraction` context option in `ReflectionExtractor::getWriteInfo()` and `ReflectionExtractor::getReadInfo()` in favor of `enable_magic_methods_extraction`
|
||||
|
||||
5.1.0
|
||||
-----
|
||||
|
||||
* Add support for extracting accessor and mutator via PHP Reflection
|
||||
|
||||
4.3.0
|
||||
-----
|
||||
|
||||
* Added the ability to extract private and protected properties and methods on `ReflectionExtractor`
|
||||
* Added the ability to extract property type based on its initial value
|
||||
|
||||
4.2.0
|
||||
-----
|
||||
|
||||
* added `PropertyInitializableExtractorInterface` to test if a property can be initialized through the constructor (implemented by `ReflectionExtractor`)
|
||||
|
||||
3.3.0
|
||||
-----
|
||||
|
||||
* Added `PropertyInfoPass`
|
||||
38
backend/vendor/symfony/property-info/DependencyInjection/PropertyInfoConstructorPass.php
vendored
Normal file
38
backend/vendor/symfony/property-info/DependencyInjection/PropertyInfoConstructorPass.php
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
<?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\DependencyInjection;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use Symfony\Component\DependencyInjection\Compiler\PriorityTaggedServiceTrait;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
|
||||
/**
|
||||
* Adds extractors to the property_info.constructor_extractor service.
|
||||
*
|
||||
* @author Dmitrii Poddubnyi <dpoddubny@gmail.com>
|
||||
*/
|
||||
final class PropertyInfoConstructorPass implements CompilerPassInterface
|
||||
{
|
||||
use PriorityTaggedServiceTrait;
|
||||
|
||||
public function process(ContainerBuilder $container): void
|
||||
{
|
||||
if (!$container->hasDefinition('property_info.constructor_extractor')) {
|
||||
return;
|
||||
}
|
||||
$definition = $container->getDefinition('property_info.constructor_extractor');
|
||||
|
||||
$listExtractors = $this->findAndSortTaggedServices('property_info.constructor_extractor', $container);
|
||||
$definition->replaceArgument(0, new IteratorArgument($listExtractors));
|
||||
}
|
||||
}
|
||||
51
backend/vendor/symfony/property-info/DependencyInjection/PropertyInfoPass.php
vendored
Normal file
51
backend/vendor/symfony/property-info/DependencyInjection/PropertyInfoPass.php
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
<?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\DependencyInjection;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use Symfony\Component\DependencyInjection\Compiler\PriorityTaggedServiceTrait;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
|
||||
/**
|
||||
* Adds extractors to the property_info service.
|
||||
*
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
class PropertyInfoPass implements CompilerPassInterface
|
||||
{
|
||||
use PriorityTaggedServiceTrait;
|
||||
|
||||
public function process(ContainerBuilder $container): void
|
||||
{
|
||||
if (!$container->hasDefinition('property_info')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$definition = $container->getDefinition('property_info');
|
||||
|
||||
$listExtractors = $this->findAndSortTaggedServices('property_info.list_extractor', $container);
|
||||
$definition->replaceArgument(0, new IteratorArgument($listExtractors));
|
||||
|
||||
$typeExtractors = $this->findAndSortTaggedServices('property_info.type_extractor', $container);
|
||||
$definition->replaceArgument(1, new IteratorArgument($typeExtractors));
|
||||
|
||||
$descriptionExtractors = $this->findAndSortTaggedServices('property_info.description_extractor', $container);
|
||||
$definition->replaceArgument(2, new IteratorArgument($descriptionExtractors));
|
||||
|
||||
$accessExtractors = $this->findAndSortTaggedServices('property_info.access_extractor', $container);
|
||||
$definition->replaceArgument(3, new IteratorArgument($accessExtractors));
|
||||
|
||||
$initializableExtractors = $this->findAndSortTaggedServices('property_info.initializable_extractor', $container);
|
||||
$definition->setArgument(4, new IteratorArgument($initializableExtractors));
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
19
backend/vendor/symfony/property-info/LICENSE
vendored
Normal file
19
backend/vendor/symfony/property-info/LICENSE
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2015-present Fabien Potencier
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
30
backend/vendor/symfony/property-info/PropertyAccessExtractorInterface.php
vendored
Normal file
30
backend/vendor/symfony/property-info/PropertyAccessExtractorInterface.php
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* Guesses if the property can be accessed or mutated.
|
||||
*
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
interface PropertyAccessExtractorInterface
|
||||
{
|
||||
/**
|
||||
* Is the property readable?
|
||||
*/
|
||||
public function isReadable(string $class, string $property, array $context = []): ?bool;
|
||||
|
||||
/**
|
||||
* Is the property writable?
|
||||
*/
|
||||
public function isWritable(string $class, string $property, array $context = []): ?bool;
|
||||
}
|
||||
30
backend/vendor/symfony/property-info/PropertyDescriptionExtractorInterface.php
vendored
Normal file
30
backend/vendor/symfony/property-info/PropertyDescriptionExtractorInterface.php
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* Guesses the property's human readable description.
|
||||
*
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
interface PropertyDescriptionExtractorInterface
|
||||
{
|
||||
/**
|
||||
* Gets the short description of the property.
|
||||
*/
|
||||
public function getShortDescription(string $class, string $property, array $context = []): ?string;
|
||||
|
||||
/**
|
||||
* Gets the long description of the property.
|
||||
*/
|
||||
public function getLongDescription(string $class, string $property, array $context = []): ?string;
|
||||
}
|
||||
36
backend/vendor/symfony/property-info/PropertyDocBlockExtractorInterface.php
vendored
Normal file
36
backend/vendor/symfony/property-info/PropertyDocBlockExtractorInterface.php
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
<?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;
|
||||
|
||||
use phpDocumentor\Reflection\DocBlock;
|
||||
|
||||
/**
|
||||
* Extract a property's doc block.
|
||||
*
|
||||
* A property's doc block may be located on a constructor promoted argument, on
|
||||
* the property or on a mutator for that property.
|
||||
*
|
||||
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
|
||||
*/
|
||||
interface PropertyDocBlockExtractorInterface
|
||||
{
|
||||
/**
|
||||
* Gets the first available doc block for a property. It finds the doc block
|
||||
* by the following priority:
|
||||
* - constructor promoted argument,
|
||||
* - the class property,
|
||||
* - a mutator method for that property.
|
||||
*
|
||||
* If no doc block is found, it will return null.
|
||||
*/
|
||||
public function getDocBlock(string $class, string $property): ?DocBlock;
|
||||
}
|
||||
144
backend/vendor/symfony/property-info/PropertyInfoCacheExtractor.php
vendored
Normal file
144
backend/vendor/symfony/property-info/PropertyInfoCacheExtractor.php
vendored
Normal file
@@ -0,0 +1,144 @@
|
||||
<?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;
|
||||
|
||||
use Psr\Cache\CacheItemPoolInterface;
|
||||
use Symfony\Component\PropertyInfo\Util\LegacyTypeConverter;
|
||||
use Symfony\Component\TypeInfo\Type;
|
||||
|
||||
/**
|
||||
* Adds a PSR-6 cache layer on top of an extractor.
|
||||
*
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
class PropertyInfoCacheExtractor implements PropertyInfoExtractorInterface, PropertyInitializableExtractorInterface
|
||||
{
|
||||
private array $arrayCache = [];
|
||||
|
||||
public function __construct(
|
||||
private readonly PropertyInfoExtractorInterface $propertyInfoExtractor,
|
||||
private readonly CacheItemPoolInterface $cacheItemPool,
|
||||
) {
|
||||
}
|
||||
|
||||
public function isReadable(string $class, string $property, array $context = []): ?bool
|
||||
{
|
||||
return $this->extract('isReadable', [$class, $property, $context]);
|
||||
}
|
||||
|
||||
public function isWritable(string $class, string $property, array $context = []): ?bool
|
||||
{
|
||||
return $this->extract('isWritable', [$class, $property, $context]);
|
||||
}
|
||||
|
||||
public function getShortDescription(string $class, string $property, array $context = []): ?string
|
||||
{
|
||||
return $this->extract('getShortDescription', [$class, $property, $context]);
|
||||
}
|
||||
|
||||
public function getLongDescription(string $class, string $property, array $context = []): ?string
|
||||
{
|
||||
return $this->extract('getLongDescription', [$class, $property, $context]);
|
||||
}
|
||||
|
||||
public function getProperties(string $class, array $context = []): ?array
|
||||
{
|
||||
return $this->extract('getProperties', [$class, $context]);
|
||||
}
|
||||
|
||||
public function getType(string $class, string $property, array $context = []): ?Type
|
||||
{
|
||||
try {
|
||||
$serializedArguments = serialize([$class, $property, $context]);
|
||||
} catch (\Exception) {
|
||||
// If arguments are not serializable, skip the cache
|
||||
if (method_exists($this->propertyInfoExtractor, 'getType')) {
|
||||
return $this->propertyInfoExtractor->getType($class, $property, $context);
|
||||
}
|
||||
|
||||
return LegacyTypeConverter::toTypeInfoType($this->propertyInfoExtractor->getTypes($class, $property, $context));
|
||||
}
|
||||
|
||||
// Calling rawurlencode escapes special characters not allowed in PSR-6's keys
|
||||
$key = rawurlencode('getType.'.$serializedArguments);
|
||||
|
||||
if (\array_key_exists($key, $this->arrayCache)) {
|
||||
return $this->arrayCache[$key];
|
||||
}
|
||||
|
||||
$item = $this->cacheItemPool->getItem($key);
|
||||
|
||||
if ($item->isHit()) {
|
||||
return $this->arrayCache[$key] = $item->get();
|
||||
}
|
||||
|
||||
if (method_exists($this->propertyInfoExtractor, 'getType')) {
|
||||
$value = $this->propertyInfoExtractor->getType($class, $property, $context);
|
||||
} else {
|
||||
$value = LegacyTypeConverter::toTypeInfoType($this->propertyInfoExtractor->getTypes($class, $property, $context));
|
||||
}
|
||||
|
||||
$item->set($value);
|
||||
$this->cacheItemPool->save($item);
|
||||
|
||||
return $this->arrayCache[$key] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
return $this->extract('getTypes', [$class, $property, $context]);
|
||||
}
|
||||
|
||||
public function isInitializable(string $class, string $property, array $context = []): ?bool
|
||||
{
|
||||
return $this->extract('isInitializable', [$class, $property, $context]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the cached data if applicable or delegates to the decorated extractor.
|
||||
*/
|
||||
private function extract(string $method, array $arguments): mixed
|
||||
{
|
||||
try {
|
||||
$serializedArguments = serialize($arguments);
|
||||
} catch (\Exception) {
|
||||
// If arguments are not serializable, skip the cache
|
||||
return $this->propertyInfoExtractor->{$method}(...$arguments);
|
||||
}
|
||||
|
||||
// Calling rawurlencode escapes special characters not allowed in PSR-6's keys
|
||||
$key = rawurlencode($method.'.'.$serializedArguments);
|
||||
|
||||
if (\array_key_exists($key, $this->arrayCache)) {
|
||||
return $this->arrayCache[$key];
|
||||
}
|
||||
|
||||
$item = $this->cacheItemPool->getItem($key);
|
||||
|
||||
if ($item->isHit()) {
|
||||
return $this->arrayCache[$key] = $item->get();
|
||||
}
|
||||
|
||||
$value = $this->propertyInfoExtractor->{$method}(...$arguments);
|
||||
$item->set($value);
|
||||
$this->cacheItemPool->save($item);
|
||||
|
||||
return $this->arrayCache[$key] = $value;
|
||||
}
|
||||
}
|
||||
119
backend/vendor/symfony/property-info/PropertyInfoExtractor.php
vendored
Normal file
119
backend/vendor/symfony/property-info/PropertyInfoExtractor.php
vendored
Normal file
@@ -0,0 +1,119 @@
|
||||
<?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;
|
||||
|
||||
use Symfony\Component\PropertyInfo\Util\LegacyTypeConverter;
|
||||
use Symfony\Component\TypeInfo\Type;
|
||||
|
||||
/**
|
||||
* Default {@see PropertyInfoExtractorInterface} implementation.
|
||||
*
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
class PropertyInfoExtractor implements PropertyInfoExtractorInterface, PropertyInitializableExtractorInterface
|
||||
{
|
||||
/**
|
||||
* @param iterable<mixed, PropertyListExtractorInterface> $listExtractors
|
||||
* @param iterable<mixed, PropertyTypeExtractorInterface> $typeExtractors
|
||||
* @param iterable<mixed, PropertyDescriptionExtractorInterface> $descriptionExtractors
|
||||
* @param iterable<mixed, PropertyAccessExtractorInterface> $accessExtractors
|
||||
* @param iterable<mixed, PropertyInitializableExtractorInterface> $initializableExtractors
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly iterable $listExtractors = [],
|
||||
private readonly iterable $typeExtractors = [],
|
||||
private readonly iterable $descriptionExtractors = [],
|
||||
private readonly iterable $accessExtractors = [],
|
||||
private readonly iterable $initializableExtractors = [],
|
||||
) {
|
||||
}
|
||||
|
||||
public function getProperties(string $class, array $context = []): ?array
|
||||
{
|
||||
return $this->extract($this->listExtractors, 'getProperties', [$class, $context]);
|
||||
}
|
||||
|
||||
public function getShortDescription(string $class, string $property, array $context = []): ?string
|
||||
{
|
||||
return $this->extract($this->descriptionExtractors, 'getShortDescription', [$class, $property, $context]);
|
||||
}
|
||||
|
||||
public function getLongDescription(string $class, string $property, array $context = []): ?string
|
||||
{
|
||||
return $this->extract($this->descriptionExtractors, 'getLongDescription', [$class, $property, $context]);
|
||||
}
|
||||
|
||||
public function getType(string $class, string $property, array $context = []): ?Type
|
||||
{
|
||||
foreach ($this->typeExtractors as $extractor) {
|
||||
if (!method_exists($extractor, 'getType')) {
|
||||
$legacyTypes = $extractor->getTypes($class, $property, $context);
|
||||
|
||||
if (null !== $legacyTypes) {
|
||||
return LegacyTypeConverter::toTypeInfoType($legacyTypes);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (null !== $value = $extractor->getType($class, $property, $context)) {
|
||||
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);
|
||||
|
||||
return $this->extract($this->typeExtractors, 'getTypes', [$class, $property, $context]);
|
||||
}
|
||||
|
||||
public function isReadable(string $class, string $property, array $context = []): ?bool
|
||||
{
|
||||
return $this->extract($this->accessExtractors, 'isReadable', [$class, $property, $context]);
|
||||
}
|
||||
|
||||
public function isWritable(string $class, string $property, array $context = []): ?bool
|
||||
{
|
||||
return $this->extract($this->accessExtractors, 'isWritable', [$class, $property, $context]);
|
||||
}
|
||||
|
||||
public function isInitializable(string $class, string $property, array $context = []): ?bool
|
||||
{
|
||||
return $this->extract($this->initializableExtractors, 'isInitializable', [$class, $property, $context]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates over registered extractors and return the first value found.
|
||||
*
|
||||
* @param iterable<mixed, object> $extractors
|
||||
* @param list<mixed> $arguments
|
||||
*/
|
||||
private function extract(iterable $extractors, string $method, array $arguments): mixed
|
||||
{
|
||||
foreach ($extractors as $extractor) {
|
||||
if (null !== $value = $extractor->{$method}(...$arguments)) {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
23
backend/vendor/symfony/property-info/PropertyInfoExtractorInterface.php
vendored
Normal file
23
backend/vendor/symfony/property-info/PropertyInfoExtractorInterface.php
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* Gets info about PHP class properties.
|
||||
*
|
||||
* A convenient interface inheriting all specific info interfaces.
|
||||
*
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
interface PropertyInfoExtractorInterface extends PropertyTypeExtractorInterface, PropertyDescriptionExtractorInterface, PropertyAccessExtractorInterface, PropertyListExtractorInterface
|
||||
{
|
||||
}
|
||||
25
backend/vendor/symfony/property-info/PropertyInitializableExtractorInterface.php
vendored
Normal file
25
backend/vendor/symfony/property-info/PropertyInitializableExtractorInterface.php
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* Guesses if the property can be initialized through the constructor.
|
||||
*
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
interface PropertyInitializableExtractorInterface
|
||||
{
|
||||
/**
|
||||
* Is the property initializable? Returns true if a constructor's parameter matches the given property name.
|
||||
*/
|
||||
public function isInitializable(string $class, string $property, array $context = []): ?bool;
|
||||
}
|
||||
27
backend/vendor/symfony/property-info/PropertyListExtractorInterface.php
vendored
Normal file
27
backend/vendor/symfony/property-info/PropertyListExtractorInterface.php
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* Extracts the list of properties available for the given class.
|
||||
*
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
interface PropertyListExtractorInterface
|
||||
{
|
||||
/**
|
||||
* Gets the list of properties available for the given class.
|
||||
*
|
||||
* @return string[]|null
|
||||
*/
|
||||
public function getProperties(string $class, array $context = []): ?array;
|
||||
}
|
||||
70
backend/vendor/symfony/property-info/PropertyReadInfo.php
vendored
Normal file
70
backend/vendor/symfony/property-info/PropertyReadInfo.php
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* The property read info tells how a property can be read.
|
||||
*
|
||||
* @author Joel Wurtz <jwurtz@jolicode.com>
|
||||
*/
|
||||
final class PropertyReadInfo
|
||||
{
|
||||
public const TYPE_METHOD = 'method';
|
||||
public const TYPE_PROPERTY = 'property';
|
||||
|
||||
public const VISIBILITY_PUBLIC = 'public';
|
||||
public const VISIBILITY_PROTECTED = 'protected';
|
||||
public const VISIBILITY_PRIVATE = 'private';
|
||||
|
||||
public function __construct(
|
||||
private readonly string $type,
|
||||
private readonly string $name,
|
||||
private readonly string $visibility,
|
||||
private readonly bool $static,
|
||||
private readonly bool $byRef,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get type of access.
|
||||
*/
|
||||
public function getType(): string
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get name of the access, which can be a method name or a property name, depending on the type.
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function getVisibility(): string
|
||||
{
|
||||
return $this->visibility;
|
||||
}
|
||||
|
||||
public function isStatic(): bool
|
||||
{
|
||||
return $this->static;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this accessor can be accessed by reference.
|
||||
*/
|
||||
public function canBeReference(): bool
|
||||
{
|
||||
return $this->byRef;
|
||||
}
|
||||
}
|
||||
25
backend/vendor/symfony/property-info/PropertyReadInfoExtractorInterface.php
vendored
Normal file
25
backend/vendor/symfony/property-info/PropertyReadInfoExtractorInterface.php
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* Extract read information for the property of a class.
|
||||
*
|
||||
* @author Joel Wurtz <jwurtz@jolicode.com>
|
||||
*/
|
||||
interface PropertyReadInfoExtractorInterface
|
||||
{
|
||||
/**
|
||||
* Get read information object for a given property of a class.
|
||||
*/
|
||||
public function getReadInfo(string $class, string $property, array $context = []): ?PropertyReadInfo;
|
||||
}
|
||||
34
backend/vendor/symfony/property-info/PropertyTypeExtractorInterface.php
vendored
Normal file
34
backend/vendor/symfony/property-info/PropertyTypeExtractorInterface.php
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
<?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;
|
||||
|
||||
use Symfony\Component\PropertyInfo\Type as LegacyType;
|
||||
use Symfony\Component\TypeInfo\Type;
|
||||
|
||||
/**
|
||||
* Type Extractor Interface.
|
||||
*
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*
|
||||
* @method Type|null getType(string $class, string $property, array $context = [])
|
||||
*/
|
||||
interface PropertyTypeExtractorInterface
|
||||
{
|
||||
/**
|
||||
* Gets types of a property.
|
||||
*
|
||||
* @deprecated since Symfony 7.3, use "getType" instead
|
||||
*
|
||||
* @return LegacyType[]|null
|
||||
*/
|
||||
public function getTypes(string $class, string $property, array $context = []): ?array;
|
||||
}
|
||||
117
backend/vendor/symfony/property-info/PropertyWriteInfo.php
vendored
Normal file
117
backend/vendor/symfony/property-info/PropertyWriteInfo.php
vendored
Normal file
@@ -0,0 +1,117 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* The write mutator defines how a property can be written.
|
||||
*
|
||||
* @author Joel Wurtz <jwurtz@jolicode.com>
|
||||
*/
|
||||
final class PropertyWriteInfo
|
||||
{
|
||||
public const TYPE_NONE = 'none';
|
||||
public const TYPE_METHOD = 'method';
|
||||
public const TYPE_PROPERTY = 'property';
|
||||
public const TYPE_ADDER_AND_REMOVER = 'adder_and_remover';
|
||||
public const TYPE_CONSTRUCTOR = 'constructor';
|
||||
|
||||
public const VISIBILITY_PUBLIC = 'public';
|
||||
public const VISIBILITY_PROTECTED = 'protected';
|
||||
public const VISIBILITY_PRIVATE = 'private';
|
||||
|
||||
private ?self $adderInfo = null;
|
||||
private ?self $removerInfo = null;
|
||||
private array $errors = [];
|
||||
|
||||
public function __construct(
|
||||
private readonly string $type = self::TYPE_NONE,
|
||||
private readonly ?string $name = null,
|
||||
private readonly ?string $visibility = null,
|
||||
private readonly ?bool $static = null,
|
||||
) {
|
||||
}
|
||||
|
||||
public function getType(): string
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
if (null === $this->name) {
|
||||
throw new \LogicException("Calling getName() when having a mutator of type {$this->type} is not tolerated.");
|
||||
}
|
||||
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function setAdderInfo(self $adderInfo): void
|
||||
{
|
||||
$this->adderInfo = $adderInfo;
|
||||
}
|
||||
|
||||
public function getAdderInfo(): self
|
||||
{
|
||||
if (null === $this->adderInfo) {
|
||||
throw new \LogicException("Calling getAdderInfo() when having a mutator of type {$this->type} is not tolerated.");
|
||||
}
|
||||
|
||||
return $this->adderInfo;
|
||||
}
|
||||
|
||||
public function setRemoverInfo(self $removerInfo): void
|
||||
{
|
||||
$this->removerInfo = $removerInfo;
|
||||
}
|
||||
|
||||
public function getRemoverInfo(): self
|
||||
{
|
||||
if (null === $this->removerInfo) {
|
||||
throw new \LogicException("Calling getRemoverInfo() when having a mutator of type {$this->type} is not tolerated.");
|
||||
}
|
||||
|
||||
return $this->removerInfo;
|
||||
}
|
||||
|
||||
public function getVisibility(): string
|
||||
{
|
||||
if (null === $this->visibility) {
|
||||
throw new \LogicException("Calling getVisibility() when having a mutator of type {$this->type} is not tolerated.");
|
||||
}
|
||||
|
||||
return $this->visibility;
|
||||
}
|
||||
|
||||
public function isStatic(): bool
|
||||
{
|
||||
if (null === $this->static) {
|
||||
throw new \LogicException("Calling isStatic() when having a mutator of type {$this->type} is not tolerated.");
|
||||
}
|
||||
|
||||
return $this->static;
|
||||
}
|
||||
|
||||
public function setErrors(array $errors): void
|
||||
{
|
||||
$this->errors = $errors;
|
||||
}
|
||||
|
||||
public function getErrors(): array
|
||||
{
|
||||
return $this->errors;
|
||||
}
|
||||
|
||||
public function hasErrors(): bool
|
||||
{
|
||||
return (bool) \count($this->errors);
|
||||
}
|
||||
}
|
||||
25
backend/vendor/symfony/property-info/PropertyWriteInfoExtractorInterface.php
vendored
Normal file
25
backend/vendor/symfony/property-info/PropertyWriteInfoExtractorInterface.php
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* Extract write information for the property of a class.
|
||||
*
|
||||
* @author Joel Wurtz <jwurtz@jolicode.com>
|
||||
*/
|
||||
interface PropertyWriteInfoExtractorInterface
|
||||
{
|
||||
/**
|
||||
* Get write information object for a given property of a class.
|
||||
*/
|
||||
public function getWriteInfo(string $class, string $property, array $context = []): ?PropertyWriteInfo;
|
||||
}
|
||||
14
backend/vendor/symfony/property-info/README.md
vendored
Normal file
14
backend/vendor/symfony/property-info/README.md
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
PropertyInfo Component
|
||||
======================
|
||||
|
||||
The PropertyInfo component extracts information about PHP class' properties
|
||||
using metadata of popular sources.
|
||||
|
||||
Resources
|
||||
---------
|
||||
|
||||
* [Documentation](https://symfony.com/doc/current/components/property_info.html)
|
||||
* [Contributing](https://symfony.com/doc/current/contributing/index.html)
|
||||
* [Report issues](https://github.com/symfony/symfony/issues) and
|
||||
[send Pull Requests](https://github.com/symfony/symfony/pulls)
|
||||
in the [main Symfony repository](https://github.com/symfony/symfony)
|
||||
167
backend/vendor/symfony/property-info/Type.php
vendored
Normal file
167
backend/vendor/symfony/property-info/Type.php
vendored
Normal file
@@ -0,0 +1,167 @@
|
||||
<?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;
|
||||
|
||||
trigger_deprecation('symfony/property-info', '7.3', 'The "%s" class is deprecated. Use "%s" class from "symfony/type-info" instead.', Type::class, \Symfony\Component\TypeInfo\Type::class);
|
||||
|
||||
/**
|
||||
* Type value object (immutable).
|
||||
*
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*
|
||||
* @deprecated since Symfony 7.3, use "Symfony\Component\TypeInfo\Type" class from "symfony/type-info" instead
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
class Type
|
||||
{
|
||||
public const BUILTIN_TYPE_INT = 'int';
|
||||
public const BUILTIN_TYPE_FLOAT = 'float';
|
||||
public const BUILTIN_TYPE_STRING = 'string';
|
||||
public const BUILTIN_TYPE_BOOL = 'bool';
|
||||
public const BUILTIN_TYPE_RESOURCE = 'resource';
|
||||
public const BUILTIN_TYPE_OBJECT = 'object';
|
||||
public const BUILTIN_TYPE_ARRAY = 'array';
|
||||
public const BUILTIN_TYPE_NULL = 'null';
|
||||
public const BUILTIN_TYPE_FALSE = 'false';
|
||||
public const BUILTIN_TYPE_TRUE = 'true';
|
||||
public const BUILTIN_TYPE_CALLABLE = 'callable';
|
||||
public const BUILTIN_TYPE_ITERABLE = 'iterable';
|
||||
|
||||
/**
|
||||
* List of PHP builtin types.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
public static array $builtinTypes = [
|
||||
self::BUILTIN_TYPE_INT,
|
||||
self::BUILTIN_TYPE_FLOAT,
|
||||
self::BUILTIN_TYPE_STRING,
|
||||
self::BUILTIN_TYPE_BOOL,
|
||||
self::BUILTIN_TYPE_RESOURCE,
|
||||
self::BUILTIN_TYPE_OBJECT,
|
||||
self::BUILTIN_TYPE_ARRAY,
|
||||
self::BUILTIN_TYPE_CALLABLE,
|
||||
self::BUILTIN_TYPE_FALSE,
|
||||
self::BUILTIN_TYPE_TRUE,
|
||||
self::BUILTIN_TYPE_NULL,
|
||||
self::BUILTIN_TYPE_ITERABLE,
|
||||
];
|
||||
|
||||
/**
|
||||
* List of PHP builtin collection types.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
public static array $builtinCollectionTypes = [
|
||||
self::BUILTIN_TYPE_ARRAY,
|
||||
self::BUILTIN_TYPE_ITERABLE,
|
||||
];
|
||||
|
||||
private array $collectionKeyType;
|
||||
private array $collectionValueType;
|
||||
|
||||
/**
|
||||
* @param Type[]|Type|null $collectionKeyType
|
||||
* @param Type[]|Type|null $collectionValueType
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function __construct(
|
||||
private string $builtinType,
|
||||
private bool $nullable = false,
|
||||
private ?string $class = null,
|
||||
private bool $collection = false,
|
||||
array|self|null $collectionKeyType = null,
|
||||
array|self|null $collectionValueType = null,
|
||||
) {
|
||||
if (!\in_array($builtinType, self::$builtinTypes, true)) {
|
||||
throw new \InvalidArgumentException(\sprintf('"%s" is not a valid PHP type.', $builtinType));
|
||||
}
|
||||
|
||||
$this->collectionKeyType = $this->validateCollectionArgument($collectionKeyType, 5, '$collectionKeyType') ?? [];
|
||||
$this->collectionValueType = $this->validateCollectionArgument($collectionValueType, 6, '$collectionValueType') ?? [];
|
||||
}
|
||||
|
||||
private function validateCollectionArgument(array|self|null $collectionArgument, int $argumentIndex, string $argumentName): ?array
|
||||
{
|
||||
if (null === $collectionArgument) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (\is_array($collectionArgument)) {
|
||||
foreach ($collectionArgument as $type) {
|
||||
if (!$type instanceof self) {
|
||||
throw new \TypeError(\sprintf('"%s()": Argument #%d (%s) must be of type "%s[]", "%s" or "null", array value "%s" given.', __METHOD__, $argumentIndex, $argumentName, self::class, self::class, get_debug_type($collectionArgument)));
|
||||
}
|
||||
}
|
||||
|
||||
return $collectionArgument;
|
||||
}
|
||||
|
||||
return [$collectionArgument];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets built-in type.
|
||||
*
|
||||
* Can be bool, int, float, string, array, object, resource, null, callback or iterable.
|
||||
*/
|
||||
public function getBuiltinType(): string
|
||||
{
|
||||
return $this->builtinType;
|
||||
}
|
||||
|
||||
public function isNullable(): bool
|
||||
{
|
||||
return $this->nullable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the class name.
|
||||
*
|
||||
* Only applicable if the built-in type is object.
|
||||
*/
|
||||
public function getClassName(): ?string
|
||||
{
|
||||
return $this->class;
|
||||
}
|
||||
|
||||
public function isCollection(): bool
|
||||
{
|
||||
return $this->collection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets collection key types.
|
||||
*
|
||||
* Only applicable for a collection type.
|
||||
*
|
||||
* @return Type[]
|
||||
*/
|
||||
public function getCollectionKeyTypes(): array
|
||||
{
|
||||
return $this->collectionKeyType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets collection value types.
|
||||
*
|
||||
* Only applicable for a collection type.
|
||||
*
|
||||
* @return Type[]
|
||||
*/
|
||||
public function getCollectionValueTypes(): array
|
||||
{
|
||||
return $this->collectionValueType;
|
||||
}
|
||||
}
|
||||
94
backend/vendor/symfony/property-info/Util/LegacyTypeConverter.php
vendored
Normal file
94
backend/vendor/symfony/property-info/Util/LegacyTypeConverter.php
vendored
Normal file
@@ -0,0 +1,94 @@
|
||||
<?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\Util;
|
||||
|
||||
use Symfony\Component\PropertyInfo\Type as LegacyType;
|
||||
use Symfony\Component\TypeInfo\Type;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class LegacyTypeConverter
|
||||
{
|
||||
/**
|
||||
* @param LegacyType[]|null $legacyTypes
|
||||
*/
|
||||
public static function toTypeInfoType(?array $legacyTypes): ?Type
|
||||
{
|
||||
if (null === $legacyTypes || [] === $legacyTypes) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$nullable = false;
|
||||
$types = [];
|
||||
|
||||
foreach ($legacyTypes as $legacyType) {
|
||||
switch ($legacyType->getBuiltinType()) {
|
||||
case LegacyType::BUILTIN_TYPE_ARRAY:
|
||||
$typeInfoType = Type::array(self::toTypeInfoType($legacyType->getCollectionValueTypes()), self::toTypeInfoType($legacyType->getCollectionKeyTypes()));
|
||||
break;
|
||||
case LegacyType::BUILTIN_TYPE_BOOL:
|
||||
$typeInfoType = Type::bool();
|
||||
break;
|
||||
case LegacyType::BUILTIN_TYPE_CALLABLE:
|
||||
$typeInfoType = Type::callable();
|
||||
break;
|
||||
case LegacyType::BUILTIN_TYPE_FALSE:
|
||||
$typeInfoType = Type::false();
|
||||
break;
|
||||
case LegacyType::BUILTIN_TYPE_FLOAT:
|
||||
$typeInfoType = Type::float();
|
||||
break;
|
||||
case LegacyType::BUILTIN_TYPE_INT:
|
||||
$typeInfoType = Type::int();
|
||||
break;
|
||||
case LegacyType::BUILTIN_TYPE_ITERABLE:
|
||||
$typeInfoType = Type::iterable(self::toTypeInfoType($legacyType->getCollectionValueTypes()), self::toTypeInfoType($legacyType->getCollectionKeyTypes()));
|
||||
break;
|
||||
case LegacyType::BUILTIN_TYPE_OBJECT:
|
||||
if ($legacyType->isCollection()) {
|
||||
$typeInfoType = Type::collection(Type::object($legacyType->getClassName()), self::toTypeInfoType($legacyType->getCollectionValueTypes()), self::toTypeInfoType($legacyType->getCollectionKeyTypes()));
|
||||
} else {
|
||||
$typeInfoType = Type::object($legacyType->getClassName());
|
||||
}
|
||||
|
||||
break;
|
||||
case LegacyType::BUILTIN_TYPE_RESOURCE:
|
||||
$typeInfoType = Type::resource();
|
||||
break;
|
||||
case LegacyType::BUILTIN_TYPE_STRING:
|
||||
$typeInfoType = Type::string();
|
||||
break;
|
||||
case LegacyType::BUILTIN_TYPE_TRUE:
|
||||
$typeInfoType = Type::true();
|
||||
break;
|
||||
default:
|
||||
$typeInfoType = null;
|
||||
break;
|
||||
}
|
||||
|
||||
if (LegacyType::BUILTIN_TYPE_NULL === $legacyType->getBuiltinType() || $legacyType->isNullable()) {
|
||||
$nullable = true;
|
||||
}
|
||||
|
||||
if (null !== $typeInfoType) {
|
||||
$types[] = $typeInfoType;
|
||||
}
|
||||
}
|
||||
|
||||
if (1 === \count($types)) {
|
||||
return $nullable ? Type::nullable($types[0]) : $types[0];
|
||||
}
|
||||
|
||||
return $nullable ? Type::nullable(Type::union(...$types)) : Type::union(...$types);
|
||||
}
|
||||
}
|
||||
475
backend/vendor/symfony/property-info/Util/PhpDocTypeHelper.php
vendored
Normal file
475
backend/vendor/symfony/property-info/Util/PhpDocTypeHelper.php
vendored
Normal file
@@ -0,0 +1,475 @@
|
||||
<?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\Util;
|
||||
|
||||
use phpDocumentor\Reflection\PseudoType;
|
||||
use phpDocumentor\Reflection\PseudoTypes\ConstExpression;
|
||||
use phpDocumentor\Reflection\PseudoTypes\Generic;
|
||||
use phpDocumentor\Reflection\PseudoTypes\List_;
|
||||
use phpDocumentor\Reflection\PseudoTypes\Scalar;
|
||||
use phpDocumentor\Reflection\Type as DocType;
|
||||
use phpDocumentor\Reflection\Types\Array_;
|
||||
use phpDocumentor\Reflection\Types\Collection;
|
||||
use phpDocumentor\Reflection\Types\Compound;
|
||||
use phpDocumentor\Reflection\Types\Integer;
|
||||
use phpDocumentor\Reflection\Types\Mixed_;
|
||||
use phpDocumentor\Reflection\Types\Null_;
|
||||
use phpDocumentor\Reflection\Types\Nullable;
|
||||
use phpDocumentor\Reflection\Types\Scalar as LegacyScalar;
|
||||
use phpDocumentor\Reflection\Types\String_;
|
||||
use Symfony\Component\PropertyInfo\Type as LegacyType;
|
||||
use Symfony\Component\TypeInfo\Type;
|
||||
use Symfony\Component\TypeInfo\Type\BuiltinType;
|
||||
use Symfony\Component\TypeInfo\TypeIdentifier;
|
||||
|
||||
// Workaround for phpdocumentor/type-resolver < 1.6
|
||||
// We trigger the autoloader here, so we don't need to trigger it inside the loop later.
|
||||
class_exists(List_::class);
|
||||
|
||||
/**
|
||||
* Transforms a php doc type to a {@link Type} instance.
|
||||
*
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
* @author Guilhem N. <egetick@gmail.com>
|
||||
*/
|
||||
final class PhpDocTypeHelper
|
||||
{
|
||||
/**
|
||||
* Creates a {@see LegacyType} from a PHPDoc type.
|
||||
*
|
||||
* @deprecated since Symfony 7.3, use "getType" instead
|
||||
*
|
||||
* @return LegacyType[]
|
||||
*/
|
||||
public function getTypes(DocType $varType): array
|
||||
{
|
||||
trigger_deprecation('symfony/property-info', '7.3', 'The "%s()" method is deprecated, use "%s::getType()" instead.', __METHOD__, self::class);
|
||||
|
||||
if ($varType instanceof ConstExpression) {
|
||||
// It's safer to fall back to other extractors here, as resolving const types correctly is not easy at the moment
|
||||
return [];
|
||||
}
|
||||
|
||||
$types = [];
|
||||
$nullable = false;
|
||||
|
||||
if ($varType instanceof Nullable) {
|
||||
$nullable = true;
|
||||
$varType = $varType->getActualType();
|
||||
}
|
||||
|
||||
if ($varType instanceof LegacyScalar || $varType instanceof Scalar) {
|
||||
return [
|
||||
new LegacyType(LegacyType::BUILTIN_TYPE_BOOL),
|
||||
new LegacyType(LegacyType::BUILTIN_TYPE_FLOAT),
|
||||
new LegacyType(LegacyType::BUILTIN_TYPE_INT),
|
||||
new LegacyType(LegacyType::BUILTIN_TYPE_STRING),
|
||||
];
|
||||
}
|
||||
|
||||
if (!$varType instanceof Compound) {
|
||||
if ($varType instanceof Null_) {
|
||||
$nullable = true;
|
||||
}
|
||||
|
||||
$type = $this->createLegacyType($varType, $nullable);
|
||||
if (null !== $type) {
|
||||
$types[] = $type;
|
||||
}
|
||||
|
||||
return $types;
|
||||
}
|
||||
|
||||
$varTypes = [];
|
||||
for ($typeIndex = 0; $varType->has($typeIndex); ++$typeIndex) {
|
||||
$type = $varType->get($typeIndex);
|
||||
|
||||
if ($type instanceof Mixed_) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if ($type instanceof ConstExpression) {
|
||||
// It's safer to fall back to other extractors here, as resolving const types correctly is not easy at the moment
|
||||
return [];
|
||||
}
|
||||
|
||||
// If null is present, all types are nullable
|
||||
if ($type instanceof Null_) {
|
||||
$nullable = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($type instanceof Nullable) {
|
||||
$nullable = true;
|
||||
$type = $type->getActualType();
|
||||
}
|
||||
|
||||
$varTypes[] = $type;
|
||||
}
|
||||
|
||||
foreach ($varTypes as $varType) {
|
||||
$type = $this->createLegacyType($varType, $nullable);
|
||||
if (null !== $type) {
|
||||
$types[] = $type;
|
||||
}
|
||||
}
|
||||
|
||||
return $types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@see Type} from a PHPDoc type.
|
||||
*/
|
||||
public function getType(DocType $varType): ?Type
|
||||
{
|
||||
if ($varType instanceof ConstExpression) {
|
||||
// It's safer to fall back to other extractors here, as resolving const types correctly is not easy at the moment
|
||||
return null;
|
||||
}
|
||||
|
||||
$nullable = false;
|
||||
|
||||
if ($varType instanceof Nullable) {
|
||||
$nullable = true;
|
||||
$varType = $varType->getActualType();
|
||||
}
|
||||
|
||||
if (!$varType instanceof Compound) {
|
||||
if ($varType instanceof Null_) {
|
||||
$nullable = true;
|
||||
}
|
||||
|
||||
$type = $this->createType($varType);
|
||||
|
||||
return $nullable ? Type::nullable($type) : $type;
|
||||
}
|
||||
|
||||
$varTypes = [];
|
||||
for ($typeIndex = 0; $varType->has($typeIndex); ++$typeIndex) {
|
||||
$type = $varType->get($typeIndex);
|
||||
|
||||
if ($type instanceof ConstExpression) {
|
||||
// It's safer to fall back to other extractors here, as resolving const types correctly is not easy at the moment
|
||||
return null;
|
||||
}
|
||||
|
||||
// If null is present, all types are nullable
|
||||
if ($type instanceof Null_) {
|
||||
$nullable = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($type instanceof Nullable) {
|
||||
$nullable = true;
|
||||
$type = $type->getActualType();
|
||||
}
|
||||
|
||||
$varTypes[] = $type;
|
||||
}
|
||||
|
||||
$unionTypes = [];
|
||||
foreach ($varTypes as $varType) {
|
||||
if (!$t = $this->createType($varType)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($t instanceof BuiltinType && TypeIdentifier::MIXED === $t->getTypeIdentifier()) {
|
||||
return Type::mixed();
|
||||
}
|
||||
|
||||
$unionTypes[] = $t;
|
||||
}
|
||||
|
||||
if (!$unionTypes) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$type = 1 === \count($unionTypes) ? $unionTypes[0] : Type::union(...$unionTypes);
|
||||
|
||||
return $nullable ? Type::nullable($type) : $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@see LegacyType} from a PHPDoc type.
|
||||
*/
|
||||
private function createLegacyType(DocType $type, bool $nullable): ?LegacyType
|
||||
{
|
||||
$docType = (string) $type;
|
||||
|
||||
if ('mixed[]' === $docType) {
|
||||
$docType = 'array';
|
||||
} elseif ('array' !== $docType && $type instanceof Array_ && $this->hasNoExplicitKeyType($type)) {
|
||||
$docType = \sprintf('%s[]', $type->getValueType());
|
||||
}
|
||||
|
||||
if ($type instanceof Collection || $type instanceof Generic) {
|
||||
$fqsen = $type->getFqsen();
|
||||
if ($type instanceof Collection && $fqsen && 'list' === $fqsen->getName() && !class_exists(List_::class, false) && !class_exists((string) $fqsen)) {
|
||||
// Workaround for phpdocumentor/type-resolver < 1.6
|
||||
return new LegacyType(LegacyType::BUILTIN_TYPE_ARRAY, $nullable, null, true, new LegacyType(LegacyType::BUILTIN_TYPE_INT), $this->getTypes($type->getValueType()));
|
||||
}
|
||||
|
||||
[$phpType, $class] = $this->getPhpTypeAndClass((string) $fqsen);
|
||||
|
||||
$collection = is_a($class, \Traversable::class, true) || is_a($class, \ArrayAccess::class, true);
|
||||
|
||||
// it's safer to fall back to other extractors if the generic type is too abstract
|
||||
if (!$collection && !class_exists($class, false) && !interface_exists($class, false)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($type instanceof Generic) {
|
||||
$genericTypes = $type->getTypes();
|
||||
|
||||
if (null === $valueType = $genericTypes[1] ?? null) {
|
||||
$keyType = new Compound([new String_(), new Integer()]);
|
||||
$valueType = $genericTypes[0] ?? new Mixed_();
|
||||
} else {
|
||||
$keyType = $genericTypes[0];
|
||||
}
|
||||
} else {
|
||||
$keyType = $type->getKeyType();
|
||||
$valueType = $type->getValueType();
|
||||
}
|
||||
|
||||
$keys = $this->getTypes($keyType);
|
||||
$values = $this->getTypes($valueType);
|
||||
|
||||
return new LegacyType($phpType, $nullable, $class, $collection, $keys, $values);
|
||||
}
|
||||
|
||||
// Cannot guess
|
||||
if (!$docType || 'mixed' === $docType) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (str_ends_with($docType, '[]') && $type instanceof Array_) {
|
||||
$collectionKeyTypes = new LegacyType(LegacyType::BUILTIN_TYPE_INT);
|
||||
$collectionValueTypes = $this->getTypes($type->getValueType());
|
||||
|
||||
return new LegacyType(LegacyType::BUILTIN_TYPE_ARRAY, $nullable, null, true, $collectionKeyTypes, $collectionValueTypes);
|
||||
}
|
||||
|
||||
if ((str_starts_with($docType, 'list<') || str_starts_with($docType, 'array<')) && $type instanceof Array_) {
|
||||
// array<value> is converted to x[] which is handled above
|
||||
// so it's only necessary to handle array<key, value> here
|
||||
$collectionKeyTypes = $this->getTypes($type->getKeyType());
|
||||
$collectionValueTypes = $this->getTypes($type->getValueType());
|
||||
|
||||
return new LegacyType(LegacyType::BUILTIN_TYPE_ARRAY, $nullable, null, true, $collectionKeyTypes, $collectionValueTypes);
|
||||
}
|
||||
|
||||
if ($type instanceof PseudoType) {
|
||||
if ($type->underlyingType() instanceof Integer) {
|
||||
return new LegacyType(LegacyType::BUILTIN_TYPE_INT, $nullable, null);
|
||||
} elseif ($type->underlyingType() instanceof String_) {
|
||||
return new LegacyType(LegacyType::BUILTIN_TYPE_STRING, $nullable, null);
|
||||
}
|
||||
}
|
||||
|
||||
$docType = $this->normalizeType($docType);
|
||||
[$phpType, $class] = $this->getPhpTypeAndClass($docType);
|
||||
|
||||
if ('array' === $docType) {
|
||||
return new LegacyType(LegacyType::BUILTIN_TYPE_ARRAY, $nullable, null, true, null, null);
|
||||
}
|
||||
|
||||
return new LegacyType($phpType, $nullable, $class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@see Type} from a PHPDoc type.
|
||||
*/
|
||||
private function createType(DocType $docType): ?Type
|
||||
{
|
||||
$docTypeString = (string) $docType;
|
||||
|
||||
if ('mixed[]' === $docTypeString) {
|
||||
$docTypeString = 'array';
|
||||
}
|
||||
|
||||
if ($docType instanceof Generic) {
|
||||
$fqsen = $docType->getFqsen();
|
||||
|
||||
[$phpType, $class] = $this->getPhpTypeAndClass((string) $fqsen);
|
||||
|
||||
$collection = is_a($class, \Traversable::class, true) || is_a($class, \ArrayAccess::class, true);
|
||||
|
||||
// it's safer to fall back to other extractors if the generic type is too abstract
|
||||
if (!$collection && !class_exists($class, false) && !interface_exists($class, false)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$genericTypes = $docType->getTypes();
|
||||
$type = null !== $class ? Type::object($class) : Type::builtin($phpType);
|
||||
|
||||
if ($collection) {
|
||||
if (null === $valueType = $genericTypes[1] ?? null) {
|
||||
$keyType = null;
|
||||
$valueType = $genericTypes[0] ?? null;
|
||||
} else {
|
||||
$keyType = $genericTypes[0] ?? null;
|
||||
}
|
||||
|
||||
$value = $valueType ? $this->getType($valueType) : null;
|
||||
$key = $keyType ? $this->getType($keyType) : null;
|
||||
|
||||
return Type::collection($type, $value, $key);
|
||||
}
|
||||
|
||||
$variableTypes = array_map(fn ($t) => $this->getType($t), $genericTypes);
|
||||
|
||||
return Type::generic($type, ...array_filter($variableTypes));
|
||||
}
|
||||
|
||||
if ($docType instanceof Collection) {
|
||||
$fqsen = $docType->getFqsen();
|
||||
if ($fqsen && 'list' === $fqsen->getName() && !class_exists(List_::class, false) && !class_exists((string) $fqsen)) {
|
||||
// Workaround for phpdocumentor/type-resolver < 1.6
|
||||
return Type::list($this->getType($docType->getValueType()));
|
||||
}
|
||||
|
||||
[$phpType, $class] = $this->getPhpTypeAndClass((string) $fqsen);
|
||||
|
||||
$collection = is_a($class, \Traversable::class, true) || is_a($class, \ArrayAccess::class, true);
|
||||
|
||||
// it's safer to fall back to other extractors if the generic type is too abstract
|
||||
if (!$collection && !class_exists($class, false) && !interface_exists($class, false)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$type = null !== $class ? Type::object($class) : Type::builtin($phpType);
|
||||
|
||||
if ($collection) {
|
||||
$value = $this->getType($docType->getValueType());
|
||||
$key = $this->getType($docType->getKeyType());
|
||||
|
||||
return Type::collection($type, $value, $key);
|
||||
}
|
||||
|
||||
$variableTypes = [];
|
||||
|
||||
if (!$this->hasNoExplicitKeyType($docType) && null !== $keyType = $this->getType($docType->getKeyType())) {
|
||||
$variableTypes[] = $keyType;
|
||||
}
|
||||
|
||||
if (null !== $valueType = $this->getType($docType->getValueType())) {
|
||||
$variableTypes[] = $valueType;
|
||||
}
|
||||
|
||||
return Type::generic($type, ...$variableTypes);
|
||||
}
|
||||
|
||||
if (!$docTypeString) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($docType instanceof Array_ && $this->hasNoExplicitKeyType($docType) && str_starts_with($docTypeString, 'array<')) {
|
||||
return Type::list($this->getType($docType->getValueType()));
|
||||
}
|
||||
|
||||
if (str_ends_with($docTypeString, '[]') && $docType instanceof Array_) {
|
||||
return Type::list($this->getType($docType->getValueType()));
|
||||
}
|
||||
|
||||
if (str_starts_with($docTypeString, 'list<') && $docType instanceof Array_) {
|
||||
$collectionValueType = $this->getType($docType->getValueType());
|
||||
|
||||
return Type::list($collectionValueType);
|
||||
}
|
||||
|
||||
if (str_starts_with($docTypeString, 'array<') && $docType instanceof Array_) {
|
||||
// array<value> is converted to x[] which is handled above
|
||||
// so it's only necessary to handle array<key, value> here
|
||||
$collectionKeyType = $this->getType($docType->getKeyType());
|
||||
$collectionValueType = $this->getType($docType->getValueType());
|
||||
|
||||
return Type::array($collectionValueType, $collectionKeyType);
|
||||
}
|
||||
|
||||
$docTypeString = match ($docTypeString) {
|
||||
'integer' => 'int',
|
||||
'boolean' => 'bool',
|
||||
// real is not part of the PHPDoc standard, so we ignore it
|
||||
'double' => 'float',
|
||||
'callback' => 'callable',
|
||||
'void' => 'null',
|
||||
default => $docTypeString,
|
||||
};
|
||||
|
||||
[$phpType, $class] = $this->getPhpTypeAndClass($docTypeString);
|
||||
|
||||
if ('array' === $docTypeString) {
|
||||
return Type::array();
|
||||
}
|
||||
|
||||
if (null === $class) {
|
||||
return Type::builtin($phpType);
|
||||
}
|
||||
|
||||
if ($docType instanceof LegacyScalar || $docType instanceof Scalar) {
|
||||
return Type::object('scalar');
|
||||
}
|
||||
|
||||
if ($docType instanceof PseudoType) {
|
||||
if ($docType->underlyingType() instanceof Integer) {
|
||||
return Type::int();
|
||||
}
|
||||
|
||||
if ($docType->underlyingType() instanceof String_) {
|
||||
return Type::string();
|
||||
}
|
||||
|
||||
// It's safer to fall back to other extractors here, as resolving pseudo types correctly is not easy at the moment
|
||||
return null;
|
||||
}
|
||||
|
||||
return Type::object($class);
|
||||
}
|
||||
|
||||
private function normalizeType(string $docType): string
|
||||
{
|
||||
return match ($docType) {
|
||||
'integer' => 'int',
|
||||
'boolean' => 'bool',
|
||||
// real is not part of the PHPDoc standard, so we ignore it
|
||||
'double' => 'float',
|
||||
'callback' => 'callable',
|
||||
'void' => 'null',
|
||||
default => $docType,
|
||||
};
|
||||
}
|
||||
|
||||
private function hasNoExplicitKeyType(Array_|Collection $type): bool
|
||||
{
|
||||
if (method_exists($type, 'getOriginalKeyType')) {
|
||||
return null === $type->getOriginalKeyType();
|
||||
}
|
||||
|
||||
// Workaround for phpdocumentor/reflection-docblock < 6
|
||||
// "getOriginalKeyType()" doesn't exist, so we check if key type is Compound(string, int) which is the default.
|
||||
return $type->getKeyType() instanceof Compound;
|
||||
}
|
||||
|
||||
private function getPhpTypeAndClass(string $docType): array
|
||||
{
|
||||
if (\in_array($docType, TypeIdentifier::values(), true)) {
|
||||
return [$docType, null];
|
||||
}
|
||||
|
||||
if (\in_array($docType, ['parent', 'self', 'static'], true)) {
|
||||
return ['object', $docType];
|
||||
}
|
||||
|
||||
return ['object', ltrim($docType, '\\')];
|
||||
}
|
||||
}
|
||||
214
backend/vendor/symfony/property-info/Util/PhpStanTypeHelper.php
vendored
Normal file
214
backend/vendor/symfony/property-info/Util/PhpStanTypeHelper.php
vendored
Normal file
@@ -0,0 +1,214 @@
|
||||
<?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\Util;
|
||||
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\ArrayShapeNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\CallableTypeNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\CallableTypeParameterNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\ConstTypeNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\NullableTypeNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\ThisTypeNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\UnionTypeNode;
|
||||
use Symfony\Component\PropertyInfo\Type;
|
||||
use Symfony\Component\TypeInfo\TypeContext\TypeContext;
|
||||
|
||||
/**
|
||||
* Transforms a php doc tag value to a {@link Type} instance.
|
||||
*
|
||||
* @author Baptiste Leduc <baptiste.leduc@gmail.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class PhpStanTypeHelper
|
||||
{
|
||||
/**
|
||||
* Creates a {@see Type} from a PhpDocTagValueNode type.
|
||||
*
|
||||
* @return Type[]
|
||||
*/
|
||||
public function getTypes(PhpDocTagValueNode $node, TypeContext $typeContext): array
|
||||
{
|
||||
if ($node instanceof ParamTagValueNode || $node instanceof ReturnTagValueNode || $node instanceof VarTagValueNode) {
|
||||
return $this->compressNullableType($this->extractTypes($node->type, $typeContext));
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Because PhpStan extract null as a separated type when Symfony / PHP compress it in the first available type we
|
||||
* need this method to mimic how Symfony want null types.
|
||||
*
|
||||
* @param Type[] $types
|
||||
*
|
||||
* @return Type[]
|
||||
*/
|
||||
private function compressNullableType(array $types): array
|
||||
{
|
||||
$firstTypeIndex = null;
|
||||
$nullableTypeIndex = null;
|
||||
|
||||
foreach ($types as $k => $type) {
|
||||
if (null === $firstTypeIndex && Type::BUILTIN_TYPE_NULL !== $type->getBuiltinType() && !$type->isNullable()) {
|
||||
$firstTypeIndex = $k;
|
||||
}
|
||||
|
||||
if (null === $nullableTypeIndex && Type::BUILTIN_TYPE_NULL === $type->getBuiltinType()) {
|
||||
$nullableTypeIndex = $k;
|
||||
}
|
||||
|
||||
if (null !== $firstTypeIndex && null !== $nullableTypeIndex) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (null !== $firstTypeIndex && null !== $nullableTypeIndex) {
|
||||
$firstType = $types[$firstTypeIndex];
|
||||
$types[$firstTypeIndex] = new Type(
|
||||
$firstType->getBuiltinType(),
|
||||
true,
|
||||
$firstType->getClassName(),
|
||||
$firstType->isCollection(),
|
||||
$firstType->getCollectionKeyTypes(),
|
||||
$firstType->getCollectionValueTypes()
|
||||
);
|
||||
unset($types[$nullableTypeIndex]);
|
||||
}
|
||||
|
||||
return array_values($types);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Type[]
|
||||
*/
|
||||
private function extractTypes(TypeNode $node, TypeContext $typeContext): array
|
||||
{
|
||||
if ($node instanceof UnionTypeNode) {
|
||||
$types = [];
|
||||
foreach ($node->types as $type) {
|
||||
if ($type instanceof ConstTypeNode) {
|
||||
// It's safer to fall back to other extractors here, as resolving const types correctly is not easy at the moment
|
||||
return [];
|
||||
}
|
||||
foreach ($this->extractTypes($type, $typeContext) as $subType) {
|
||||
$types[] = $subType;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->compressNullableType($types);
|
||||
}
|
||||
if ($node instanceof GenericTypeNode) {
|
||||
if ('class-string' === $node->type->name) {
|
||||
return [new Type(Type::BUILTIN_TYPE_STRING)];
|
||||
}
|
||||
|
||||
[$mainType] = $this->extractTypes($node->type, $typeContext);
|
||||
|
||||
if (Type::BUILTIN_TYPE_INT === $mainType->getBuiltinType()) {
|
||||
return [$mainType];
|
||||
}
|
||||
|
||||
$collection = $mainType->isCollection() || is_a($mainType->getClassName(), \Traversable::class, true) || is_a($mainType->getClassName(), \ArrayAccess::class, true);
|
||||
|
||||
// it's safer to fall back to other extractors if the generic type is too abstract
|
||||
if (!$collection && !class_exists($mainType->getClassName()) && !interface_exists($mainType->getClassName(), false)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$collectionKeyTypes = $mainType->getCollectionKeyTypes();
|
||||
$collectionKeyValues = [];
|
||||
if (1 === \count($node->genericTypes)) {
|
||||
foreach ($this->extractTypes($node->genericTypes[0], $typeContext) as $subType) {
|
||||
$collectionKeyValues[] = $subType;
|
||||
}
|
||||
} elseif (2 === \count($node->genericTypes)) {
|
||||
foreach ($this->extractTypes($node->genericTypes[0], $typeContext) as $keySubType) {
|
||||
$collectionKeyTypes[] = $keySubType;
|
||||
}
|
||||
foreach ($this->extractTypes($node->genericTypes[1], $typeContext) as $valueSubType) {
|
||||
$collectionKeyValues[] = $valueSubType;
|
||||
}
|
||||
}
|
||||
|
||||
return [new Type($mainType->getBuiltinType(), $mainType->isNullable(), $mainType->getClassName(), $collection, $collectionKeyTypes, $collectionKeyValues)];
|
||||
}
|
||||
if ($node instanceof ArrayShapeNode) {
|
||||
return [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true)];
|
||||
}
|
||||
if ($node instanceof ArrayTypeNode) {
|
||||
return [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, [new Type(Type::BUILTIN_TYPE_INT)], $this->extractTypes($node->type, $typeContext))];
|
||||
}
|
||||
if ($node instanceof CallableTypeNode || $node instanceof CallableTypeParameterNode) {
|
||||
return [new Type(Type::BUILTIN_TYPE_CALLABLE)];
|
||||
}
|
||||
if ($node instanceof NullableTypeNode) {
|
||||
$subTypes = $this->extractTypes($node->type, $typeContext);
|
||||
if (\count($subTypes) > 1) {
|
||||
$subTypes[] = new Type(Type::BUILTIN_TYPE_NULL);
|
||||
|
||||
return $subTypes;
|
||||
}
|
||||
|
||||
return [new Type($subTypes[0]->getBuiltinType(), true, $subTypes[0]->getClassName(), $subTypes[0]->isCollection(), $subTypes[0]->getCollectionKeyTypes(), $subTypes[0]->getCollectionValueTypes())];
|
||||
}
|
||||
if ($node instanceof ThisTypeNode) {
|
||||
return [new Type(Type::BUILTIN_TYPE_OBJECT, false, $typeContext->getCalledClass())];
|
||||
}
|
||||
if ($node instanceof IdentifierTypeNode) {
|
||||
if (\in_array($node->name, Type::$builtinTypes, true)) {
|
||||
return [new Type($node->name, false, null, \in_array($node->name, Type::$builtinCollectionTypes, true))];
|
||||
}
|
||||
|
||||
return match ($node->name) {
|
||||
'integer',
|
||||
'positive-int',
|
||||
'negative-int',
|
||||
'non-positive-int',
|
||||
'non-negative-int',
|
||||
'non-zero-int' => [new Type(Type::BUILTIN_TYPE_INT)],
|
||||
'double' => [new Type(Type::BUILTIN_TYPE_FLOAT)],
|
||||
'list',
|
||||
'non-empty-list' => [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT))],
|
||||
'non-empty-array' => [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true)],
|
||||
'mixed' => [], // mixed seems to be ignored in all other extractors
|
||||
'parent' => [new Type(Type::BUILTIN_TYPE_OBJECT, false, $node->name)],
|
||||
'static',
|
||||
'self' => [new Type(Type::BUILTIN_TYPE_OBJECT, false, $typeContext->getCalledClass())],
|
||||
'class-string',
|
||||
'html-escaped-string',
|
||||
'lowercase-string',
|
||||
'non-empty-lowercase-string',
|
||||
'non-empty-string',
|
||||
'numeric-string',
|
||||
'trait-string',
|
||||
'interface-string',
|
||||
'literal-string' => [new Type(Type::BUILTIN_TYPE_STRING)],
|
||||
'void' => [new Type(Type::BUILTIN_TYPE_NULL)],
|
||||
'scalar' => [new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_FLOAT), new Type(Type::BUILTIN_TYPE_STRING), new Type(Type::BUILTIN_TYPE_BOOL)],
|
||||
'number' => [new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_FLOAT)],
|
||||
'numeric' => [new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_FLOAT), new Type(Type::BUILTIN_TYPE_STRING)],
|
||||
'array-key' => [new Type(Type::BUILTIN_TYPE_STRING), new Type(Type::BUILTIN_TYPE_INT)],
|
||||
default => [new Type(Type::BUILTIN_TYPE_OBJECT, false, $typeContext->normalize($node->name))],
|
||||
};
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
52
backend/vendor/symfony/property-info/composer.json
vendored
Normal file
52
backend/vendor/symfony/property-info/composer.json
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
{
|
||||
"name": "symfony/property-info",
|
||||
"type": "library",
|
||||
"description": "Extracts information about PHP class' properties using metadata of popular sources",
|
||||
"keywords": [
|
||||
"property",
|
||||
"type",
|
||||
"phpdoc",
|
||||
"symfony",
|
||||
"validator",
|
||||
"doctrine"
|
||||
],
|
||||
"homepage": "https://symfony.com",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Kévin Dunglas",
|
||||
"email": "dunglas@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=8.2",
|
||||
"symfony/deprecation-contracts": "^2.5|^3",
|
||||
"symfony/string": "^6.4|^7.0|^8.0",
|
||||
"symfony/type-info": "^7.4.7|^8.0.7"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/serializer": "^6.4|^7.0|^8.0",
|
||||
"symfony/cache": "^6.4|^7.0|^8.0",
|
||||
"symfony/dependency-injection": "^6.4|^7.0|^8.0",
|
||||
"phpdocumentor/reflection-docblock": "^5.2|^6.0",
|
||||
"phpstan/phpdoc-parser": "^1.0|^2.0"
|
||||
},
|
||||
"conflict": {
|
||||
"phpdocumentor/reflection-docblock": "<5.2|>=7",
|
||||
"phpdocumentor/type-resolver": "<1.5.1",
|
||||
"symfony/dependency-injection": "<6.4",
|
||||
"symfony/cache": "<6.4",
|
||||
"symfony/serializer": "<6.4"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": { "Symfony\\Component\\PropertyInfo\\": "" },
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
},
|
||||
"minimum-stability": "dev"
|
||||
}
|
||||
Reference in New Issue
Block a user