login consent app sql
This commit is contained in:
39
vendor/symfony/property-info/CHANGELOG.md
vendored
Normal file
39
vendor/symfony/property-info/CHANGELOG.md
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
CHANGELOG
|
||||
=========
|
||||
|
||||
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`
|
54
vendor/symfony/property-info/DependencyInjection/PropertyInfoConstructorPass.php
vendored
Normal file
54
vendor/symfony/property-info/DependencyInjection/PropertyInfoConstructorPass.php
vendored
Normal file
@ -0,0 +1,54 @@
|
||||
<?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;
|
||||
|
||||
private $service;
|
||||
private $tag;
|
||||
|
||||
public function __construct(string $service = 'property_info.constructor_extractor', string $tag = 'property_info.constructor_extractor')
|
||||
{
|
||||
if (0 < \func_num_args()) {
|
||||
trigger_deprecation('symfony/property-info', '5.3', 'Configuring "%s" is deprecated.', __CLASS__);
|
||||
}
|
||||
|
||||
$this->service = $service;
|
||||
$this->tag = $tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function process(ContainerBuilder $container)
|
||||
{
|
||||
if (!$container->hasDefinition($this->service)) {
|
||||
return;
|
||||
}
|
||||
$definition = $container->getDefinition($this->service);
|
||||
|
||||
$listExtractors = $this->findAndSortTaggedServices($this->tag, $container);
|
||||
$definition->replaceArgument(0, new IteratorArgument($listExtractors));
|
||||
}
|
||||
}
|
75
vendor/symfony/property-info/DependencyInjection/PropertyInfoPass.php
vendored
Normal file
75
vendor/symfony/property-info/DependencyInjection/PropertyInfoPass.php
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
<?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;
|
||||
|
||||
private $propertyInfoService;
|
||||
private $listExtractorTag;
|
||||
private $typeExtractorTag;
|
||||
private $descriptionExtractorTag;
|
||||
private $accessExtractorTag;
|
||||
private $initializableExtractorTag;
|
||||
|
||||
public function __construct(string $propertyInfoService = 'property_info', string $listExtractorTag = 'property_info.list_extractor', string $typeExtractorTag = 'property_info.type_extractor', string $descriptionExtractorTag = 'property_info.description_extractor', string $accessExtractorTag = 'property_info.access_extractor', string $initializableExtractorTag = 'property_info.initializable_extractor')
|
||||
{
|
||||
if (0 < \func_num_args()) {
|
||||
trigger_deprecation('symfony/property-info', '5.3', 'Configuring "%s" is deprecated.', __CLASS__);
|
||||
}
|
||||
|
||||
$this->propertyInfoService = $propertyInfoService;
|
||||
$this->listExtractorTag = $listExtractorTag;
|
||||
$this->typeExtractorTag = $typeExtractorTag;
|
||||
$this->descriptionExtractorTag = $descriptionExtractorTag;
|
||||
$this->accessExtractorTag = $accessExtractorTag;
|
||||
$this->initializableExtractorTag = $initializableExtractorTag;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function process(ContainerBuilder $container)
|
||||
{
|
||||
if (!$container->hasDefinition($this->propertyInfoService)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$definition = $container->getDefinition($this->propertyInfoService);
|
||||
|
||||
$listExtractors = $this->findAndSortTaggedServices($this->listExtractorTag, $container);
|
||||
$definition->replaceArgument(0, new IteratorArgument($listExtractors));
|
||||
|
||||
$typeExtractors = $this->findAndSortTaggedServices($this->typeExtractorTag, $container);
|
||||
$definition->replaceArgument(1, new IteratorArgument($typeExtractors));
|
||||
|
||||
$descriptionExtractors = $this->findAndSortTaggedServices($this->descriptionExtractorTag, $container);
|
||||
$definition->replaceArgument(2, new IteratorArgument($descriptionExtractors));
|
||||
|
||||
$accessExtractors = $this->findAndSortTaggedServices($this->accessExtractorTag, $container);
|
||||
$definition->replaceArgument(3, new IteratorArgument($accessExtractors));
|
||||
|
||||
$initializableExtractors = $this->findAndSortTaggedServices($this->initializableExtractorTag, $container);
|
||||
$definition->setArgument(4, new IteratorArgument($initializableExtractors));
|
||||
}
|
||||
}
|
33
vendor/symfony/property-info/Extractor/ConstructorArgumentTypeExtractorInterface.php
vendored
Normal file
33
vendor/symfony/property-info/Extractor/ConstructorArgumentTypeExtractorInterface.php
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* Infers the constructor argument type.
|
||||
*
|
||||
* @author Dmitrii Poddubnyi <dpoddubny@gmail.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
interface ConstructorArgumentTypeExtractorInterface
|
||||
{
|
||||
/**
|
||||
* Gets types of an argument from constructor.
|
||||
*
|
||||
* @return Type[]|null
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function getTypesFromConstructor(string $class, string $property): ?array;
|
||||
}
|
47
vendor/symfony/property-info/Extractor/ConstructorExtractor.php
vendored
Normal file
47
vendor/symfony/property-info/Extractor/ConstructorExtractor.php
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* Extracts the constructor argument type using ConstructorArgumentTypeExtractorInterface implementations.
|
||||
*
|
||||
* @author Dmitrii Poddubnyi <dpoddubny@gmail.com>
|
||||
*/
|
||||
final class ConstructorExtractor implements PropertyTypeExtractorInterface
|
||||
{
|
||||
private $extractors;
|
||||
|
||||
/**
|
||||
* @param iterable<int, ConstructorArgumentTypeExtractorInterface> $extractors
|
||||
*/
|
||||
public function __construct(iterable $extractors = [])
|
||||
{
|
||||
$this->extractors = $extractors;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTypes(string $class, string $property, array $context = []): ?array
|
||||
{
|
||||
foreach ($this->extractors as $extractor) {
|
||||
$value = $extractor->getTypesFromConstructor($class, $property);
|
||||
if (null !== $value) {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
360
vendor/symfony/property-info/Extractor/PhpDocExtractor.php
vendored
Normal file
360
vendor/symfony/property-info/Extractor/PhpDocExtractor.php
vendored
Normal file
@ -0,0 +1,360 @@
|
||||
<?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\PropertyTypeExtractorInterface;
|
||||
use Symfony\Component\PropertyInfo\Type;
|
||||
use Symfony\Component\PropertyInfo\Util\PhpDocTypeHelper;
|
||||
|
||||
/**
|
||||
* Extracts data using a PHPDoc parser.
|
||||
*
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
class PhpDocExtractor implements PropertyDescriptionExtractorInterface, PropertyTypeExtractorInterface, ConstructorArgumentTypeExtractorInterface
|
||||
{
|
||||
public const PROPERTY = 0;
|
||||
public const ACCESSOR = 1;
|
||||
public const MUTATOR = 2;
|
||||
|
||||
/**
|
||||
* @var array<string, array{DocBlock|null, int|null, string|null}>
|
||||
*/
|
||||
private $docBlocks = [];
|
||||
|
||||
/**
|
||||
* @var Context[]
|
||||
*/
|
||||
private $contexts = [];
|
||||
|
||||
private $docBlockFactory;
|
||||
private $contextFactory;
|
||||
private $phpDocTypeHelper;
|
||||
private $mutatorPrefixes;
|
||||
private $accessorPrefixes;
|
||||
private $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.', __CLASS__));
|
||||
}
|
||||
|
||||
$this->docBlockFactory = $docBlockFactory ?: DocBlockFactory::createInstance();
|
||||
$this->contextFactory = new ContextFactory();
|
||||
$this->phpDocTypeHelper = new PhpDocTypeHelper();
|
||||
$this->mutatorPrefixes = $mutatorPrefixes ?? ReflectionExtractor::$defaultMutatorPrefixes;
|
||||
$this->accessorPrefixes = $accessorPrefixes ?? ReflectionExtractor::$defaultAccessorPrefixes;
|
||||
$this->arrayMutatorPrefixes = $arrayMutatorPrefixes ?? ReflectionExtractor::$defaultArrayMutatorPrefixes;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getShortDescription(string $class, string $property, array $context = []): ?string
|
||||
{
|
||||
/** @var $docBlock DocBlock */
|
||||
[$docBlock] = $this->getDocBlock($class, $property);
|
||||
if (!$docBlock) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$shortDescription = $docBlock->getSummary();
|
||||
|
||||
if (!empty($shortDescription)) {
|
||||
return $shortDescription;
|
||||
}
|
||||
|
||||
foreach ($docBlock->getTagsByName('var') as $var) {
|
||||
if ($var && !$var instanceof InvalidTag) {
|
||||
$varDescription = $var->getDescription()->render();
|
||||
|
||||
if (!empty($varDescription)) {
|
||||
return $varDescription;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getLongDescription(string $class, string $property, array $context = []): ?string
|
||||
{
|
||||
/** @var $docBlock DocBlock */
|
||||
[$docBlock] = $this->getDocBlock($class, $property);
|
||||
if (!$docBlock) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$contents = $docBlock->getDescription()->render();
|
||||
|
||||
return '' === $contents ? null : $contents;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTypes(string $class, string $property, array $context = []): ?array
|
||||
{
|
||||
/** @var $docBlock DocBlock */
|
||||
[$docBlock, $source, $prefix] = $this->getDocBlock($class, $property);
|
||||
if (!$docBlock) {
|
||||
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 = [];
|
||||
/** @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':
|
||||
case 'static':
|
||||
$resolvedClass = $class;
|
||||
break;
|
||||
|
||||
case 'parent':
|
||||
if (false !== $resolvedClass = $parentClass ?? $parentClass = get_parent_class($class)) {
|
||||
break;
|
||||
}
|
||||
// no break
|
||||
|
||||
default:
|
||||
$types[] = $type;
|
||||
continue 2;
|
||||
}
|
||||
|
||||
$types[] = new Type(Type::BUILTIN_TYPE_OBJECT, $type->isNullable(), $resolvedClass, $type->isCollection(), $type->getCollectionKeyTypes(), $type->getCollectionValueTypes());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($types[0])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!\in_array($prefix, $this->arrayMutatorPrefixes)) {
|
||||
return $types;
|
||||
}
|
||||
|
||||
return [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), $types[0])];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTypesFromConstructor(string $class, string $property): ?array
|
||||
{
|
||||
$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])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return array_merge([], ...$types);
|
||||
}
|
||||
|
||||
private function getDocBlockFromConstructor(string $class, string $property): ?DocBlock
|
||||
{
|
||||
try {
|
||||
$reflectionClass = new \ReflectionClass($class);
|
||||
} catch (\ReflectionException $e) {
|
||||
return null;
|
||||
}
|
||||
$reflectionConstructor = $reflectionClass->getConstructor();
|
||||
if (!$reflectionConstructor) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
$docBlock = $this->docBlockFactory->create($reflectionConstructor, $this->contextFactory->createFromReflector($reflectionConstructor));
|
||||
|
||||
return $this->filterDocBlockParams($docBlock, $property);
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private function filterDocBlockParams(DocBlock $docBlock, string $allowedParam): DocBlock
|
||||
{
|
||||
$tags = array_values(array_filter($docBlock->getTagsByName('param'), function ($tag) use ($allowedParam) {
|
||||
return $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}
|
||||
*/
|
||||
private function getDocBlock(string $class, string $property): array
|
||||
{
|
||||
$propertyHash = sprintf('%s::%s', $class, $property);
|
||||
|
||||
if (isset($this->docBlocks[$propertyHash])) {
|
||||
return $this->docBlocks[$propertyHash];
|
||||
}
|
||||
|
||||
$ucFirstProperty = ucfirst($property);
|
||||
|
||||
switch (true) {
|
||||
case $docBlock = $this->getDocBlockFromProperty($class, $property):
|
||||
$data = [$docBlock, self::PROPERTY, null];
|
||||
break;
|
||||
|
||||
case [$docBlock] = $this->getDocBlockFromMethod($class, $ucFirstProperty, self::ACCESSOR):
|
||||
$data = [$docBlock, self::ACCESSOR, null];
|
||||
break;
|
||||
|
||||
case [$docBlock, $prefix] = $this->getDocBlockFromMethod($class, $ucFirstProperty, self::MUTATOR):
|
||||
$data = [$docBlock, self::MUTATOR, $prefix];
|
||||
break;
|
||||
|
||||
default:
|
||||
$data = [null, null, null];
|
||||
}
|
||||
|
||||
return $this->docBlocks[$propertyHash] = $data;
|
||||
}
|
||||
|
||||
private function getDocBlockFromProperty(string $class, string $property): ?DocBlock
|
||||
{
|
||||
// Use a ReflectionProperty instead of $class to get the parent class if applicable
|
||||
try {
|
||||
$reflectionProperty = new \ReflectionProperty($class, $property);
|
||||
} catch (\ReflectionException $e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$reflector = $reflectionProperty->getDeclaringClass();
|
||||
|
||||
foreach ($reflector->getTraits() as $trait) {
|
||||
if ($trait->hasProperty($property)) {
|
||||
return $this->getDocBlockFromProperty($trait->getName(), $property);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
return $this->docBlockFactory->create($reflectionProperty, $this->createFromReflector($reflector));
|
||||
} catch (\InvalidArgumentException|\RuntimeException $e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{DocBlock, string}|null
|
||||
*/
|
||||
private function getDocBlockFromMethod(string $class, string $ucFirstProperty, int $type): ?array
|
||||
{
|
||||
$prefixes = self::ACCESSOR === $type ? $this->accessorPrefixes : $this->mutatorPrefixes;
|
||||
$prefix = null;
|
||||
|
||||
foreach ($prefixes as $prefix) {
|
||||
$methodName = $prefix.$ucFirstProperty;
|
||||
|
||||
try {
|
||||
$reflectionMethod = new \ReflectionMethod($class, $methodName);
|
||||
if ($reflectionMethod->isStatic()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
(self::ACCESSOR === $type && 0 === $reflectionMethod->getNumberOfRequiredParameters()) ||
|
||||
(self::MUTATOR === $type && $reflectionMethod->getNumberOfParameters() >= 1)
|
||||
) {
|
||||
break;
|
||||
}
|
||||
} catch (\ReflectionException $e) {
|
||||
// Try the next prefix if the method doesn't exist
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($reflectionMethod)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$reflector = $reflectionMethod->getDeclaringClass();
|
||||
|
||||
foreach ($reflector->getTraits() as $trait) {
|
||||
if ($trait->hasMethod($methodName)) {
|
||||
return $this->getDocBlockFromMethod($trait->getName(), $ucFirstProperty, $type);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
return [$this->docBlockFactory->create($reflectionMethod, $this->createFromReflector($reflector)), $prefix];
|
||||
} catch (\InvalidArgumentException|\RuntimeException $e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevents a lot of redundant calls to ContextFactory::createForNamespace().
|
||||
*/
|
||||
private function createFromReflector(\ReflectionClass $reflector): Context
|
||||
{
|
||||
$cacheKey = $reflector->getNamespaceName().':'.$reflector->getFileName();
|
||||
|
||||
if (isset($this->contexts[$cacheKey])) {
|
||||
return $this->contexts[$cacheKey];
|
||||
}
|
||||
|
||||
$this->contexts[$cacheKey] = $this->contextFactory->createFromReflector($reflector);
|
||||
|
||||
return $this->contexts[$cacheKey];
|
||||
}
|
||||
}
|
277
vendor/symfony/property-info/Extractor/PhpStanExtractor.php
vendored
Normal file
277
vendor/symfony/property-info/Extractor/PhpStanExtractor.php
vendored
Normal file
@ -0,0 +1,277 @@
|
||||
<?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 PHPStan\PhpDocParser\Ast\PhpDoc\InvalidTagValueNode;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocNode;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
|
||||
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 Symfony\Component\PropertyInfo\PhpStan\NameScopeFactory;
|
||||
use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
|
||||
use Symfony\Component\PropertyInfo\Type;
|
||||
use Symfony\Component\PropertyInfo\Util\PhpStanTypeHelper;
|
||||
|
||||
/**
|
||||
* Extracts data using PHPStan parser.
|
||||
*
|
||||
* @author Baptiste Leduc <baptiste.leduc@gmail.com>
|
||||
*/
|
||||
final class PhpStanExtractor implements PropertyTypeExtractorInterface, ConstructorArgumentTypeExtractorInterface
|
||||
{
|
||||
private const PROPERTY = 0;
|
||||
private const ACCESSOR = 1;
|
||||
private const MUTATOR = 2;
|
||||
|
||||
/** @var PhpDocParser */
|
||||
private $phpDocParser;
|
||||
|
||||
/** @var Lexer */
|
||||
private $lexer;
|
||||
|
||||
/** @var NameScopeFactory */
|
||||
private $nameScopeFactory;
|
||||
|
||||
/** @var array<string, array{PhpDocNode|null, int|null, string|null, string|null}> */
|
||||
private $docBlocks = [];
|
||||
private $phpStanTypeHelper;
|
||||
private $mutatorPrefixes;
|
||||
private $accessorPrefixes;
|
||||
private $arrayMutatorPrefixes;
|
||||
|
||||
/**
|
||||
* @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)
|
||||
{
|
||||
$this->phpStanTypeHelper = new PhpStanTypeHelper();
|
||||
$this->mutatorPrefixes = $mutatorPrefixes ?? ReflectionExtractor::$defaultMutatorPrefixes;
|
||||
$this->accessorPrefixes = $accessorPrefixes ?? ReflectionExtractor::$defaultAccessorPrefixes;
|
||||
$this->arrayMutatorPrefixes = $arrayMutatorPrefixes ?? ReflectionExtractor::$defaultArrayMutatorPrefixes;
|
||||
|
||||
$this->phpDocParser = new PhpDocParser(new TypeParser(new ConstExprParser()), new ConstExprParser());
|
||||
$this->lexer = new Lexer();
|
||||
$this->nameScopeFactory = new NameScopeFactory();
|
||||
}
|
||||
|
||||
public function getTypes(string $class, string $property, array $context = []): ?array
|
||||
{
|
||||
/** @var PhpDocNode|null $docNode */
|
||||
[$docNode, $source, $prefix, $declaringClass] = $this->getDocBlock($class, $property);
|
||||
$nameScope = $this->nameScopeFactory->create($class, $declaringClass);
|
||||
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;
|
||||
}
|
||||
|
||||
foreach ($this->phpStanTypeHelper->getTypes($tagDocNode->value, $nameScope) as $type) {
|
||||
switch ($type->getClassName()) {
|
||||
case 'self':
|
||||
case 'static':
|
||||
$resolvedClass = $class;
|
||||
break;
|
||||
|
||||
case 'parent':
|
||||
if (false !== $resolvedClass = $parentClass ?? $parentClass = get_parent_class($class)) {
|
||||
break;
|
||||
}
|
||||
// no break
|
||||
|
||||
default:
|
||||
$types[] = $type;
|
||||
continue 2;
|
||||
}
|
||||
|
||||
$types[] = new Type(Type::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 Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), $types[0])];
|
||||
}
|
||||
|
||||
public function getTypesFromConstructor(string $class, string $property): ?array
|
||||
{
|
||||
if (null === $tagDocNode = $this->getDocBlockFromConstructor($class, $property)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$types = [];
|
||||
foreach ($this->phpStanTypeHelper->getTypes($tagDocNode, $this->nameScopeFactory->create($class)) as $type) {
|
||||
$types[] = $type;
|
||||
}
|
||||
|
||||
if (!isset($types[0])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $types;
|
||||
}
|
||||
|
||||
private function getDocBlockFromConstructor(string $class, string $property): ?ParamTagValueNode
|
||||
{
|
||||
try {
|
||||
$reflectionClass = new \ReflectionClass($class);
|
||||
} catch (\ReflectionException $e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (null === $reflectionConstructor = $reflectionClass->getConstructor()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$rawDocNode = $reflectionConstructor->getDocComment();
|
||||
$tokens = new TokenIterator($this->lexer->tokenize($rawDocNode));
|
||||
$phpDocNode = $this->phpDocParser->parse($tokens);
|
||||
$tokens->consumeTokenType(Lexer::TOKEN_END);
|
||||
|
||||
return $this->filterDocBlockParams($phpDocNode, $property);
|
||||
}
|
||||
|
||||
private function filterDocBlockParams(PhpDocNode $docNode, string $allowedParam): ?ParamTagValueNode
|
||||
{
|
||||
$tags = array_values(array_filter($docNode->getTagsByName('@param'), function ($tagNode) use ($allowedParam) {
|
||||
return $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, $declaringClass] = $this->getDocBlockFromProperty($class, $property)) {
|
||||
$data = [$docBlock, self::PROPERTY, 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, 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 $e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (null === $rawDocNode = $reflectionProperty->getDocComment() ?: null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$tokens = new TokenIterator($this->lexer->tokenize($rawDocNode));
|
||||
$phpDocNode = $this->phpDocParser->parse($tokens);
|
||||
$tokens->consumeTokenType(Lexer::TOKEN_END);
|
||||
|
||||
return [$phpDocNode, $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;
|
||||
|
||||
foreach ($prefixes as $prefix) {
|
||||
$methodName = $prefix.$ucFirstProperty;
|
||||
|
||||
try {
|
||||
$reflectionMethod = new \ReflectionMethod($class, $methodName);
|
||||
if ($reflectionMethod->isStatic()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
(self::ACCESSOR === $type && 0 === $reflectionMethod->getNumberOfRequiredParameters())
|
||||
|| (self::MUTATOR === $type && $reflectionMethod->getNumberOfParameters() >= 1)
|
||||
) {
|
||||
break;
|
||||
}
|
||||
} catch (\ReflectionException $e) {
|
||||
// Try the next prefix if the method doesn't exist
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($reflectionMethod)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (null === $rawDocNode = $reflectionMethod->getDocComment() ?: null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$tokens = new TokenIterator($this->lexer->tokenize($rawDocNode));
|
||||
$phpDocNode = $this->phpDocParser->parse($tokens);
|
||||
$tokens->consumeTokenType(Lexer::TOKEN_END);
|
||||
|
||||
return [$phpDocNode, $prefix, $reflectionMethod->class];
|
||||
}
|
||||
}
|
872
vendor/symfony/property-info/Extractor/ReflectionExtractor.php
vendored
Normal file
872
vendor/symfony/property-info/Extractor/ReflectionExtractor.php
vendored
Normal file
@ -0,0 +1,872 @@
|
||||
<?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\PropertyAccessExtractorInterface;
|
||||
use Symfony\Component\PropertyInfo\PropertyInitializableExtractorInterface;
|
||||
use Symfony\Component\PropertyInfo\PropertyListExtractorInterface;
|
||||
use Symfony\Component\PropertyInfo\PropertyReadInfo;
|
||||
use Symfony\Component\PropertyInfo\PropertyReadInfoExtractorInterface;
|
||||
use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
|
||||
use Symfony\Component\PropertyInfo\PropertyWriteInfo;
|
||||
use Symfony\Component\PropertyInfo\PropertyWriteInfoExtractorInterface;
|
||||
use Symfony\Component\PropertyInfo\Type;
|
||||
use Symfony\Component\String\Inflector\EnglishInflector;
|
||||
use Symfony\Component\String\Inflector\InflectorInterface;
|
||||
|
||||
/**
|
||||
* Extracts data using the reflection API.
|
||||
*
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
class ReflectionExtractor implements PropertyListExtractorInterface, PropertyTypeExtractorInterface, PropertyAccessExtractorInterface, PropertyInitializableExtractorInterface, PropertyReadInfoExtractorInterface, PropertyWriteInfoExtractorInterface, ConstructorArgumentTypeExtractorInterface
|
||||
{
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public static $defaultMutatorPrefixes = ['add', 'remove', 'set'];
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public static $defaultAccessorPrefixes = ['get', 'is', 'has', 'can'];
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public static $defaultArrayMutatorPrefixes = ['add', 'remove'];
|
||||
|
||||
public const ALLOW_PRIVATE = 1;
|
||||
public const ALLOW_PROTECTED = 2;
|
||||
public const ALLOW_PUBLIC = 4;
|
||||
|
||||
/** @var int Allow none of the magic methods */
|
||||
public const DISALLOW_MAGIC_METHODS = 0;
|
||||
/** @var int Allow magic __get methods */
|
||||
public const ALLOW_MAGIC_GET = 1 << 0;
|
||||
/** @var int Allow magic __set methods */
|
||||
public const ALLOW_MAGIC_SET = 1 << 1;
|
||||
/** @var int Allow magic __call methods */
|
||||
public const ALLOW_MAGIC_CALL = 1 << 2;
|
||||
|
||||
private const MAP_TYPES = [
|
||||
'integer' => Type::BUILTIN_TYPE_INT,
|
||||
'boolean' => Type::BUILTIN_TYPE_BOOL,
|
||||
'double' => Type::BUILTIN_TYPE_FLOAT,
|
||||
];
|
||||
|
||||
private $mutatorPrefixes;
|
||||
private $accessorPrefixes;
|
||||
private $arrayMutatorPrefixes;
|
||||
private $enableConstructorExtraction;
|
||||
private $methodReflectionFlags;
|
||||
private $magicMethodsFlags;
|
||||
private $propertyReflectionFlags;
|
||||
private $inflector;
|
||||
|
||||
private $arrayMutatorPrefixesFirst;
|
||||
private $arrayMutatorPrefixesLast;
|
||||
|
||||
/**
|
||||
* @param string[]|null $mutatorPrefixes
|
||||
* @param string[]|null $accessorPrefixes
|
||||
* @param string[]|null $arrayMutatorPrefixes
|
||||
*/
|
||||
public function __construct(array $mutatorPrefixes = null, array $accessorPrefixes = null, array $arrayMutatorPrefixes = null, bool $enableConstructorExtraction = true, int $accessFlags = self::ALLOW_PUBLIC, InflectorInterface $inflector = null, int $magicMethodsFlags = self::ALLOW_MAGIC_GET | self::ALLOW_MAGIC_SET)
|
||||
{
|
||||
$this->mutatorPrefixes = $mutatorPrefixes ?? self::$defaultMutatorPrefixes;
|
||||
$this->accessorPrefixes = $accessorPrefixes ?? self::$defaultAccessorPrefixes;
|
||||
$this->arrayMutatorPrefixes = $arrayMutatorPrefixes ?? self::$defaultArrayMutatorPrefixes;
|
||||
$this->enableConstructorExtraction = $enableConstructorExtraction;
|
||||
$this->methodReflectionFlags = $this->getMethodsFlags($accessFlags);
|
||||
$this->propertyReflectionFlags = $this->getPropertyFlags($accessFlags);
|
||||
$this->magicMethodsFlags = $magicMethodsFlags;
|
||||
$this->inflector = $inflector ?? new EnglishInflector();
|
||||
|
||||
$this->arrayMutatorPrefixesFirst = array_merge($this->arrayMutatorPrefixes, array_diff($this->mutatorPrefixes, $this->arrayMutatorPrefixes));
|
||||
$this->arrayMutatorPrefixesLast = array_reverse($this->arrayMutatorPrefixesFirst);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getProperties(string $class, array $context = []): ?array
|
||||
{
|
||||
try {
|
||||
$reflectionClass = new \ReflectionClass($class);
|
||||
} catch (\ReflectionException $e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$reflectionProperties = $reflectionClass->getProperties();
|
||||
|
||||
$properties = [];
|
||||
foreach ($reflectionProperties as $reflectionProperty) {
|
||||
if ($reflectionProperty->getModifiers() & $this->propertyReflectionFlags) {
|
||||
$properties[$reflectionProperty->name] = $reflectionProperty->name;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($reflectionClass->getMethods($this->methodReflectionFlags) as $reflectionMethod) {
|
||||
if ($reflectionMethod->isStatic()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$propertyName = $this->getPropertyName($reflectionMethod->name, $reflectionProperties);
|
||||
if (!$propertyName || isset($properties[$propertyName])) {
|
||||
continue;
|
||||
}
|
||||
if ($reflectionClass->hasProperty($lowerCasedPropertyName = lcfirst($propertyName)) || (!$reflectionClass->hasProperty($propertyName) && !preg_match('/^[A-Z]{2,}/', $propertyName))) {
|
||||
$propertyName = $lowerCasedPropertyName;
|
||||
}
|
||||
$properties[$propertyName] = $propertyName;
|
||||
}
|
||||
|
||||
return $properties ? array_values($properties) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTypes(string $class, string $property, array $context = []): ?array
|
||||
{
|
||||
if ($fromMutator = $this->extractFromMutator($class, $property)) {
|
||||
return $fromMutator;
|
||||
}
|
||||
|
||||
if ($fromAccessor = $this->extractFromAccessor($class, $property)) {
|
||||
return $fromAccessor;
|
||||
}
|
||||
|
||||
if (
|
||||
($context['enable_constructor_extraction'] ?? $this->enableConstructorExtraction) &&
|
||||
$fromConstructor = $this->extractFromConstructor($class, $property)
|
||||
) {
|
||||
return $fromConstructor;
|
||||
}
|
||||
|
||||
if ($fromPropertyDeclaration = $this->extractFromPropertyDeclaration($class, $property)) {
|
||||
return $fromPropertyDeclaration;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTypesFromConstructor(string $class, string $property): ?array
|
||||
{
|
||||
try {
|
||||
$reflection = new \ReflectionClass($class);
|
||||
} catch (\ReflectionException $e) {
|
||||
return null;
|
||||
}
|
||||
if (!$reflectionConstructor = $reflection->getConstructor()) {
|
||||
return null;
|
||||
}
|
||||
if (!$reflectionParameter = $this->getReflectionParameterFromConstructor($property, $reflectionConstructor)) {
|
||||
return null;
|
||||
}
|
||||
if (!$reflectionType = $reflectionParameter->getType()) {
|
||||
return null;
|
||||
}
|
||||
if (!$types = $this->extractFromReflectionType($reflectionType, $reflectionConstructor->getDeclaringClass())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $types;
|
||||
}
|
||||
|
||||
private function getReflectionParameterFromConstructor(string $property, \ReflectionMethod $reflectionConstructor): ?\ReflectionParameter
|
||||
{
|
||||
$reflectionParameter = null;
|
||||
foreach ($reflectionConstructor->getParameters() as $reflectionParameter) {
|
||||
if ($reflectionParameter->getName() === $property) {
|
||||
return $reflectionParameter;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isReadable(string $class, string $property, array $context = []): ?bool
|
||||
{
|
||||
if ($this->isAllowedProperty($class, $property)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return null !== $this->getReadInfo($class, $property, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isWritable(string $class, string $property, array $context = []): ?bool
|
||||
{
|
||||
if ($this->isAllowedProperty($class, $property)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
[$reflectionMethod] = $this->getMutatorMethod($class, $property);
|
||||
|
||||
return null !== $reflectionMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isInitializable(string $class, string $property, array $context = []): ?bool
|
||||
{
|
||||
try {
|
||||
$reflectionClass = new \ReflectionClass($class);
|
||||
} catch (\ReflectionException $e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!$reflectionClass->isInstantiable()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($constructor = $reflectionClass->getConstructor()) {
|
||||
foreach ($constructor->getParameters() as $parameter) {
|
||||
if ($property === $parameter->name) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} elseif ($parentClass = $reflectionClass->getParentClass()) {
|
||||
return $this->isInitializable($parentClass->getName(), $property);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getReadInfo(string $class, string $property, array $context = []): ?PropertyReadInfo
|
||||
{
|
||||
try {
|
||||
$reflClass = new \ReflectionClass($class);
|
||||
} catch (\ReflectionException $e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$allowGetterSetter = $context['enable_getter_setter_extraction'] ?? false;
|
||||
$magicMethods = $context['enable_magic_methods_extraction'] ?? $this->magicMethodsFlags;
|
||||
$allowMagicCall = (bool) ($magicMethods & self::ALLOW_MAGIC_CALL);
|
||||
$allowMagicGet = (bool) ($magicMethods & self::ALLOW_MAGIC_GET);
|
||||
|
||||
if (isset($context['enable_magic_call_extraction'])) {
|
||||
trigger_deprecation('symfony/property-info', '5.2', 'Using the "enable_magic_call_extraction" context option in "%s()" is deprecated. Use "enable_magic_methods_extraction" instead.', __METHOD__);
|
||||
|
||||
$allowMagicCall = $context['enable_magic_call_extraction'] ?? false;
|
||||
}
|
||||
|
||||
$hasProperty = $reflClass->hasProperty($property);
|
||||
$camelProp = $this->camelize($property);
|
||||
$getsetter = lcfirst($camelProp); // jQuery style, e.g. read: last(), write: last($item)
|
||||
|
||||
foreach ($this->accessorPrefixes as $prefix) {
|
||||
$methodName = $prefix.$camelProp;
|
||||
|
||||
if ($reflClass->hasMethod($methodName) && $reflClass->getMethod($methodName)->getModifiers() & $this->methodReflectionFlags && !$reflClass->getMethod($methodName)->getNumberOfRequiredParameters()) {
|
||||
$method = $reflClass->getMethod($methodName);
|
||||
|
||||
return new PropertyReadInfo(PropertyReadInfo::TYPE_METHOD, $methodName, $this->getReadVisiblityForMethod($method), $method->isStatic(), false);
|
||||
}
|
||||
}
|
||||
|
||||
if ($allowGetterSetter && $reflClass->hasMethod($getsetter) && ($reflClass->getMethod($getsetter)->getModifiers() & $this->methodReflectionFlags)) {
|
||||
$method = $reflClass->getMethod($getsetter);
|
||||
|
||||
return new PropertyReadInfo(PropertyReadInfo::TYPE_METHOD, $getsetter, $this->getReadVisiblityForMethod($method), $method->isStatic(), false);
|
||||
}
|
||||
|
||||
if ($allowMagicGet && $reflClass->hasMethod('__get') && ($reflClass->getMethod('__get')->getModifiers() & $this->methodReflectionFlags)) {
|
||||
return new PropertyReadInfo(PropertyReadInfo::TYPE_PROPERTY, $property, PropertyReadInfo::VISIBILITY_PUBLIC, false, false);
|
||||
}
|
||||
|
||||
if ($hasProperty && ($reflClass->getProperty($property)->getModifiers() & $this->propertyReflectionFlags)) {
|
||||
$reflProperty = $reflClass->getProperty($property);
|
||||
|
||||
return new PropertyReadInfo(PropertyReadInfo::TYPE_PROPERTY, $property, $this->getReadVisiblityForProperty($reflProperty), $reflProperty->isStatic(), true);
|
||||
}
|
||||
|
||||
if ($allowMagicCall && $reflClass->hasMethod('__call') && ($reflClass->getMethod('__call')->getModifiers() & $this->methodReflectionFlags)) {
|
||||
return new PropertyReadInfo(PropertyReadInfo::TYPE_METHOD, 'get'.$camelProp, PropertyReadInfo::VISIBILITY_PUBLIC, false, false);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getWriteInfo(string $class, string $property, array $context = []): ?PropertyWriteInfo
|
||||
{
|
||||
try {
|
||||
$reflClass = new \ReflectionClass($class);
|
||||
} catch (\ReflectionException $e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$allowGetterSetter = $context['enable_getter_setter_extraction'] ?? false;
|
||||
$magicMethods = $context['enable_magic_methods_extraction'] ?? $this->magicMethodsFlags;
|
||||
$allowMagicCall = (bool) ($magicMethods & self::ALLOW_MAGIC_CALL);
|
||||
$allowMagicSet = (bool) ($magicMethods & self::ALLOW_MAGIC_SET);
|
||||
|
||||
if (isset($context['enable_magic_call_extraction'])) {
|
||||
trigger_deprecation('symfony/property-info', '5.2', 'Using the "enable_magic_call_extraction" context option in "%s()" is deprecated. Use "enable_magic_methods_extraction" instead.', __METHOD__);
|
||||
|
||||
$allowMagicCall = $context['enable_magic_call_extraction'] ?? false;
|
||||
}
|
||||
|
||||
$allowConstruct = $context['enable_constructor_extraction'] ?? $this->enableConstructorExtraction;
|
||||
$allowAdderRemover = $context['enable_adder_remover_extraction'] ?? true;
|
||||
|
||||
$camelized = $this->camelize($property);
|
||||
$constructor = $reflClass->getConstructor();
|
||||
$singulars = $this->inflector->singularize($camelized);
|
||||
$errors = [];
|
||||
|
||||
if (null !== $constructor && $allowConstruct) {
|
||||
foreach ($constructor->getParameters() as $parameter) {
|
||||
if ($parameter->getName() === $property) {
|
||||
return new PropertyWriteInfo(PropertyWriteInfo::TYPE_CONSTRUCTOR, $property);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[$adderAccessName, $removerAccessName, $adderAndRemoverErrors] = $this->findAdderAndRemover($reflClass, $singulars);
|
||||
if ($allowAdderRemover && null !== $adderAccessName && null !== $removerAccessName) {
|
||||
$adderMethod = $reflClass->getMethod($adderAccessName);
|
||||
$removerMethod = $reflClass->getMethod($removerAccessName);
|
||||
|
||||
$mutator = new PropertyWriteInfo(PropertyWriteInfo::TYPE_ADDER_AND_REMOVER);
|
||||
$mutator->setAdderInfo(new PropertyWriteInfo(PropertyWriteInfo::TYPE_METHOD, $adderAccessName, $this->getWriteVisiblityForMethod($adderMethod), $adderMethod->isStatic()));
|
||||
$mutator->setRemoverInfo(new PropertyWriteInfo(PropertyWriteInfo::TYPE_METHOD, $removerAccessName, $this->getWriteVisiblityForMethod($removerMethod), $removerMethod->isStatic()));
|
||||
|
||||
return $mutator;
|
||||
}
|
||||
|
||||
$errors[] = $adderAndRemoverErrors;
|
||||
|
||||
foreach ($this->mutatorPrefixes as $mutatorPrefix) {
|
||||
$methodName = $mutatorPrefix.$camelized;
|
||||
|
||||
[$accessible, $methodAccessibleErrors] = $this->isMethodAccessible($reflClass, $methodName, 1);
|
||||
if (!$accessible) {
|
||||
$errors[] = $methodAccessibleErrors;
|
||||
continue;
|
||||
}
|
||||
|
||||
$method = $reflClass->getMethod($methodName);
|
||||
|
||||
if (!\in_array($mutatorPrefix, $this->arrayMutatorPrefixes, true)) {
|
||||
return new PropertyWriteInfo(PropertyWriteInfo::TYPE_METHOD, $methodName, $this->getWriteVisiblityForMethod($method), $method->isStatic());
|
||||
}
|
||||
}
|
||||
|
||||
$getsetter = lcfirst($camelized);
|
||||
|
||||
if ($allowGetterSetter) {
|
||||
[$accessible, $methodAccessibleErrors] = $this->isMethodAccessible($reflClass, $getsetter, 1);
|
||||
if ($accessible) {
|
||||
$method = $reflClass->getMethod($getsetter);
|
||||
|
||||
return new PropertyWriteInfo(PropertyWriteInfo::TYPE_METHOD, $getsetter, $this->getWriteVisiblityForMethod($method), $method->isStatic());
|
||||
}
|
||||
|
||||
$errors[] = $methodAccessibleErrors;
|
||||
}
|
||||
|
||||
if ($reflClass->hasProperty($property) && ($reflClass->getProperty($property)->getModifiers() & $this->propertyReflectionFlags)) {
|
||||
$reflProperty = $reflClass->getProperty($property);
|
||||
|
||||
return new PropertyWriteInfo(PropertyWriteInfo::TYPE_PROPERTY, $property, $this->getWriteVisiblityForProperty($reflProperty), $reflProperty->isStatic());
|
||||
}
|
||||
|
||||
if ($allowMagicSet) {
|
||||
[$accessible, $methodAccessibleErrors] = $this->isMethodAccessible($reflClass, '__set', 2);
|
||||
if ($accessible) {
|
||||
return new PropertyWriteInfo(PropertyWriteInfo::TYPE_PROPERTY, $property, PropertyWriteInfo::VISIBILITY_PUBLIC, false);
|
||||
}
|
||||
|
||||
$errors[] = $methodAccessibleErrors;
|
||||
}
|
||||
|
||||
if ($allowMagicCall) {
|
||||
[$accessible, $methodAccessibleErrors] = $this->isMethodAccessible($reflClass, '__call', 2);
|
||||
if ($accessible) {
|
||||
return new PropertyWriteInfo(PropertyWriteInfo::TYPE_METHOD, 'set'.$camelized, PropertyWriteInfo::VISIBILITY_PUBLIC, false);
|
||||
}
|
||||
|
||||
$errors[] = $methodAccessibleErrors;
|
||||
}
|
||||
|
||||
if (!$allowAdderRemover && null !== $adderAccessName && null !== $removerAccessName) {
|
||||
$errors[] = [sprintf(
|
||||
'The property "%s" in class "%s" can be defined with the methods "%s()" but '.
|
||||
'the new value must be an array or an instance of \Traversable',
|
||||
$property,
|
||||
$reflClass->getName(),
|
||||
implode('()", "', [$adderAccessName, $removerAccessName])
|
||||
)];
|
||||
}
|
||||
|
||||
$noneProperty = new PropertyWriteInfo();
|
||||
$noneProperty->setErrors(array_merge([], ...$errors));
|
||||
|
||||
return $noneProperty;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Type[]|null
|
||||
*/
|
||||
private function extractFromMutator(string $class, string $property): ?array
|
||||
{
|
||||
[$reflectionMethod, $prefix] = $this->getMutatorMethod($class, $property);
|
||||
if (null === $reflectionMethod) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$reflectionParameters = $reflectionMethod->getParameters();
|
||||
$reflectionParameter = $reflectionParameters[0];
|
||||
|
||||
if (!$reflectionType = $reflectionParameter->getType()) {
|
||||
return null;
|
||||
}
|
||||
$type = $this->extractFromReflectionType($reflectionType, $reflectionMethod->getDeclaringClass());
|
||||
|
||||
if (1 === \count($type) && \in_array($prefix, $this->arrayMutatorPrefixes)) {
|
||||
$type = [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), $type[0])];
|
||||
}
|
||||
|
||||
return $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to extract type information from accessors.
|
||||
*
|
||||
* @return Type[]|null
|
||||
*/
|
||||
private function extractFromAccessor(string $class, string $property): ?array
|
||||
{
|
||||
[$reflectionMethod, $prefix] = $this->getAccessorMethod($class, $property);
|
||||
if (null === $reflectionMethod) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($reflectionType = $reflectionMethod->getReturnType()) {
|
||||
return $this->extractFromReflectionType($reflectionType, $reflectionMethod->getDeclaringClass());
|
||||
}
|
||||
|
||||
if (\in_array($prefix, ['is', 'can', 'has'])) {
|
||||
return [new Type(Type::BUILTIN_TYPE_BOOL)];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to extract type information from constructor.
|
||||
*
|
||||
* @return Type[]|null
|
||||
*/
|
||||
private function extractFromConstructor(string $class, string $property): ?array
|
||||
{
|
||||
try {
|
||||
$reflectionClass = new \ReflectionClass($class);
|
||||
} catch (\ReflectionException $e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$constructor = $reflectionClass->getConstructor();
|
||||
|
||||
if (!$constructor) {
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach ($constructor->getParameters() as $parameter) {
|
||||
if ($property !== $parameter->name) {
|
||||
continue;
|
||||
}
|
||||
$reflectionType = $parameter->getType();
|
||||
|
||||
return $reflectionType ? $this->extractFromReflectionType($reflectionType, $constructor->getDeclaringClass()) : null;
|
||||
}
|
||||
|
||||
if ($parentClass = $reflectionClass->getParentClass()) {
|
||||
return $this->extractFromConstructor($parentClass->getName(), $property);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function extractFromPropertyDeclaration(string $class, string $property): ?array
|
||||
{
|
||||
try {
|
||||
$reflectionClass = new \ReflectionClass($class);
|
||||
|
||||
if (\PHP_VERSION_ID >= 70400) {
|
||||
$reflectionProperty = $reflectionClass->getProperty($property);
|
||||
$reflectionPropertyType = $reflectionProperty->getType();
|
||||
|
||||
if (null !== $reflectionPropertyType && $types = $this->extractFromReflectionType($reflectionPropertyType, $reflectionProperty->getDeclaringClass())) {
|
||||
return $types;
|
||||
}
|
||||
}
|
||||
} catch (\ReflectionException $e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$defaultValue = $reflectionClass->getDefaultProperties()[$property] ?? null;
|
||||
|
||||
if (null === $defaultValue) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$type = \gettype($defaultValue);
|
||||
$type = static::MAP_TYPES[$type] ?? $type;
|
||||
|
||||
return [new Type($type, $this->isNullableProperty($class, $property), null, Type::BUILTIN_TYPE_ARRAY === $type)];
|
||||
}
|
||||
|
||||
private function extractFromReflectionType(\ReflectionType $reflectionType, \ReflectionClass $declaringClass): array
|
||||
{
|
||||
$types = [];
|
||||
$nullable = $reflectionType->allowsNull();
|
||||
|
||||
foreach (($reflectionType instanceof \ReflectionUnionType || $reflectionType instanceof \ReflectionIntersectionType) ? $reflectionType->getTypes() : [$reflectionType] as $type) {
|
||||
$phpTypeOrClass = $type->getName();
|
||||
if ('null' === $phpTypeOrClass || 'mixed' === $phpTypeOrClass || 'never' === $phpTypeOrClass) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Type::BUILTIN_TYPE_ARRAY === $phpTypeOrClass) {
|
||||
$types[] = new Type(Type::BUILTIN_TYPE_ARRAY, $nullable, null, true);
|
||||
} elseif ('void' === $phpTypeOrClass) {
|
||||
$types[] = new Type(Type::BUILTIN_TYPE_NULL, $nullable);
|
||||
} elseif ($type->isBuiltin()) {
|
||||
$types[] = new Type($phpTypeOrClass, $nullable);
|
||||
} else {
|
||||
$types[] = new Type(Type::BUILTIN_TYPE_OBJECT, $nullable, $this->resolveTypeName($phpTypeOrClass, $declaringClass));
|
||||
}
|
||||
}
|
||||
|
||||
return $types;
|
||||
}
|
||||
|
||||
private function resolveTypeName(string $name, \ReflectionClass $declaringClass): string
|
||||
{
|
||||
if ('self' === $lcName = strtolower($name)) {
|
||||
return $declaringClass->name;
|
||||
}
|
||||
if ('parent' === $lcName && $parent = $declaringClass->getParentClass()) {
|
||||
return $parent->name;
|
||||
}
|
||||
|
||||
return $name;
|
||||
}
|
||||
|
||||
private function isNullableProperty(string $class, string $property): bool
|
||||
{
|
||||
try {
|
||||
$reflectionProperty = new \ReflectionProperty($class, $property);
|
||||
|
||||
if (\PHP_VERSION_ID >= 70400) {
|
||||
$reflectionPropertyType = $reflectionProperty->getType();
|
||||
|
||||
return null !== $reflectionPropertyType && $reflectionPropertyType->allowsNull();
|
||||
}
|
||||
|
||||
return false;
|
||||
} catch (\ReflectionException $e) {
|
||||
// Return false if the property doesn't exist
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function isAllowedProperty(string $class, string $property): bool
|
||||
{
|
||||
try {
|
||||
$reflectionProperty = new \ReflectionProperty($class, $property);
|
||||
|
||||
return (bool) ($reflectionProperty->getModifiers() & $this->propertyReflectionFlags);
|
||||
} catch (\ReflectionException $e) {
|
||||
// Return false if the property doesn't exist
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the accessor method.
|
||||
*
|
||||
* Returns an array with a the instance of \ReflectionMethod as first key
|
||||
* and the prefix of the method as second or null if not found.
|
||||
*/
|
||||
private function getAccessorMethod(string $class, string $property): ?array
|
||||
{
|
||||
$ucProperty = ucfirst($property);
|
||||
|
||||
foreach ($this->accessorPrefixes as $prefix) {
|
||||
try {
|
||||
$reflectionMethod = new \ReflectionMethod($class, $prefix.$ucProperty);
|
||||
if ($reflectionMethod->isStatic()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (0 === $reflectionMethod->getNumberOfRequiredParameters()) {
|
||||
return [$reflectionMethod, $prefix];
|
||||
}
|
||||
} catch (\ReflectionException $e) {
|
||||
// Return null if the property doesn't exist
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array with a the instance of \ReflectionMethod as first key
|
||||
* and the prefix of the method as second or null if not found.
|
||||
*/
|
||||
private function getMutatorMethod(string $class, string $property): ?array
|
||||
{
|
||||
$ucProperty = ucfirst($property);
|
||||
$ucSingulars = $this->inflector->singularize($ucProperty);
|
||||
|
||||
$mutatorPrefixes = \in_array($ucProperty, $ucSingulars, true) ? $this->arrayMutatorPrefixesLast : $this->arrayMutatorPrefixesFirst;
|
||||
|
||||
foreach ($mutatorPrefixes as $prefix) {
|
||||
$names = [$ucProperty];
|
||||
if (\in_array($prefix, $this->arrayMutatorPrefixes)) {
|
||||
$names = array_merge($names, $ucSingulars);
|
||||
}
|
||||
|
||||
foreach ($names as $name) {
|
||||
try {
|
||||
$reflectionMethod = new \ReflectionMethod($class, $prefix.$name);
|
||||
if ($reflectionMethod->isStatic()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Parameter can be optional to allow things like: method(array $foo = null)
|
||||
if ($reflectionMethod->getNumberOfParameters() >= 1) {
|
||||
return [$reflectionMethod, $prefix];
|
||||
}
|
||||
} catch (\ReflectionException $e) {
|
||||
// Try the next prefix if the method doesn't exist
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function getPropertyName(string $methodName, array $reflectionProperties): ?string
|
||||
{
|
||||
$pattern = implode('|', array_merge($this->accessorPrefixes, $this->mutatorPrefixes));
|
||||
|
||||
if ('' !== $pattern && preg_match('/^('.$pattern.')(.+)$/i', $methodName, $matches)) {
|
||||
if (!\in_array($matches[1], $this->arrayMutatorPrefixes)) {
|
||||
return $matches[2];
|
||||
}
|
||||
|
||||
foreach ($reflectionProperties as $reflectionProperty) {
|
||||
foreach ($this->inflector->singularize($reflectionProperty->name) as $name) {
|
||||
if (strtolower($name) === strtolower($matches[2])) {
|
||||
return $reflectionProperty->name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $matches[2];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for add and remove methods.
|
||||
*
|
||||
* @param \ReflectionClass $reflClass The reflection class for the given object
|
||||
* @param array $singulars The singular form of the property name or null
|
||||
*
|
||||
* @return array An array containing the adder and remover when found and errors
|
||||
*/
|
||||
private function findAdderAndRemover(\ReflectionClass $reflClass, array $singulars): array
|
||||
{
|
||||
if (!\is_array($this->arrayMutatorPrefixes) && 2 !== \count($this->arrayMutatorPrefixes)) {
|
||||
return [null, null, []];
|
||||
}
|
||||
|
||||
[$addPrefix, $removePrefix] = $this->arrayMutatorPrefixes;
|
||||
$errors = [];
|
||||
|
||||
foreach ($singulars as $singular) {
|
||||
$addMethod = $addPrefix.$singular;
|
||||
$removeMethod = $removePrefix.$singular;
|
||||
|
||||
[$addMethodFound, $addMethodAccessibleErrors] = $this->isMethodAccessible($reflClass, $addMethod, 1);
|
||||
[$removeMethodFound, $removeMethodAccessibleErrors] = $this->isMethodAccessible($reflClass, $removeMethod, 1);
|
||||
$errors[] = $addMethodAccessibleErrors;
|
||||
$errors[] = $removeMethodAccessibleErrors;
|
||||
|
||||
if ($addMethodFound && $removeMethodFound) {
|
||||
return [$addMethod, $removeMethod, []];
|
||||
}
|
||||
|
||||
if ($addMethodFound && !$removeMethodFound) {
|
||||
$errors[] = [sprintf('The add method "%s" in class "%s" was found, but the corresponding remove method "%s" was not found', $addMethod, $reflClass->getName(), $removeMethod)];
|
||||
} elseif (!$addMethodFound && $removeMethodFound) {
|
||||
$errors[] = [sprintf('The remove method "%s" in class "%s" was found, but the corresponding add method "%s" was not found', $removeMethod, $reflClass->getName(), $addMethod)];
|
||||
}
|
||||
}
|
||||
|
||||
return [null, null, array_merge([], ...$errors)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether a method is public and has the number of required parameters and errors.
|
||||
*/
|
||||
private function isMethodAccessible(\ReflectionClass $class, string $methodName, int $parameters): array
|
||||
{
|
||||
$errors = [];
|
||||
|
||||
if ($class->hasMethod($methodName)) {
|
||||
$method = $class->getMethod($methodName);
|
||||
|
||||
if (\ReflectionMethod::IS_PUBLIC === $this->methodReflectionFlags && !$method->isPublic()) {
|
||||
$errors[] = sprintf('The method "%s" in class "%s" was found but does not have public access.', $methodName, $class->getName());
|
||||
} elseif ($method->getNumberOfRequiredParameters() > $parameters || $method->getNumberOfParameters() < $parameters) {
|
||||
$errors[] = sprintf('The method "%s" in class "%s" requires %d arguments, but should accept only %d.', $methodName, $class->getName(), $method->getNumberOfRequiredParameters(), $parameters);
|
||||
} else {
|
||||
return [true, $errors];
|
||||
}
|
||||
}
|
||||
|
||||
return [false, $errors];
|
||||
}
|
||||
|
||||
/**
|
||||
* Camelizes a given string.
|
||||
*/
|
||||
private function camelize(string $string): string
|
||||
{
|
||||
return str_replace(' ', '', ucwords(str_replace('_', ' ', $string)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return allowed reflection method flags.
|
||||
*/
|
||||
private function getMethodsFlags(int $accessFlags): int
|
||||
{
|
||||
$methodFlags = 0;
|
||||
|
||||
if ($accessFlags & self::ALLOW_PUBLIC) {
|
||||
$methodFlags |= \ReflectionMethod::IS_PUBLIC;
|
||||
}
|
||||
|
||||
if ($accessFlags & self::ALLOW_PRIVATE) {
|
||||
$methodFlags |= \ReflectionMethod::IS_PRIVATE;
|
||||
}
|
||||
|
||||
if ($accessFlags & self::ALLOW_PROTECTED) {
|
||||
$methodFlags |= \ReflectionMethod::IS_PROTECTED;
|
||||
}
|
||||
|
||||
return $methodFlags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return allowed reflection property flags.
|
||||
*/
|
||||
private function getPropertyFlags(int $accessFlags): int
|
||||
{
|
||||
$propertyFlags = 0;
|
||||
|
||||
if ($accessFlags & self::ALLOW_PUBLIC) {
|
||||
$propertyFlags |= \ReflectionProperty::IS_PUBLIC;
|
||||
}
|
||||
|
||||
if ($accessFlags & self::ALLOW_PRIVATE) {
|
||||
$propertyFlags |= \ReflectionProperty::IS_PRIVATE;
|
||||
}
|
||||
|
||||
if ($accessFlags & self::ALLOW_PROTECTED) {
|
||||
$propertyFlags |= \ReflectionProperty::IS_PROTECTED;
|
||||
}
|
||||
|
||||
return $propertyFlags;
|
||||
}
|
||||
|
||||
private function getReadVisiblityForProperty(\ReflectionProperty $reflectionProperty): string
|
||||
{
|
||||
if ($reflectionProperty->isPrivate()) {
|
||||
return PropertyReadInfo::VISIBILITY_PRIVATE;
|
||||
}
|
||||
|
||||
if ($reflectionProperty->isProtected()) {
|
||||
return PropertyReadInfo::VISIBILITY_PROTECTED;
|
||||
}
|
||||
|
||||
return PropertyReadInfo::VISIBILITY_PUBLIC;
|
||||
}
|
||||
|
||||
private function getReadVisiblityForMethod(\ReflectionMethod $reflectionMethod): string
|
||||
{
|
||||
if ($reflectionMethod->isPrivate()) {
|
||||
return PropertyReadInfo::VISIBILITY_PRIVATE;
|
||||
}
|
||||
|
||||
if ($reflectionMethod->isProtected()) {
|
||||
return PropertyReadInfo::VISIBILITY_PROTECTED;
|
||||
}
|
||||
|
||||
return PropertyReadInfo::VISIBILITY_PUBLIC;
|
||||
}
|
||||
|
||||
private function getWriteVisiblityForProperty(\ReflectionProperty $reflectionProperty): string
|
||||
{
|
||||
if ($reflectionProperty->isPrivate()) {
|
||||
return PropertyWriteInfo::VISIBILITY_PRIVATE;
|
||||
}
|
||||
|
||||
if ($reflectionProperty->isProtected()) {
|
||||
return PropertyWriteInfo::VISIBILITY_PROTECTED;
|
||||
}
|
||||
|
||||
return PropertyWriteInfo::VISIBILITY_PUBLIC;
|
||||
}
|
||||
|
||||
private function getWriteVisiblityForMethod(\ReflectionMethod $reflectionMethod): string
|
||||
{
|
||||
if ($reflectionMethod->isPrivate()) {
|
||||
return PropertyWriteInfo::VISIBILITY_PRIVATE;
|
||||
}
|
||||
|
||||
if ($reflectionMethod->isProtected()) {
|
||||
return PropertyWriteInfo::VISIBILITY_PROTECTED;
|
||||
}
|
||||
|
||||
return PropertyWriteInfo::VISIBILITY_PUBLIC;
|
||||
}
|
||||
}
|
58
vendor/symfony/property-info/Extractor/SerializerExtractor.php
vendored
Normal file
58
vendor/symfony/property-info/Extractor/SerializerExtractor.php
vendored
Normal file
@ -0,0 +1,58 @@
|
||||
<?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
|
||||
{
|
||||
private $classMetadataFactory;
|
||||
|
||||
public function __construct(ClassMetadataFactoryInterface $classMetadataFactory)
|
||||
{
|
||||
$this->classMetadataFactory = $classMetadataFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
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->getMetadataFor($class)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$properties = [];
|
||||
$serializerClassMetadata = $this->classMetadataFactory->getMetadataFor($class);
|
||||
|
||||
foreach ($serializerClassMetadata->getAttributesMetadata() as $serializerAttributeMetadata) {
|
||||
$ignored = method_exists($serializerAttributeMetadata, 'isIgnored') && $serializerAttributeMetadata->isIgnored();
|
||||
if (!$ignored && (null === $context['serializer_groups'] || array_intersect($context['serializer_groups'], $serializerAttributeMetadata->getGroups()))) {
|
||||
$properties[] = $serializerAttributeMetadata->getName();
|
||||
}
|
||||
}
|
||||
|
||||
return $properties;
|
||||
}
|
||||
}
|
19
vendor/symfony/property-info/LICENSE
vendored
Normal file
19
vendor/symfony/property-info/LICENSE
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
Copyright (c) 2015-2022 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.
|
65
vendor/symfony/property-info/PhpStan/NameScope.php
vendored
Normal file
65
vendor/symfony/property-info/PhpStan/NameScope.php
vendored
Normal file
@ -0,0 +1,65 @@
|
||||
<?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\PhpStan;
|
||||
|
||||
/**
|
||||
* NameScope class adapted from PHPStan code.
|
||||
*
|
||||
* @copyright Copyright (c) 2016, PHPStan https://github.com/phpstan/phpstan-src
|
||||
* @copyright Copyright (c) 2016, Ondřej Mirtes
|
||||
* @author Baptiste Leduc <baptiste.leduc@gmail.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class NameScope
|
||||
{
|
||||
private $calledClassName;
|
||||
private $namespace;
|
||||
/** @var array<string, string> alias(string) => fullName(string) */
|
||||
private $uses;
|
||||
|
||||
public function __construct(string $calledClassName, string $namespace, array $uses = [])
|
||||
{
|
||||
$this->calledClassName = $calledClassName;
|
||||
$this->namespace = $namespace;
|
||||
$this->uses = $uses;
|
||||
}
|
||||
|
||||
public function resolveStringName(string $name): string
|
||||
{
|
||||
if (0 === strpos($name, '\\')) {
|
||||
return ltrim($name, '\\');
|
||||
}
|
||||
|
||||
$nameParts = explode('\\', $name);
|
||||
$firstNamePart = $nameParts[0];
|
||||
if (isset($this->uses[$firstNamePart])) {
|
||||
if (1 === \count($nameParts)) {
|
||||
return $this->uses[$firstNamePart];
|
||||
}
|
||||
array_shift($nameParts);
|
||||
|
||||
return sprintf('%s\\%s', $this->uses[$firstNamePart], implode('\\', $nameParts));
|
||||
}
|
||||
|
||||
if (null !== $this->namespace) {
|
||||
return sprintf('%s\\%s', $this->namespace, $name);
|
||||
}
|
||||
|
||||
return $name;
|
||||
}
|
||||
|
||||
public function resolveRootClass(): string
|
||||
{
|
||||
return $this->resolveStringName($this->calledClassName);
|
||||
}
|
||||
}
|
70
vendor/symfony/property-info/PhpStan/NameScopeFactory.php
vendored
Normal file
70
vendor/symfony/property-info/PhpStan/NameScopeFactory.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\PhpStan;
|
||||
|
||||
use phpDocumentor\Reflection\Types\ContextFactory;
|
||||
|
||||
/**
|
||||
* @author Baptiste Leduc <baptiste.leduc@gmail.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class NameScopeFactory
|
||||
{
|
||||
public function create(string $calledClassName, string $declaringClassName = null): NameScope
|
||||
{
|
||||
$declaringClassName = $declaringClassName ?? $calledClassName;
|
||||
|
||||
$path = explode('\\', $calledClassName);
|
||||
$calledClassName = array_pop($path);
|
||||
|
||||
$declaringReflection = new \ReflectionClass($declaringClassName);
|
||||
[$declaringNamespace, $declaringUses] = $this->extractFromFullClassName($declaringReflection);
|
||||
$declaringUses = array_merge($declaringUses, $this->collectUses($declaringReflection));
|
||||
|
||||
return new NameScope($calledClassName, $declaringNamespace, $declaringUses);
|
||||
}
|
||||
|
||||
private function collectUses(\ReflectionClass $reflection): array
|
||||
{
|
||||
$uses = [$this->extractFromFullClassName($reflection)[1]];
|
||||
|
||||
foreach ($reflection->getTraits() as $traitReflection) {
|
||||
$uses[] = $this->extractFromFullClassName($traitReflection)[1];
|
||||
}
|
||||
|
||||
if (false !== $parentClass = $reflection->getParentClass()) {
|
||||
$uses[] = $this->collectUses($parentClass);
|
||||
}
|
||||
|
||||
return $uses ? array_merge(...$uses) : [];
|
||||
}
|
||||
|
||||
private function extractFromFullClassName(\ReflectionClass $reflection): array
|
||||
{
|
||||
$namespace = trim($reflection->getNamespaceName(), '\\');
|
||||
$fileName = $reflection->getFileName();
|
||||
|
||||
if (\is_string($fileName) && is_file($fileName)) {
|
||||
if (false === $contents = file_get_contents($fileName)) {
|
||||
throw new \RuntimeException(sprintf('Unable to read file "%s".', $fileName));
|
||||
}
|
||||
|
||||
$factory = new ContextFactory();
|
||||
$context = $factory->createForNamespace($namespace, $contents);
|
||||
|
||||
return [$namespace, $context->getNamespaceAliases()];
|
||||
}
|
||||
|
||||
return [$namespace, []];
|
||||
}
|
||||
}
|
34
vendor/symfony/property-info/PropertyAccessExtractorInterface.php
vendored
Normal file
34
vendor/symfony/property-info/PropertyAccessExtractorInterface.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;
|
||||
|
||||
/**
|
||||
* Guesses if the property can be accessed or mutated.
|
||||
*
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
interface PropertyAccessExtractorInterface
|
||||
{
|
||||
/**
|
||||
* Is the property readable?
|
||||
*
|
||||
* @return bool|null
|
||||
*/
|
||||
public function isReadable(string $class, string $property, array $context = []);
|
||||
|
||||
/**
|
||||
* Is the property writable?
|
||||
*
|
||||
* @return bool|null
|
||||
*/
|
||||
public function isWritable(string $class, string $property, array $context = []);
|
||||
}
|
34
vendor/symfony/property-info/PropertyDescriptionExtractorInterface.php
vendored
Normal file
34
vendor/symfony/property-info/PropertyDescriptionExtractorInterface.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;
|
||||
|
||||
/**
|
||||
* Guesses the property's human readable description.
|
||||
*
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
interface PropertyDescriptionExtractorInterface
|
||||
{
|
||||
/**
|
||||
* Gets the short description of the property.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getShortDescription(string $class, string $property, array $context = []);
|
||||
|
||||
/**
|
||||
* Gets the long description of the property.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getLongDescription(string $class, string $property, array $context = []);
|
||||
}
|
124
vendor/symfony/property-info/PropertyInfoCacheExtractor.php
vendored
Normal file
124
vendor/symfony/property-info/PropertyInfoCacheExtractor.php
vendored
Normal file
@ -0,0 +1,124 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* 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 $propertyInfoExtractor;
|
||||
private $cacheItemPool;
|
||||
private $arrayCache = [];
|
||||
|
||||
public function __construct(PropertyInfoExtractorInterface $propertyInfoExtractor, CacheItemPoolInterface $cacheItemPool)
|
||||
{
|
||||
$this->propertyInfoExtractor = $propertyInfoExtractor;
|
||||
$this->cacheItemPool = $cacheItemPool;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isReadable(string $class, string $property, array $context = []): ?bool
|
||||
{
|
||||
return $this->extract('isReadable', [$class, $property, $context]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isWritable(string $class, string $property, array $context = []): ?bool
|
||||
{
|
||||
return $this->extract('isWritable', [$class, $property, $context]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getShortDescription(string $class, string $property, array $context = []): ?string
|
||||
{
|
||||
return $this->extract('getShortDescription', [$class, $property, $context]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getLongDescription(string $class, string $property, array $context = []): ?string
|
||||
{
|
||||
return $this->extract('getLongDescription', [$class, $property, $context]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getProperties(string $class, array $context = []): ?array
|
||||
{
|
||||
return $this->extract('getProperties', [$class, $context]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTypes(string $class, string $property, array $context = []): ?array
|
||||
{
|
||||
return $this->extract('getTypes', [$class, $property, $context]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
private function extract(string $method, array $arguments)
|
||||
{
|
||||
try {
|
||||
$serializedArguments = serialize($arguments);
|
||||
} catch (\Exception $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
vendor/symfony/property-info/PropertyInfoExtractor.php
vendored
Normal file
119
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;
|
||||
|
||||
/**
|
||||
* Default {@see PropertyInfoExtractorInterface} implementation.
|
||||
*
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
class PropertyInfoExtractor implements PropertyInfoExtractorInterface, PropertyInitializableExtractorInterface
|
||||
{
|
||||
private $listExtractors;
|
||||
private $typeExtractors;
|
||||
private $descriptionExtractors;
|
||||
private $accessExtractors;
|
||||
private $initializableExtractors;
|
||||
|
||||
/**
|
||||
* @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(iterable $listExtractors = [], iterable $typeExtractors = [], iterable $descriptionExtractors = [], iterable $accessExtractors = [], iterable $initializableExtractors = [])
|
||||
{
|
||||
$this->listExtractors = $listExtractors;
|
||||
$this->typeExtractors = $typeExtractors;
|
||||
$this->descriptionExtractors = $descriptionExtractors;
|
||||
$this->accessExtractors = $accessExtractors;
|
||||
$this->initializableExtractors = $initializableExtractors;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getProperties(string $class, array $context = []): ?array
|
||||
{
|
||||
return $this->extract($this->listExtractors, 'getProperties', [$class, $context]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getShortDescription(string $class, string $property, array $context = []): ?string
|
||||
{
|
||||
return $this->extract($this->descriptionExtractors, 'getShortDescription', [$class, $property, $context]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getLongDescription(string $class, string $property, array $context = []): ?string
|
||||
{
|
||||
return $this->extract($this->descriptionExtractors, 'getLongDescription', [$class, $property, $context]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTypes(string $class, string $property, array $context = []): ?array
|
||||
{
|
||||
return $this->extract($this->typeExtractors, 'getTypes', [$class, $property, $context]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isReadable(string $class, string $property, array $context = []): ?bool
|
||||
{
|
||||
return $this->extract($this->accessExtractors, 'isReadable', [$class, $property, $context]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isWritable(string $class, string $property, array $context = []): ?bool
|
||||
{
|
||||
return $this->extract($this->accessExtractors, 'isWritable', [$class, $property, $context]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
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
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
private function extract(iterable $extractors, string $method, array $arguments)
|
||||
{
|
||||
foreach ($extractors as $extractor) {
|
||||
if (null !== $value = $extractor->{$method}(...$arguments)) {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
23
vendor/symfony/property-info/PropertyInfoExtractorInterface.php
vendored
Normal file
23
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
vendor/symfony/property-info/PropertyInitializableExtractorInterface.php
vendored
Normal file
25
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
vendor/symfony/property-info/PropertyListExtractorInterface.php
vendored
Normal file
27
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 = []);
|
||||
}
|
82
vendor/symfony/property-info/PropertyReadInfo.php
vendored
Normal file
82
vendor/symfony/property-info/PropertyReadInfo.php
vendored
Normal file
@ -0,0 +1,82 @@
|
||||
<?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>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
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';
|
||||
|
||||
private $type;
|
||||
|
||||
private $name;
|
||||
|
||||
private $visibility;
|
||||
|
||||
private $static;
|
||||
|
||||
private $byRef;
|
||||
|
||||
public function __construct(string $type, string $name, string $visibility, bool $static, bool $byRef)
|
||||
{
|
||||
$this->type = $type;
|
||||
$this->name = $name;
|
||||
$this->visibility = $visibility;
|
||||
$this->static = $static;
|
||||
$this->byRef = $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
vendor/symfony/property-info/PropertyReadInfoExtractorInterface.php
vendored
Normal file
25
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;
|
||||
}
|
27
vendor/symfony/property-info/PropertyTypeExtractorInterface.php
vendored
Normal file
27
vendor/symfony/property-info/PropertyTypeExtractorInterface.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;
|
||||
|
||||
/**
|
||||
* Type Extractor Interface.
|
||||
*
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
interface PropertyTypeExtractorInterface
|
||||
{
|
||||
/**
|
||||
* Gets types of a property.
|
||||
*
|
||||
* @return Type[]|null
|
||||
*/
|
||||
public function getTypes(string $class, string $property, array $context = []);
|
||||
}
|
123
vendor/symfony/property-info/PropertyWriteInfo.php
vendored
Normal file
123
vendor/symfony/property-info/PropertyWriteInfo.php
vendored
Normal file
@ -0,0 +1,123 @@
|
||||
<?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>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
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 $type;
|
||||
private $name;
|
||||
private $visibility;
|
||||
private $static;
|
||||
private $adderInfo;
|
||||
private $removerInfo;
|
||||
private $errors = [];
|
||||
|
||||
public function __construct(string $type = self::TYPE_NONE, string $name = null, string $visibility = null, bool $static = null)
|
||||
{
|
||||
$this->type = $type;
|
||||
$this->name = $name;
|
||||
$this->visibility = $visibility;
|
||||
$this->static = $static;
|
||||
}
|
||||
|
||||
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
vendor/symfony/property-info/PropertyWriteInfoExtractorInterface.php
vendored
Normal file
25
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
vendor/symfony/property-info/README.md
vendored
Normal file
14
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)
|
206
vendor/symfony/property-info/Type.php
vendored
Normal file
206
vendor/symfony/property-info/Type.php
vendored
Normal file
@ -0,0 +1,206 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* Type value object (immutable).
|
||||
*
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*
|
||||
* @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_TRUE = 'true';
|
||||
public const BUILTIN_TYPE_FALSE = 'false';
|
||||
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_CALLABLE = 'callable';
|
||||
public const BUILTIN_TYPE_ITERABLE = 'iterable';
|
||||
|
||||
/**
|
||||
* List of PHP builtin types.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
public static $builtinTypes = [
|
||||
self::BUILTIN_TYPE_INT,
|
||||
self::BUILTIN_TYPE_FLOAT,
|
||||
self::BUILTIN_TYPE_STRING,
|
||||
self::BUILTIN_TYPE_BOOL,
|
||||
self::BUILTIN_TYPE_TRUE,
|
||||
self::BUILTIN_TYPE_FALSE,
|
||||
self::BUILTIN_TYPE_RESOURCE,
|
||||
self::BUILTIN_TYPE_OBJECT,
|
||||
self::BUILTIN_TYPE_ARRAY,
|
||||
self::BUILTIN_TYPE_CALLABLE,
|
||||
self::BUILTIN_TYPE_NULL,
|
||||
self::BUILTIN_TYPE_ITERABLE,
|
||||
];
|
||||
|
||||
/**
|
||||
* List of PHP builtin collection types.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
public static $builtinCollectionTypes = [
|
||||
self::BUILTIN_TYPE_ARRAY,
|
||||
self::BUILTIN_TYPE_ITERABLE,
|
||||
];
|
||||
|
||||
private $builtinType;
|
||||
private $nullable;
|
||||
private $class;
|
||||
private $collection;
|
||||
private $collectionKeyType;
|
||||
private $collectionValueType;
|
||||
|
||||
/**
|
||||
* @param Type[]|Type|null $collectionKeyType
|
||||
* @param Type[]|Type|null $collectionValueType
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function __construct(string $builtinType, bool $nullable = false, string $class = null, bool $collection = false, $collectionKeyType = null, $collectionValueType = null)
|
||||
{
|
||||
if (!\in_array($builtinType, self::$builtinTypes)) {
|
||||
throw new \InvalidArgumentException(sprintf('"%s" is not a valid PHP type.', $builtinType));
|
||||
}
|
||||
|
||||
$this->builtinType = $builtinType;
|
||||
$this->nullable = $nullable;
|
||||
$this->class = $class;
|
||||
$this->collection = $collection;
|
||||
$this->collectionKeyType = $this->validateCollectionArgument($collectionKeyType, 5, '$collectionKeyType') ?? [];
|
||||
$this->collectionValueType = $this->validateCollectionArgument($collectionValueType, 6, '$collectionValueType') ?? [];
|
||||
}
|
||||
|
||||
private function validateCollectionArgument($collectionArgument, int $argumentIndex, string $argumentName): ?array
|
||||
{
|
||||
if (null === $collectionArgument) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!\is_array($collectionArgument) && !$collectionArgument instanceof self) {
|
||||
throw new \TypeError(sprintf('"%s()": Argument #%d (%s) must be of type "%s[]", "%s" or "null", "%s" given.', __METHOD__, $argumentIndex, $argumentName, self::class, self::class, get_debug_type($collectionArgument)));
|
||||
}
|
||||
|
||||
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 type.
|
||||
*
|
||||
* Only applicable for a collection type.
|
||||
*
|
||||
* @deprecated since Symfony 5.3, use "getCollectionKeyTypes()" instead
|
||||
*/
|
||||
public function getCollectionKeyType(): ?self
|
||||
{
|
||||
trigger_deprecation('symfony/property-info', '5.3', 'The "%s()" method is deprecated, use "getCollectionKeyTypes()" instead.', __METHOD__);
|
||||
|
||||
$type = $this->getCollectionKeyTypes();
|
||||
if (0 === \count($type)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (\is_array($type)) {
|
||||
[$type] = $type;
|
||||
}
|
||||
|
||||
return $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets collection key types.
|
||||
*
|
||||
* Only applicable for a collection type.
|
||||
*
|
||||
* @return Type[]
|
||||
*/
|
||||
public function getCollectionKeyTypes(): array
|
||||
{
|
||||
return $this->collectionKeyType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets collection value type.
|
||||
*
|
||||
* Only applicable for a collection type.
|
||||
*
|
||||
* @deprecated since Symfony 5.3, use "getCollectionValueTypes()" instead
|
||||
*/
|
||||
public function getCollectionValueType(): ?self
|
||||
{
|
||||
trigger_deprecation('symfony/property-info', '5.3', 'The "%s()" method is deprecated, use "getCollectionValueTypes()" instead.', __METHOD__);
|
||||
|
||||
return $this->getCollectionValueTypes()[0] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets collection value types.
|
||||
*
|
||||
* Only applicable for a collection type.
|
||||
*
|
||||
* @return Type[]
|
||||
*/
|
||||
public function getCollectionValueTypes(): array
|
||||
{
|
||||
return $this->collectionValueType;
|
||||
}
|
||||
}
|
192
vendor/symfony/property-info/Util/PhpDocTypeHelper.php
vendored
Normal file
192
vendor/symfony/property-info/Util/PhpDocTypeHelper.php
vendored
Normal file
@ -0,0 +1,192 @@
|
||||
<?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\PseudoTypes\List_;
|
||||
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\Null_;
|
||||
use phpDocumentor\Reflection\Types\Nullable;
|
||||
use Symfony\Component\PropertyInfo\Type;
|
||||
|
||||
// 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 Type} from a PHPDoc type.
|
||||
*
|
||||
* @return Type[]
|
||||
*/
|
||||
public function getTypes(DocType $varType): array
|
||||
{
|
||||
$types = [];
|
||||
$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, $nullable);
|
||||
if (null !== $type) {
|
||||
$types[] = $type;
|
||||
}
|
||||
|
||||
return $types;
|
||||
}
|
||||
|
||||
$varTypes = [];
|
||||
for ($typeIndex = 0; $varType->has($typeIndex); ++$typeIndex) {
|
||||
$type = $varType->get($typeIndex);
|
||||
|
||||
// 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->createType($varType, $nullable);
|
||||
if (null !== $type) {
|
||||
$types[] = $type;
|
||||
}
|
||||
}
|
||||
|
||||
return $types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@see Type} from a PHPDoc type.
|
||||
*/
|
||||
private function createType(DocType $type, bool $nullable, string $docType = null): ?Type
|
||||
{
|
||||
$docType = $docType ?? (string) $type;
|
||||
|
||||
if ($type instanceof Collection) {
|
||||
$fqsen = $type->getFqsen();
|
||||
if ($fqsen && 'list' === $fqsen->getName() && !class_exists(List_::class, false) && !class_exists((string) $fqsen)) {
|
||||
// Workaround for phpdocumentor/type-resolver < 1.6
|
||||
return new Type(Type::BUILTIN_TYPE_ARRAY, $nullable, null, true, new Type(Type::BUILTIN_TYPE_INT), $this->getTypes($type->getValueType()));
|
||||
}
|
||||
|
||||
[$phpType, $class] = $this->getPhpTypeAndClass((string) $fqsen);
|
||||
|
||||
$key = $this->getTypes($type->getKeyType());
|
||||
$value = $this->getTypes($type->getValueType());
|
||||
|
||||
// More than 1 type returned means it is a Compound type, which is
|
||||
// not handled by Type, so better use a null value.
|
||||
$key = 1 === \count($key) ? $key[0] : null;
|
||||
$value = 1 === \count($value) ? $value[0] : null;
|
||||
|
||||
return new Type($phpType, $nullable, $class, true, $key, $value);
|
||||
}
|
||||
|
||||
// Cannot guess
|
||||
if (!$docType || 'mixed' === $docType) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (str_ends_with($docType, '[]')) {
|
||||
$collectionKeyType = new Type(Type::BUILTIN_TYPE_INT);
|
||||
$collectionValueType = $this->createType($type, false, substr($docType, 0, -2));
|
||||
|
||||
return new Type(Type::BUILTIN_TYPE_ARRAY, $nullable, null, true, $collectionKeyType, $collectionValueType);
|
||||
}
|
||||
|
||||
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
|
||||
$collectionKeyType = $this->getTypes($type->getKeyType())[0];
|
||||
|
||||
$collectionValueTypes = $this->getTypes($type->getValueType());
|
||||
if (1 != \count($collectionValueTypes)) {
|
||||
// the Type class does not support union types yet, so assume that no type was defined
|
||||
$collectionValueType = null;
|
||||
} else {
|
||||
$collectionValueType = $collectionValueTypes[0];
|
||||
}
|
||||
|
||||
return new Type(Type::BUILTIN_TYPE_ARRAY, $nullable, null, true, $collectionKeyType, $collectionValueType);
|
||||
}
|
||||
|
||||
$docType = $this->normalizeType($docType);
|
||||
[$phpType, $class] = $this->getPhpTypeAndClass($docType);
|
||||
|
||||
if ('array' === $docType) {
|
||||
return new Type(Type::BUILTIN_TYPE_ARRAY, $nullable, null, true, null, null);
|
||||
}
|
||||
|
||||
return new Type($phpType, $nullable, $class);
|
||||
}
|
||||
|
||||
private function normalizeType(string $docType): string
|
||||
{
|
||||
switch ($docType) {
|
||||
case 'integer':
|
||||
return 'int';
|
||||
|
||||
case 'boolean':
|
||||
return 'bool';
|
||||
|
||||
// real is not part of the PHPDoc standard, so we ignore it
|
||||
case 'double':
|
||||
return 'float';
|
||||
|
||||
case 'callback':
|
||||
return 'callable';
|
||||
|
||||
case 'void':
|
||||
return 'null';
|
||||
|
||||
default:
|
||||
return $docType;
|
||||
}
|
||||
}
|
||||
|
||||
private function getPhpTypeAndClass(string $docType): array
|
||||
{
|
||||
if (\in_array($docType, Type::$builtinTypes)) {
|
||||
return [$docType, null];
|
||||
}
|
||||
|
||||
if (\in_array($docType, ['parent', 'self', 'static'], true)) {
|
||||
return ['object', $docType];
|
||||
}
|
||||
|
||||
return ['object', ltrim($docType, '\\')];
|
||||
}
|
||||
}
|
188
vendor/symfony/property-info/Util/PhpStanTypeHelper.php
vendored
Normal file
188
vendor/symfony/property-info/Util/PhpStanTypeHelper.php
vendored
Normal file
@ -0,0 +1,188 @@
|
||||
<?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\PhpStan\NameScope;
|
||||
use Symfony\Component\PropertyInfo\Type;
|
||||
|
||||
/**
|
||||
* 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, NameScope $nameScope): array
|
||||
{
|
||||
if ($node instanceof ParamTagValueNode || $node instanceof ReturnTagValueNode || $node instanceof VarTagValueNode) {
|
||||
return $this->compressNullableType($this->extractTypes($node->type, $nameScope));
|
||||
}
|
||||
|
||||
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, NameScope $nameScope): 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, $nameScope) as $subType) {
|
||||
$types[] = $subType;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->compressNullableType($types);
|
||||
}
|
||||
if ($node instanceof GenericTypeNode) {
|
||||
[$mainType] = $this->extractTypes($node->type, $nameScope);
|
||||
|
||||
$collectionKeyTypes = $mainType->getCollectionKeyTypes();
|
||||
$collectionKeyValues = [];
|
||||
if (1 === \count($node->genericTypes)) {
|
||||
foreach ($this->extractTypes($node->genericTypes[0], $nameScope) as $subType) {
|
||||
$collectionKeyValues[] = $subType;
|
||||
}
|
||||
} elseif (2 === \count($node->genericTypes)) {
|
||||
foreach ($this->extractTypes($node->genericTypes[0], $nameScope) as $keySubType) {
|
||||
$collectionKeyTypes[] = $keySubType;
|
||||
}
|
||||
foreach ($this->extractTypes($node->genericTypes[1], $nameScope) as $valueSubType) {
|
||||
$collectionKeyValues[] = $valueSubType;
|
||||
}
|
||||
}
|
||||
|
||||
return [new Type($mainType->getBuiltinType(), $mainType->isNullable(), $mainType->getClassName(), true, $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, $nameScope))];
|
||||
}
|
||||
if ($node instanceof CallableTypeNode || $node instanceof CallableTypeParameterNode) {
|
||||
return [new Type(Type::BUILTIN_TYPE_CALLABLE)];
|
||||
}
|
||||
if ($node instanceof NullableTypeNode) {
|
||||
$subTypes = $this->extractTypes($node->type, $nameScope);
|
||||
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, $nameScope->resolveRootClass())];
|
||||
}
|
||||
if ($node instanceof IdentifierTypeNode) {
|
||||
if (\in_array($node->name, Type::$builtinTypes)) {
|
||||
return [new Type($node->name, false, null, \in_array($node->name, Type::$builtinCollectionTypes))];
|
||||
}
|
||||
|
||||
switch ($node->name) {
|
||||
case 'integer':
|
||||
return [new Type(Type::BUILTIN_TYPE_INT)];
|
||||
case 'list':
|
||||
case 'non-empty-list':
|
||||
return [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT))];
|
||||
case 'non-empty-array':
|
||||
return [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true)];
|
||||
case 'mixed':
|
||||
return []; // mixed seems to be ignored in all other extractors
|
||||
case 'parent':
|
||||
return [new Type(Type::BUILTIN_TYPE_OBJECT, false, $node->name)];
|
||||
case 'static':
|
||||
case 'self':
|
||||
return [new Type(Type::BUILTIN_TYPE_OBJECT, false, $nameScope->resolveRootClass())];
|
||||
case 'void':
|
||||
return [new Type(Type::BUILTIN_TYPE_NULL)];
|
||||
}
|
||||
|
||||
return [new Type(Type::BUILTIN_TYPE_OBJECT, false, $nameScope->resolveStringName($node->name))];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
57
vendor/symfony/property-info/composer.json
vendored
Normal file
57
vendor/symfony/property-info/composer.json
vendored
Normal file
@ -0,0 +1,57 @@
|
||||
{
|
||||
"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": ">=7.2.5",
|
||||
"symfony/deprecation-contracts": "^2.1|^3",
|
||||
"symfony/polyfill-php80": "^1.16",
|
||||
"symfony/string": "^5.1|^6.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/serializer": "^4.4|^5.0|^6.0",
|
||||
"symfony/cache": "^4.4|^5.0|^6.0",
|
||||
"symfony/dependency-injection": "^4.4|^5.0|^6.0",
|
||||
"phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0",
|
||||
"phpstan/phpdoc-parser": "^1.0",
|
||||
"doctrine/annotations": "^1.10.4"
|
||||
},
|
||||
"conflict": {
|
||||
"phpdocumentor/reflection-docblock": "<3.2.2",
|
||||
"phpdocumentor/type-resolver": "<1.4.0",
|
||||
"symfony/dependency-injection": "<4.4"
|
||||
},
|
||||
"suggest": {
|
||||
"psr/cache-implementation": "To cache results",
|
||||
"symfony/doctrine-bridge": "To use Doctrine metadata",
|
||||
"phpdocumentor/reflection-docblock": "To use the PHPDoc",
|
||||
"symfony/serializer": "To use Serializer metadata"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": { "Symfony\\Component\\PropertyInfo\\": "" },
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
},
|
||||
"minimum-stability": "dev"
|
||||
}
|
Reference in New Issue
Block a user