v1.0 Initial commit of project
This commit is contained in:
3
vendor/symfony/property-info/.gitattributes
vendored
Normal file
3
vendor/symfony/property-info/.gitattributes
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
/Tests export-ignore
|
||||
/phpunit.xml.dist export-ignore
|
||||
/.git* export-ignore
|
||||
8
vendor/symfony/property-info/.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
8
vendor/symfony/property-info/.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
Please do not submit any Pull Requests here. They will be closed.
|
||||
---
|
||||
|
||||
Please submit your PR here instead:
|
||||
https://github.com/symfony/symfony
|
||||
|
||||
This repository is what we call a "subtree split": a read-only subset of that main repository.
|
||||
We're looking forward to your PR there!
|
||||
20
vendor/symfony/property-info/.github/workflows/close-pull-request.yml
vendored
Normal file
20
vendor/symfony/property-info/.github/workflows/close-pull-request.yml
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
name: Close Pull Request
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened]
|
||||
|
||||
jobs:
|
||||
run:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: superbrothers/close-pull-request@v3
|
||||
with:
|
||||
comment: |
|
||||
Thanks for your Pull Request! We love contributions.
|
||||
|
||||
However, you should instead open your PR on the main repository:
|
||||
https://github.com/symfony/symfony
|
||||
|
||||
This repository is what we call a "subtree split": a read-only subset of that main repository.
|
||||
We're looking forward to your PR there!
|
||||
3
vendor/symfony/property-info/.gitignore
vendored
Normal file
3
vendor/symfony/property-info/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
vendor/
|
||||
composer.lock
|
||||
phpunit.xml
|
||||
57
vendor/symfony/property-info/CHANGELOG.md
vendored
Normal file
57
vendor/symfony/property-info/CHANGELOG.md
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
CHANGELOG
|
||||
=========
|
||||
|
||||
6.4
|
||||
---
|
||||
|
||||
* Make properties writable when a setter in camelCase exists, similar to the camelCase getter
|
||||
|
||||
6.1
|
||||
---
|
||||
|
||||
* Add support for phpDocumentor and PHPStan pseudo-types
|
||||
* Add PHP 8.0 promoted properties `@param` mutation support to `PhpDocExtractor`
|
||||
* Add PHP 8.0 promoted properties `@param` mutation support to `PhpStanExtractor`
|
||||
|
||||
6.0
|
||||
---
|
||||
|
||||
* Remove the `Type::getCollectionKeyType()` and `Type::getCollectionValueType()` methods, use `Type::getCollectionKeyTypes()` and `Type::getCollectionValueTypes()` instead
|
||||
* Remove the `enable_magic_call_extraction` context option in `ReflectionExtractor::getWriteInfo()` and `ReflectionExtractor::getReadInfo()` in favor of `enable_magic_methods_extraction`
|
||||
|
||||
5.4
|
||||
---
|
||||
|
||||
* Add PhpStanExtractor
|
||||
|
||||
5.3
|
||||
---
|
||||
|
||||
* Add support for multiple types for collection keys & values
|
||||
* Deprecate the `Type::getCollectionKeyType()` and `Type::getCollectionValueType()` methods, use `Type::getCollectionKeyTypes()` and `Type::getCollectionValueTypes()` instead
|
||||
|
||||
5.2.0
|
||||
-----
|
||||
|
||||
* deprecated the `enable_magic_call_extraction` context option in `ReflectionExtractor::getWriteInfo()` and `ReflectionExtractor::getReadInfo()` in favor of `enable_magic_methods_extraction`
|
||||
|
||||
5.1.0
|
||||
-----
|
||||
|
||||
* Add support for extracting accessor and mutator via PHP Reflection
|
||||
|
||||
4.3.0
|
||||
-----
|
||||
|
||||
* Added the ability to extract private and protected properties and methods on `ReflectionExtractor`
|
||||
* Added the ability to extract property type based on its initial value
|
||||
|
||||
4.2.0
|
||||
-----
|
||||
|
||||
* added `PropertyInitializableExtractorInterface` to test if a property can be initialized through the constructor (implemented by `ReflectionExtractor`)
|
||||
|
||||
3.3.0
|
||||
-----
|
||||
|
||||
* Added `PropertyInfoPass`
|
||||
38
vendor/symfony/property-info/DependencyInjection/PropertyInfoConstructorPass.php
vendored
Normal file
38
vendor/symfony/property-info/DependencyInjection/PropertyInfoConstructorPass.php
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\PropertyInfo\DependencyInjection;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use Symfony\Component\DependencyInjection\Compiler\PriorityTaggedServiceTrait;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
|
||||
/**
|
||||
* Adds extractors to the property_info.constructor_extractor service.
|
||||
*
|
||||
* @author Dmitrii Poddubnyi <dpoddubny@gmail.com>
|
||||
*/
|
||||
final class PropertyInfoConstructorPass implements CompilerPassInterface
|
||||
{
|
||||
use PriorityTaggedServiceTrait;
|
||||
|
||||
public function process(ContainerBuilder $container): void
|
||||
{
|
||||
if (!$container->hasDefinition('property_info.constructor_extractor')) {
|
||||
return;
|
||||
}
|
||||
$definition = $container->getDefinition('property_info.constructor_extractor');
|
||||
|
||||
$listExtractors = $this->findAndSortTaggedServices('property_info.constructor_extractor', $container);
|
||||
$definition->replaceArgument(0, new IteratorArgument($listExtractors));
|
||||
}
|
||||
}
|
||||
54
vendor/symfony/property-info/DependencyInjection/PropertyInfoPass.php
vendored
Normal file
54
vendor/symfony/property-info/DependencyInjection/PropertyInfoPass.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 service.
|
||||
*
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
class PropertyInfoPass implements CompilerPassInterface
|
||||
{
|
||||
use PriorityTaggedServiceTrait;
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function process(ContainerBuilder $container)
|
||||
{
|
||||
if (!$container->hasDefinition('property_info')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$definition = $container->getDefinition('property_info');
|
||||
|
||||
$listExtractors = $this->findAndSortTaggedServices('property_info.list_extractor', $container);
|
||||
$definition->replaceArgument(0, new IteratorArgument($listExtractors));
|
||||
|
||||
$typeExtractors = $this->findAndSortTaggedServices('property_info.type_extractor', $container);
|
||||
$definition->replaceArgument(1, new IteratorArgument($typeExtractors));
|
||||
|
||||
$descriptionExtractors = $this->findAndSortTaggedServices('property_info.description_extractor', $container);
|
||||
$definition->replaceArgument(2, new IteratorArgument($descriptionExtractors));
|
||||
|
||||
$accessExtractors = $this->findAndSortTaggedServices('property_info.access_extractor', $container);
|
||||
$definition->replaceArgument(3, new IteratorArgument($accessExtractors));
|
||||
|
||||
$initializableExtractors = $this->findAndSortTaggedServices('property_info.initializable_extractor', $container);
|
||||
$definition->setArgument(4, new IteratorArgument($initializableExtractors));
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
42
vendor/symfony/property-info/Extractor/ConstructorExtractor.php
vendored
Normal file
42
vendor/symfony/property-info/Extractor/ConstructorExtractor.php
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
<?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
|
||||
{
|
||||
/**
|
||||
* @param iterable<int, ConstructorArgumentTypeExtractorInterface> $extractors
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly iterable $extractors = [],
|
||||
) {
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
348
vendor/symfony/property-info/Extractor/PhpDocExtractor.php
vendored
Normal file
348
vendor/symfony/property-info/Extractor/PhpDocExtractor.php
vendored
Normal file
@@ -0,0 +1,348 @@
|
||||
<?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 array $docBlocks = [];
|
||||
|
||||
/**
|
||||
* @var Context[]
|
||||
*/
|
||||
private array $contexts = [];
|
||||
|
||||
private DocBlockFactoryInterface $docBlockFactory;
|
||||
private ContextFactory $contextFactory;
|
||||
private PhpDocTypeHelper $phpDocTypeHelper;
|
||||
private array $mutatorPrefixes;
|
||||
private array $accessorPrefixes;
|
||||
private array $arrayMutatorPrefixes;
|
||||
|
||||
/**
|
||||
* @param string[]|null $mutatorPrefixes
|
||||
* @param string[]|null $accessorPrefixes
|
||||
* @param string[]|null $arrayMutatorPrefixes
|
||||
*/
|
||||
public function __construct(?DocBlockFactoryInterface $docBlockFactory = null, ?array $mutatorPrefixes = null, ?array $accessorPrefixes = null, ?array $arrayMutatorPrefixes = null)
|
||||
{
|
||||
if (!class_exists(DocBlockFactory::class)) {
|
||||
throw new \LogicException(sprintf('Unable to use the "%s" class as the "phpdocumentor/reflection-docblock" package is not installed. Try running composer require "phpdocumentor/reflection-docblock".', __CLASS__));
|
||||
}
|
||||
|
||||
$this->docBlockFactory = $docBlockFactory ?: DocBlockFactory::createInstance();
|
||||
$this->contextFactory = new ContextFactory();
|
||||
$this->phpDocTypeHelper = new PhpDocTypeHelper();
|
||||
$this->mutatorPrefixes = $mutatorPrefixes ?? ReflectionExtractor::$defaultMutatorPrefixes;
|
||||
$this->accessorPrefixes = $accessorPrefixes ?? ReflectionExtractor::$defaultAccessorPrefixes;
|
||||
$this->arrayMutatorPrefixes = $arrayMutatorPrefixes ?? ReflectionExtractor::$defaultArrayMutatorPrefixes;
|
||||
}
|
||||
|
||||
public function getShortDescription(string $class, string $property, array $context = []): ?string
|
||||
{
|
||||
/** @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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public function getTypes(string $class, string $property, array $context = []): ?array
|
||||
{
|
||||
/** @var $docBlock DocBlock */
|
||||
[$docBlock, $source, $prefix] = $this->getDocBlock($class, $property);
|
||||
if (!$docBlock) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$tag = match ($source) {
|
||||
self::PROPERTY => 'var',
|
||||
self::ACCESSOR => 'return',
|
||||
self::MUTATOR => 'param',
|
||||
};
|
||||
|
||||
$parentClass = null;
|
||||
$types = [];
|
||||
/** @var DocBlock\Tags\Var_|DocBlock\Tags\Return_|DocBlock\Tags\Param $tag */
|
||||
foreach ($docBlock->getTagsByName($tag) as $tag) {
|
||||
if ($tag && !$tag instanceof InvalidTag && null !== $tag->getType()) {
|
||||
foreach ($this->phpDocTypeHelper->getTypes($tag->getType()) as $type) {
|
||||
switch ($type->getClassName()) {
|
||||
case 'self':
|
||||
case 'static':
|
||||
$resolvedClass = $class;
|
||||
break;
|
||||
|
||||
case 'parent':
|
||||
if (false !== $resolvedClass = $parentClass ??= get_parent_class($class)) {
|
||||
break;
|
||||
}
|
||||
// no break
|
||||
|
||||
default:
|
||||
$types[] = $type;
|
||||
continue 2;
|
||||
}
|
||||
|
||||
$types[] = new 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])];
|
||||
}
|
||||
|
||||
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]) || [] === $types[0]) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return array_merge([], ...$types);
|
||||
}
|
||||
|
||||
private function getDocBlockFromConstructor(string $class, string $property): ?DocBlock
|
||||
{
|
||||
try {
|
||||
$reflectionClass = new \ReflectionClass($class);
|
||||
} catch (\ReflectionException) {
|
||||
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) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private function filterDocBlockParams(DocBlock $docBlock, string $allowedParam): DocBlock
|
||||
{
|
||||
$tags = array_values(array_filter($docBlock->getTagsByName('param'), fn ($tag) => $tag instanceof DocBlock\Tags\Param && $allowedParam === $tag->getVariableName()));
|
||||
|
||||
return new DocBlock($docBlock->getSummary(), $docBlock->getDescription(), $tags, $docBlock->getContext(),
|
||||
$docBlock->getLocation(), $docBlock->isTemplateStart(), $docBlock->isTemplateEnd());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{DocBlock|null, int|null, string|null}
|
||||
*/
|
||||
private function getDocBlock(string $class, string $property): array
|
||||
{
|
||||
$propertyHash = sprintf('%s::%s', $class, $property);
|
||||
|
||||
if (isset($this->docBlocks[$propertyHash])) {
|
||||
return $this->docBlocks[$propertyHash];
|
||||
}
|
||||
|
||||
try {
|
||||
$reflectionProperty = new \ReflectionProperty($class, $property);
|
||||
} catch (\ReflectionException) {
|
||||
$reflectionProperty = null;
|
||||
}
|
||||
|
||||
$ucFirstProperty = ucfirst($property);
|
||||
|
||||
switch (true) {
|
||||
case $reflectionProperty?->isPromoted() && $docBlock = $this->getDocBlockFromConstructor($class, $property):
|
||||
$data = [$docBlock, self::MUTATOR, null];
|
||||
break;
|
||||
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
// 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) {
|
||||
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];
|
||||
}
|
||||
}
|
||||
323
vendor/symfony/property-info/Extractor/PhpStanExtractor.php
vendored
Normal file
323
vendor/symfony/property-info/Extractor/PhpStanExtractor.php
vendored
Normal file
@@ -0,0 +1,323 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\PropertyInfo\Extractor;
|
||||
|
||||
use phpDocumentor\Reflection\Types\ContextFactory;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\InvalidTagValueNode;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\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 PHPStan\PhpDocParser\ParserConfig;
|
||||
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;
|
||||
|
||||
private PhpDocParser $phpDocParser;
|
||||
private Lexer $lexer;
|
||||
private NameScopeFactory $nameScopeFactory;
|
||||
|
||||
/** @var array<string, array{PhpDocNode|null, int|null, string|null, string|null}> */
|
||||
private array $docBlocks = [];
|
||||
private PhpStanTypeHelper $phpStanTypeHelper;
|
||||
private array $mutatorPrefixes;
|
||||
private array $accessorPrefixes;
|
||||
private array $arrayMutatorPrefixes;
|
||||
|
||||
/**
|
||||
* @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)
|
||||
{
|
||||
if (!class_exists(ContextFactory::class)) {
|
||||
throw new \LogicException(sprintf('Unable to use the "%s" class as the "phpdocumentor/type-resolver" package is not installed. Try running composer require "phpdocumentor/type-resolver".', __CLASS__));
|
||||
}
|
||||
|
||||
if (!class_exists(PhpDocParser::class)) {
|
||||
throw new \LogicException(sprintf('Unable to use the "%s" class as the "phpstan/phpdoc-parser" package is not installed. Try running composer require "phpstan/phpdoc-parser".', __CLASS__));
|
||||
}
|
||||
|
||||
$this->phpStanTypeHelper = new PhpStanTypeHelper();
|
||||
$this->mutatorPrefixes = $mutatorPrefixes ?? ReflectionExtractor::$defaultMutatorPrefixes;
|
||||
$this->accessorPrefixes = $accessorPrefixes ?? ReflectionExtractor::$defaultAccessorPrefixes;
|
||||
$this->arrayMutatorPrefixes = $arrayMutatorPrefixes ?? ReflectionExtractor::$defaultArrayMutatorPrefixes;
|
||||
|
||||
if (class_exists(ParserConfig::class)) {
|
||||
$parserConfig = new ParserConfig([]);
|
||||
$this->phpDocParser = new PhpDocParser($parserConfig, new TypeParser($parserConfig, new ConstExprParser($parserConfig)), new ConstExprParser($parserConfig));
|
||||
$this->lexer = new Lexer($parserConfig);
|
||||
} else {
|
||||
$this->phpDocParser = new PhpDocParser(new TypeParser(new ConstExprParser()), new ConstExprParser());
|
||||
$this->lexer = new Lexer();
|
||||
}
|
||||
$this->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;
|
||||
}
|
||||
|
||||
if (
|
||||
$tagDocNode->value instanceof ParamTagValueNode
|
||||
&& null === $prefix
|
||||
&& $tagDocNode->value->parameterName !== '$'.$property
|
||||
) {
|
||||
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 ??= 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) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (null === $reflectionConstructor = $reflectionClass->getConstructor()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!$rawDocNode = $reflectionConstructor->getDocComment()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$phpDocNode = $this->getPhpDocNode($rawDocNode);
|
||||
|
||||
return $this->filterDocBlockParams($phpDocNode, $property);
|
||||
}
|
||||
|
||||
private function filterDocBlockParams(PhpDocNode $docNode, string $allowedParam): ?ParamTagValueNode
|
||||
{
|
||||
$tags = array_values(array_filter($docNode->getTagsByName('@param'), fn ($tagNode) => $tagNode instanceof PhpDocTagNode && ('$'.$allowedParam) === $tagNode->value->parameterName));
|
||||
|
||||
if (!$tags) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $tags[0]->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{PhpDocNode|null, int|null, string|null, string|null}
|
||||
*/
|
||||
private function getDocBlock(string $class, string $property): array
|
||||
{
|
||||
$propertyHash = $class.'::'.$property;
|
||||
|
||||
if (isset($this->docBlocks[$propertyHash])) {
|
||||
return $this->docBlocks[$propertyHash];
|
||||
}
|
||||
|
||||
$ucFirstProperty = ucfirst($property);
|
||||
|
||||
if ([$docBlock, $source, $declaringClass] = $this->getDocBlockFromProperty($class, $property)) {
|
||||
$data = [$docBlock, $source, null, $declaringClass];
|
||||
} elseif ([$docBlock, $_, $declaringClass] = $this->getDocBlockFromMethod($class, $ucFirstProperty, self::ACCESSOR)) {
|
||||
$data = [$docBlock, self::ACCESSOR, null, $declaringClass];
|
||||
} elseif ([$docBlock, $prefix, $declaringClass] = $this->getDocBlockFromMethod($class, $ucFirstProperty, self::MUTATOR)) {
|
||||
$data = [$docBlock, self::MUTATOR, $prefix, $declaringClass];
|
||||
} else {
|
||||
$data = [null, null, null, null];
|
||||
}
|
||||
|
||||
return $this->docBlocks[$propertyHash] = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{PhpDocNode, int, string}|null
|
||||
*/
|
||||
private function getDocBlockFromProperty(string $class, string $property): ?array
|
||||
{
|
||||
// Use a ReflectionProperty instead of $class to get the parent class if applicable
|
||||
try {
|
||||
$reflectionProperty = new \ReflectionProperty($class, $property);
|
||||
} catch (\ReflectionException) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$reflector = $reflectionProperty->getDeclaringClass();
|
||||
|
||||
foreach ($reflector->getTraits() as $trait) {
|
||||
if ($trait->hasProperty($property)) {
|
||||
return $this->getDocBlockFromProperty($trait->getName(), $property);
|
||||
}
|
||||
}
|
||||
|
||||
// Type can be inside property docblock as `@var`
|
||||
$rawDocNode = $reflectionProperty->getDocComment();
|
||||
$phpDocNode = $rawDocNode ? $this->getPhpDocNode($rawDocNode) : null;
|
||||
$source = self::PROPERTY;
|
||||
|
||||
if (!$phpDocNode?->getTagsByName('@var')) {
|
||||
$phpDocNode = null;
|
||||
}
|
||||
|
||||
// or in the constructor as `@param` for promoted properties
|
||||
if (!$phpDocNode && $reflectionProperty->isPromoted()) {
|
||||
$constructor = new \ReflectionMethod($class, '__construct');
|
||||
$rawDocNode = $constructor->getDocComment();
|
||||
$phpDocNode = $rawDocNode ? $this->getPhpDocNode($rawDocNode) : null;
|
||||
$source = self::MUTATOR;
|
||||
}
|
||||
|
||||
if (!$phpDocNode) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [$phpDocNode, $source, $reflectionProperty->class];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{PhpDocNode, string, string}|null
|
||||
*/
|
||||
private function getDocBlockFromMethod(string $class, string $ucFirstProperty, int $type): ?array
|
||||
{
|
||||
$prefixes = self::ACCESSOR === $type ? $this->accessorPrefixes : $this->mutatorPrefixes;
|
||||
$prefix = null;
|
||||
|
||||
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) {
|
||||
// Try the next prefix if the method doesn't exist
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($reflectionMethod)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (null === $rawDocNode = $reflectionMethod->getDocComment() ?: null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$phpDocNode = $this->getPhpDocNode($rawDocNode);
|
||||
|
||||
return [$phpDocNode, $prefix, $reflectionMethod->class];
|
||||
}
|
||||
|
||||
private function getPhpDocNode(string $rawDocNode): PhpDocNode
|
||||
{
|
||||
$tokens = new TokenIterator($this->lexer->tokenize($rawDocNode));
|
||||
$phpDocNode = $this->phpDocParser->parse($tokens);
|
||||
$tokens->consumeTokenType(Lexer::TOKEN_END);
|
||||
|
||||
return $phpDocNode;
|
||||
}
|
||||
}
|
||||
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 array $defaultMutatorPrefixes = ['add', 'remove', 'set'];
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public static array $defaultAccessorPrefixes = ['get', 'is', 'has', 'can'];
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public static array $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 array $mutatorPrefixes;
|
||||
private array $accessorPrefixes;
|
||||
private array $arrayMutatorPrefixes;
|
||||
private bool $enableConstructorExtraction;
|
||||
private int $methodReflectionFlags;
|
||||
private int $magicMethodsFlags;
|
||||
private int $propertyReflectionFlags;
|
||||
private InflectorInterface $inflector;
|
||||
private array $arrayMutatorPrefixesFirst;
|
||||
private array $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);
|
||||
}
|
||||
|
||||
public function getProperties(string $class, array $context = []): ?array
|
||||
{
|
||||
try {
|
||||
$reflectionClass = new \ReflectionClass($class);
|
||||
} catch (\ReflectionException) {
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public function getTypesFromConstructor(string $class, string $property): ?array
|
||||
{
|
||||
try {
|
||||
$reflection = new \ReflectionClass($class);
|
||||
} catch (\ReflectionException) {
|
||||
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
|
||||
{
|
||||
foreach ($reflectionConstructor->getParameters() as $reflectionParameter) {
|
||||
if ($reflectionParameter->getName() === $property) {
|
||||
return $reflectionParameter;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function isReadable(string $class, string $property, array $context = []): ?bool
|
||||
{
|
||||
if ($this->isAllowedProperty($class, $property)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return null !== $this->getReadInfo($class, $property, $context);
|
||||
}
|
||||
|
||||
public function isWritable(string $class, string $property, array $context = []): ?bool
|
||||
{
|
||||
if ($this->isAllowedProperty($class, $property, true)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// First test with the camelized property name
|
||||
[$reflectionMethod] = $this->getMutatorMethod($class, $this->camelize($property));
|
||||
if (null !== $reflectionMethod) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Otherwise check for the old way
|
||||
[$reflectionMethod] = $this->getMutatorMethod($class, $property);
|
||||
|
||||
return null !== $reflectionMethod;
|
||||
}
|
||||
|
||||
public function isInitializable(string $class, string $property, array $context = []): ?bool
|
||||
{
|
||||
try {
|
||||
$reflectionClass = new \ReflectionClass($class);
|
||||
} catch (\ReflectionException) {
|
||||
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;
|
||||
}
|
||||
|
||||
public function getReadInfo(string $class, string $property, array $context = []): ?PropertyReadInfo
|
||||
{
|
||||
try {
|
||||
$reflClass = new \ReflectionClass($class);
|
||||
} catch (\ReflectionException) {
|
||||
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);
|
||||
$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') && (($r = $reflClass->getMethod('__get'))->getModifiers() & $this->methodReflectionFlags)) {
|
||||
return new PropertyReadInfo(PropertyReadInfo::TYPE_PROPERTY, $property, PropertyReadInfo::VISIBILITY_PUBLIC, false, $r->returnsReference());
|
||||
}
|
||||
|
||||
if ($hasProperty && (($r = $reflClass->getProperty($property))->getModifiers() & $this->propertyReflectionFlags)) {
|
||||
return new PropertyReadInfo(PropertyReadInfo::TYPE_PROPERTY, $property, $this->getReadVisiblityForProperty($r), $r->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;
|
||||
}
|
||||
|
||||
public function getWriteInfo(string $class, string $property, array $context = []): ?PropertyWriteInfo
|
||||
{
|
||||
try {
|
||||
$reflClass = new \ReflectionClass($class);
|
||||
} catch (\ReflectionException) {
|
||||
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);
|
||||
$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);
|
||||
if (!$reflProperty->isReadOnly()) {
|
||||
return new PropertyWriteInfo(PropertyWriteInfo::TYPE_PROPERTY, $property, $this->getWriteVisiblityForProperty($reflProperty), $reflProperty->isStatic());
|
||||
}
|
||||
|
||||
$errors[] = [sprintf('The property "%s" in class "%s" is a promoted readonly property.', $property, $reflClass->getName())];
|
||||
$allowMagicSet = $allowMagicCall = false;
|
||||
}
|
||||
|
||||
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, $this->isNullableProperty($class, $property), 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) {
|
||||
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);
|
||||
|
||||
$reflectionProperty = $reflectionClass->getProperty($property);
|
||||
$reflectionPropertyType = $reflectionProperty->getType();
|
||||
|
||||
if (null !== $reflectionPropertyType && $types = $this->extractFromReflectionType($reflectionPropertyType, $reflectionProperty->getDeclaringClass())) {
|
||||
return $types;
|
||||
}
|
||||
} catch (\ReflectionException) {
|
||||
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) {
|
||||
if (!$type instanceof \ReflectionNamedType) {
|
||||
// Nested composite types are not supported yet.
|
||||
return [];
|
||||
}
|
||||
|
||||
$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);
|
||||
|
||||
$reflectionPropertyType = $reflectionProperty->getType();
|
||||
|
||||
return null !== $reflectionPropertyType && $reflectionPropertyType->allowsNull();
|
||||
} catch (\ReflectionException) {
|
||||
// Return false if the property doesn't exist
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function isAllowedProperty(string $class, string $property, bool $writeAccessRequired = false): bool
|
||||
{
|
||||
try {
|
||||
$reflectionProperty = new \ReflectionProperty($class, $property);
|
||||
|
||||
if ($writeAccessRequired) {
|
||||
if ($reflectionProperty->isReadOnly()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (\PHP_VERSION_ID >= 80400 && $reflectionProperty->isProtectedSet()) {
|
||||
return (bool) ($this->propertyReflectionFlags & \ReflectionProperty::IS_PROTECTED);
|
||||
}
|
||||
|
||||
if (\PHP_VERSION_ID >= 80400 && $reflectionProperty->isPrivateSet()) {
|
||||
return (bool) ($this->propertyReflectionFlags & \ReflectionProperty::IS_PRIVATE);
|
||||
}
|
||||
|
||||
if (\PHP_VERSION_ID >= 80400 &&$reflectionProperty->isVirtual() && !$reflectionProperty->hasHook(\PropertyHookType::Set)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return (bool) ($reflectionProperty->getModifiers() & $this->propertyReflectionFlags);
|
||||
} catch (\ReflectionException) {
|
||||
// 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) {
|
||||
// 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) {
|
||||
// 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 (\PHP_VERSION_ID >= 80400) {
|
||||
if ($reflectionProperty->isVirtual() && !$reflectionProperty->hasHook(\PropertyHookType::Set)) {
|
||||
return PropertyWriteInfo::VISIBILITY_PRIVATE;
|
||||
}
|
||||
|
||||
if ($reflectionProperty->isPrivateSet()) {
|
||||
return PropertyWriteInfo::VISIBILITY_PRIVATE;
|
||||
}
|
||||
|
||||
if ($reflectionProperty->isProtectedSet()) {
|
||||
return PropertyWriteInfo::VISIBILITY_PROTECTED;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
52
vendor/symfony/property-info/Extractor/SerializerExtractor.php
vendored
Normal file
52
vendor/symfony/property-info/Extractor/SerializerExtractor.php
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\PropertyInfo\Extractor;
|
||||
|
||||
use Symfony\Component\PropertyInfo\PropertyListExtractorInterface;
|
||||
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface;
|
||||
|
||||
/**
|
||||
* Lists available properties using Symfony Serializer Component metadata.
|
||||
*
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
class SerializerExtractor implements PropertyListExtractorInterface
|
||||
{
|
||||
public function __construct(
|
||||
private readonly ClassMetadataFactoryInterface $classMetadataFactory,
|
||||
) {
|
||||
}
|
||||
|
||||
public function getProperties(string $class, array $context = []): ?array
|
||||
{
|
||||
if (!\array_key_exists('serializer_groups', $context) || (null !== $context['serializer_groups'] && !\is_array($context['serializer_groups']))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!$this->classMetadataFactory->hasMetadataFor($class)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$properties = [];
|
||||
$serializerClassMetadata = $this->classMetadataFactory->getMetadataFor($class);
|
||||
|
||||
foreach ($serializerClassMetadata->getAttributesMetadata() as $serializerAttributeMetadata) {
|
||||
if (!$serializerAttributeMetadata->isIgnored() && (null === $context['serializer_groups'] || 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-present Fabien Potencier
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
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 string $calledClassName;
|
||||
private string $namespace;
|
||||
/** @var array<string, string> alias(string) => fullName(string) */
|
||||
private array $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 (str_starts_with($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 ??= $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 = []);
|
||||
}
|
||||
30
vendor/symfony/property-info/PropertyDescriptionExtractorInterface.php
vendored
Normal file
30
vendor/symfony/property-info/PropertyDescriptionExtractorInterface.php
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\PropertyInfo;
|
||||
|
||||
/**
|
||||
* Guesses the property's human readable description.
|
||||
*
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
interface PropertyDescriptionExtractorInterface
|
||||
{
|
||||
/**
|
||||
* Gets the short description of the property.
|
||||
*/
|
||||
public function getShortDescription(string $class, string $property, array $context = []): ?string;
|
||||
|
||||
/**
|
||||
* Gets the long description of the property.
|
||||
*/
|
||||
public function getLongDescription(string $class, string $property, array $context = []): ?string;
|
||||
}
|
||||
99
vendor/symfony/property-info/PropertyInfoCacheExtractor.php
vendored
Normal file
99
vendor/symfony/property-info/PropertyInfoCacheExtractor.php
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
<?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 array $arrayCache = [];
|
||||
|
||||
public function __construct(
|
||||
private readonly PropertyInfoExtractorInterface $propertyInfoExtractor,
|
||||
private readonly CacheItemPoolInterface $cacheItemPool,
|
||||
) {
|
||||
}
|
||||
|
||||
public function isReadable(string $class, string $property, array $context = []): ?bool
|
||||
{
|
||||
return $this->extract('isReadable', [$class, $property, $context]);
|
||||
}
|
||||
|
||||
public function isWritable(string $class, string $property, array $context = []): ?bool
|
||||
{
|
||||
return $this->extract('isWritable', [$class, $property, $context]);
|
||||
}
|
||||
|
||||
public function getShortDescription(string $class, string $property, array $context = []): ?string
|
||||
{
|
||||
return $this->extract('getShortDescription', [$class, $property, $context]);
|
||||
}
|
||||
|
||||
public function getLongDescription(string $class, string $property, array $context = []): ?string
|
||||
{
|
||||
return $this->extract('getLongDescription', [$class, $property, $context]);
|
||||
}
|
||||
|
||||
public function getProperties(string $class, array $context = []): ?array
|
||||
{
|
||||
return $this->extract('getProperties', [$class, $context]);
|
||||
}
|
||||
|
||||
public function getTypes(string $class, string $property, array $context = []): ?array
|
||||
{
|
||||
return $this->extract('getTypes', [$class, $property, $context]);
|
||||
}
|
||||
|
||||
public function isInitializable(string $class, string $property, array $context = []): ?bool
|
||||
{
|
||||
return $this->extract('isInitializable', [$class, $property, $context]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the cached data if applicable or delegates to the decorated extractor.
|
||||
*/
|
||||
private function extract(string $method, array $arguments): mixed
|
||||
{
|
||||
try {
|
||||
$serializedArguments = serialize($arguments);
|
||||
} catch (\Exception) {
|
||||
// If arguments are not serializable, skip the cache
|
||||
return $this->propertyInfoExtractor->{$method}(...$arguments);
|
||||
}
|
||||
|
||||
// Calling rawurlencode escapes special characters not allowed in PSR-6's keys
|
||||
$key = rawurlencode($method.'.'.$serializedArguments);
|
||||
|
||||
if (\array_key_exists($key, $this->arrayCache)) {
|
||||
return $this->arrayCache[$key];
|
||||
}
|
||||
|
||||
$item = $this->cacheItemPool->getItem($key);
|
||||
|
||||
if ($item->isHit()) {
|
||||
return $this->arrayCache[$key] = $item->get();
|
||||
}
|
||||
|
||||
$value = $this->propertyInfoExtractor->{$method}(...$arguments);
|
||||
$item->set($value);
|
||||
$this->cacheItemPool->save($item);
|
||||
|
||||
return $this->arrayCache[$key] = $value;
|
||||
}
|
||||
}
|
||||
90
vendor/symfony/property-info/PropertyInfoExtractor.php
vendored
Normal file
90
vendor/symfony/property-info/PropertyInfoExtractor.php
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
<?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
|
||||
{
|
||||
/**
|
||||
* @param iterable<mixed, PropertyListExtractorInterface> $listExtractors
|
||||
* @param iterable<mixed, PropertyTypeExtractorInterface> $typeExtractors
|
||||
* @param iterable<mixed, PropertyDescriptionExtractorInterface> $descriptionExtractors
|
||||
* @param iterable<mixed, PropertyAccessExtractorInterface> $accessExtractors
|
||||
* @param iterable<mixed, PropertyInitializableExtractorInterface> $initializableExtractors
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly iterable $listExtractors = [],
|
||||
private readonly iterable $typeExtractors = [],
|
||||
private readonly iterable $descriptionExtractors = [],
|
||||
private readonly iterable $accessExtractors = [],
|
||||
private readonly iterable $initializableExtractors = [],
|
||||
) {
|
||||
}
|
||||
|
||||
public function getProperties(string $class, array $context = []): ?array
|
||||
{
|
||||
return $this->extract($this->listExtractors, 'getProperties', [$class, $context]);
|
||||
}
|
||||
|
||||
public function getShortDescription(string $class, string $property, array $context = []): ?string
|
||||
{
|
||||
return $this->extract($this->descriptionExtractors, 'getShortDescription', [$class, $property, $context]);
|
||||
}
|
||||
|
||||
public function getLongDescription(string $class, string $property, array $context = []): ?string
|
||||
{
|
||||
return $this->extract($this->descriptionExtractors, 'getLongDescription', [$class, $property, $context]);
|
||||
}
|
||||
|
||||
public function getTypes(string $class, string $property, array $context = []): ?array
|
||||
{
|
||||
return $this->extract($this->typeExtractors, 'getTypes', [$class, $property, $context]);
|
||||
}
|
||||
|
||||
public function isReadable(string $class, string $property, array $context = []): ?bool
|
||||
{
|
||||
return $this->extract($this->accessExtractors, 'isReadable', [$class, $property, $context]);
|
||||
}
|
||||
|
||||
public function isWritable(string $class, string $property, array $context = []): ?bool
|
||||
{
|
||||
return $this->extract($this->accessExtractors, 'isWritable', [$class, $property, $context]);
|
||||
}
|
||||
|
||||
public function isInitializable(string $class, string $property, array $context = []): ?bool
|
||||
{
|
||||
return $this->extract($this->initializableExtractors, 'isInitializable', [$class, $property, $context]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates over registered extractors and return the first value found.
|
||||
*
|
||||
* @param iterable<mixed, object> $extractors
|
||||
* @param list<mixed> $arguments
|
||||
*/
|
||||
private function extract(iterable $extractors, string $method, array $arguments): mixed
|
||||
{
|
||||
foreach ($extractors as $extractor) {
|
||||
if (null !== $value = $extractor->{$method}(...$arguments)) {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
23
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 = []);
|
||||
}
|
||||
70
vendor/symfony/property-info/PropertyReadInfo.php
vendored
Normal file
70
vendor/symfony/property-info/PropertyReadInfo.php
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\PropertyInfo;
|
||||
|
||||
/**
|
||||
* The property read info tells how a property can be read.
|
||||
*
|
||||
* @author Joel Wurtz <jwurtz@jolicode.com>
|
||||
*/
|
||||
final class PropertyReadInfo
|
||||
{
|
||||
public const TYPE_METHOD = 'method';
|
||||
public const TYPE_PROPERTY = 'property';
|
||||
|
||||
public const VISIBILITY_PUBLIC = 'public';
|
||||
public const VISIBILITY_PROTECTED = 'protected';
|
||||
public const VISIBILITY_PRIVATE = 'private';
|
||||
|
||||
public function __construct(
|
||||
private readonly string $type,
|
||||
private readonly string $name,
|
||||
private readonly string $visibility,
|
||||
private readonly bool $static,
|
||||
private readonly bool $byRef,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get type of access.
|
||||
*/
|
||||
public function getType(): string
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get name of the access, which can be a method name or a property name, depending on the type.
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function getVisibility(): string
|
||||
{
|
||||
return $this->visibility;
|
||||
}
|
||||
|
||||
public function isStatic(): bool
|
||||
{
|
||||
return $this->static;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this accessor can be accessed by reference.
|
||||
*/
|
||||
public function canBeReference(): bool
|
||||
{
|
||||
return $this->byRef;
|
||||
}
|
||||
}
|
||||
25
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 = []);
|
||||
}
|
||||
117
vendor/symfony/property-info/PropertyWriteInfo.php
vendored
Normal file
117
vendor/symfony/property-info/PropertyWriteInfo.php
vendored
Normal file
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\PropertyInfo;
|
||||
|
||||
/**
|
||||
* The write mutator defines how a property can be written.
|
||||
*
|
||||
* @author Joel Wurtz <jwurtz@jolicode.com>
|
||||
*/
|
||||
final class PropertyWriteInfo
|
||||
{
|
||||
public const TYPE_NONE = 'none';
|
||||
public const TYPE_METHOD = 'method';
|
||||
public const TYPE_PROPERTY = 'property';
|
||||
public const TYPE_ADDER_AND_REMOVER = 'adder_and_remover';
|
||||
public const TYPE_CONSTRUCTOR = 'constructor';
|
||||
|
||||
public const VISIBILITY_PUBLIC = 'public';
|
||||
public const VISIBILITY_PROTECTED = 'protected';
|
||||
public const VISIBILITY_PRIVATE = 'private';
|
||||
|
||||
private ?self $adderInfo = null;
|
||||
private ?self $removerInfo = null;
|
||||
private array $errors = [];
|
||||
|
||||
public function __construct(
|
||||
private readonly string $type = self::TYPE_NONE,
|
||||
private readonly ?string $name = null,
|
||||
private readonly ?string $visibility = null,
|
||||
private readonly ?bool $static = null,
|
||||
) {
|
||||
}
|
||||
|
||||
public function getType(): string
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
if (null === $this->name) {
|
||||
throw new \LogicException("Calling getName() when having a mutator of type {$this->type} is not tolerated.");
|
||||
}
|
||||
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function setAdderInfo(self $adderInfo): void
|
||||
{
|
||||
$this->adderInfo = $adderInfo;
|
||||
}
|
||||
|
||||
public function getAdderInfo(): self
|
||||
{
|
||||
if (null === $this->adderInfo) {
|
||||
throw new \LogicException("Calling getAdderInfo() when having a mutator of type {$this->type} is not tolerated.");
|
||||
}
|
||||
|
||||
return $this->adderInfo;
|
||||
}
|
||||
|
||||
public function setRemoverInfo(self $removerInfo): void
|
||||
{
|
||||
$this->removerInfo = $removerInfo;
|
||||
}
|
||||
|
||||
public function getRemoverInfo(): self
|
||||
{
|
||||
if (null === $this->removerInfo) {
|
||||
throw new \LogicException("Calling getRemoverInfo() when having a mutator of type {$this->type} is not tolerated.");
|
||||
}
|
||||
|
||||
return $this->removerInfo;
|
||||
}
|
||||
|
||||
public function getVisibility(): string
|
||||
{
|
||||
if (null === $this->visibility) {
|
||||
throw new \LogicException("Calling getVisibility() when having a mutator of type {$this->type} is not tolerated.");
|
||||
}
|
||||
|
||||
return $this->visibility;
|
||||
}
|
||||
|
||||
public function isStatic(): bool
|
||||
{
|
||||
if (null === $this->static) {
|
||||
throw new \LogicException("Calling isStatic() when having a mutator of type {$this->type} is not tolerated.");
|
||||
}
|
||||
|
||||
return $this->static;
|
||||
}
|
||||
|
||||
public function setErrors(array $errors): void
|
||||
{
|
||||
$this->errors = $errors;
|
||||
}
|
||||
|
||||
public function getErrors(): array
|
||||
{
|
||||
return $this->errors;
|
||||
}
|
||||
|
||||
public function hasErrors(): bool
|
||||
{
|
||||
return (bool) \count($this->errors);
|
||||
}
|
||||
}
|
||||
25
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)
|
||||
81
vendor/symfony/property-info/Tests/AbstractPropertyInfoExtractorTest.php
vendored
Normal file
81
vendor/symfony/property-info/Tests/AbstractPropertyInfoExtractorTest.php
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
<?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\Tests;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\PropertyInfo\PropertyAccessExtractorInterface;
|
||||
use Symfony\Component\PropertyInfo\PropertyDescriptionExtractorInterface;
|
||||
use Symfony\Component\PropertyInfo\PropertyInfoExtractor;
|
||||
use Symfony\Component\PropertyInfo\PropertyInfoExtractorInterface;
|
||||
use Symfony\Component\PropertyInfo\PropertyInitializableExtractorInterface;
|
||||
use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
|
||||
use Symfony\Component\PropertyInfo\Tests\Fixtures\DummyExtractor;
|
||||
use Symfony\Component\PropertyInfo\Tests\Fixtures\NullExtractor;
|
||||
use Symfony\Component\PropertyInfo\Type;
|
||||
|
||||
/**
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
class AbstractPropertyInfoExtractorTest extends TestCase
|
||||
{
|
||||
protected PropertyInfoExtractorInterface $propertyInfo;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$extractors = [new NullExtractor(), new DummyExtractor()];
|
||||
$this->propertyInfo = new PropertyInfoExtractor($extractors, $extractors, $extractors, $extractors, $extractors);
|
||||
}
|
||||
|
||||
public function testInstanceOf()
|
||||
{
|
||||
$this->assertInstanceOf(PropertyInfoExtractorInterface::class, $this->propertyInfo);
|
||||
$this->assertInstanceOf(PropertyTypeExtractorInterface::class, $this->propertyInfo);
|
||||
$this->assertInstanceOf(PropertyDescriptionExtractorInterface::class, $this->propertyInfo);
|
||||
$this->assertInstanceOf(PropertyAccessExtractorInterface::class, $this->propertyInfo);
|
||||
$this->assertInstanceOf(PropertyInitializableExtractorInterface::class, $this->propertyInfo);
|
||||
}
|
||||
|
||||
public function testGetShortDescription()
|
||||
{
|
||||
$this->assertSame('short', $this->propertyInfo->getShortDescription('Foo', 'bar', []));
|
||||
}
|
||||
|
||||
public function testGetLongDescription()
|
||||
{
|
||||
$this->assertSame('long', $this->propertyInfo->getLongDescription('Foo', 'bar', []));
|
||||
}
|
||||
|
||||
public function testGetTypes()
|
||||
{
|
||||
$this->assertEquals([new Type(Type::BUILTIN_TYPE_INT)], $this->propertyInfo->getTypes('Foo', 'bar', []));
|
||||
}
|
||||
|
||||
public function testIsReadable()
|
||||
{
|
||||
$this->assertTrue($this->propertyInfo->isReadable('Foo', 'bar', []));
|
||||
}
|
||||
|
||||
public function testIsWritable()
|
||||
{
|
||||
$this->assertTrue($this->propertyInfo->isWritable('Foo', 'bar', []));
|
||||
}
|
||||
|
||||
public function testGetProperties()
|
||||
{
|
||||
$this->assertEquals(['a', 'b'], $this->propertyInfo->getProperties('Foo'));
|
||||
}
|
||||
|
||||
public function testIsInitializable()
|
||||
{
|
||||
$this->assertTrue($this->propertyInfo->isInitializable('Foo', 'bar', []));
|
||||
}
|
||||
}
|
||||
54
vendor/symfony/property-info/Tests/DependencyInjection/PropertyInfoConstructorPassTest.php
vendored
Normal file
54
vendor/symfony/property-info/Tests/DependencyInjection/PropertyInfoConstructorPassTest.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\Tests\DependencyInjection;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
use Symfony\Component\PropertyInfo\DependencyInjection\PropertyInfoConstructorPass;
|
||||
|
||||
class PropertyInfoConstructorPassTest extends TestCase
|
||||
{
|
||||
public function testServicesAreOrderedAccordingToPriority()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
|
||||
$tag = 'property_info.constructor_extractor';
|
||||
$definition = $container->register('property_info.constructor_extractor')->setArguments([null, null]);
|
||||
$container->register('n2')->addTag($tag, ['priority' => 100]);
|
||||
$container->register('n1')->addTag($tag, ['priority' => 200]);
|
||||
$container->register('n3')->addTag($tag);
|
||||
|
||||
$pass = new PropertyInfoConstructorPass();
|
||||
$pass->process($container);
|
||||
|
||||
$expected = new IteratorArgument([
|
||||
new Reference('n1'),
|
||||
new Reference('n2'),
|
||||
new Reference('n3'),
|
||||
]);
|
||||
$this->assertEquals($expected, $definition->getArgument(0));
|
||||
}
|
||||
|
||||
public function testReturningEmptyArrayWhenNoService()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
$propertyInfoExtractorDefinition = $container->register('property_info.constructor_extractor')
|
||||
->setArguments([[]]);
|
||||
|
||||
$pass = new PropertyInfoConstructorPass();
|
||||
$pass->process($container);
|
||||
|
||||
$this->assertEquals(new IteratorArgument([]), $propertyInfoExtractorDefinition->getArgument(0));
|
||||
}
|
||||
}
|
||||
71
vendor/symfony/property-info/Tests/DependencyInjection/PropertyInfoPassTest.php
vendored
Normal file
71
vendor/symfony/property-info/Tests/DependencyInjection/PropertyInfoPassTest.php
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
<?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\Tests\DependencyInjection;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
use Symfony\Component\PropertyInfo\DependencyInjection\PropertyInfoPass;
|
||||
|
||||
class PropertyInfoPassTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider provideTags
|
||||
*/
|
||||
public function testServicesAreOrderedAccordingToPriority($index, $tag)
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
|
||||
$definition = $container->register('property_info')->setArguments([null, null, null, null, null]);
|
||||
$container->register('n2')->addTag($tag, ['priority' => 100]);
|
||||
$container->register('n1')->addTag($tag, ['priority' => 200]);
|
||||
$container->register('n3')->addTag($tag);
|
||||
|
||||
$propertyInfoPass = new PropertyInfoPass();
|
||||
$propertyInfoPass->process($container);
|
||||
|
||||
$expected = new IteratorArgument([
|
||||
new Reference('n1'),
|
||||
new Reference('n2'),
|
||||
new Reference('n3'),
|
||||
]);
|
||||
$this->assertEquals($expected, $definition->getArgument($index));
|
||||
}
|
||||
|
||||
public static function provideTags()
|
||||
{
|
||||
return [
|
||||
[0, 'property_info.list_extractor'],
|
||||
[1, 'property_info.type_extractor'],
|
||||
[2, 'property_info.description_extractor'],
|
||||
[3, 'property_info.access_extractor'],
|
||||
[4, 'property_info.initializable_extractor'],
|
||||
];
|
||||
}
|
||||
|
||||
public function testReturningEmptyArrayWhenNoService()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
$propertyInfoExtractorDefinition = $container->register('property_info')
|
||||
->setArguments([[], [], [], [], []]);
|
||||
|
||||
$propertyInfoPass = new PropertyInfoPass();
|
||||
$propertyInfoPass->process($container);
|
||||
|
||||
$this->assertEquals(new IteratorArgument([]), $propertyInfoExtractorDefinition->getArgument(0));
|
||||
$this->assertEquals(new IteratorArgument([]), $propertyInfoExtractorDefinition->getArgument(1));
|
||||
$this->assertEquals(new IteratorArgument([]), $propertyInfoExtractorDefinition->getArgument(2));
|
||||
$this->assertEquals(new IteratorArgument([]), $propertyInfoExtractorDefinition->getArgument(3));
|
||||
$this->assertEquals(new IteratorArgument([]), $propertyInfoExtractorDefinition->getArgument(4));
|
||||
}
|
||||
}
|
||||
46
vendor/symfony/property-info/Tests/Extractor/ConstructorExtractorTest.php
vendored
Normal file
46
vendor/symfony/property-info/Tests/Extractor/ConstructorExtractorTest.php
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
<?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\Tests\Extractor;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\PropertyInfo\Extractor\ConstructorExtractor;
|
||||
use Symfony\Component\PropertyInfo\Tests\Fixtures\DummyExtractor;
|
||||
use Symfony\Component\PropertyInfo\Type;
|
||||
|
||||
/**
|
||||
* @author Dmitrii Poddubnyi <dpoddubny@gmail.com>
|
||||
*/
|
||||
class ConstructorExtractorTest extends TestCase
|
||||
{
|
||||
private ConstructorExtractor $extractor;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->extractor = new ConstructorExtractor([new DummyExtractor()]);
|
||||
}
|
||||
|
||||
public function testInstanceOf()
|
||||
{
|
||||
$this->assertInstanceOf(\Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface::class, $this->extractor);
|
||||
}
|
||||
|
||||
public function testGetTypes()
|
||||
{
|
||||
$this->assertEquals([new Type(Type::BUILTIN_TYPE_STRING)], $this->extractor->getTypes('Foo', 'bar', []));
|
||||
}
|
||||
|
||||
public function testGetTypesIfNoExtractors()
|
||||
{
|
||||
$extractor = new ConstructorExtractor([]);
|
||||
$this->assertNull($extractor->getTypes('Foo', 'bar', []));
|
||||
}
|
||||
}
|
||||
483
vendor/symfony/property-info/Tests/Extractor/PhpDocExtractorTest.php
vendored
Normal file
483
vendor/symfony/property-info/Tests/Extractor/PhpDocExtractorTest.php
vendored
Normal file
@@ -0,0 +1,483 @@
|
||||
<?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\Tests\Extractor;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor;
|
||||
use Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy;
|
||||
use Symfony\Component\PropertyInfo\Tests\Fixtures\DummyCollection;
|
||||
use Symfony\Component\PropertyInfo\Tests\Fixtures\ParentDummy;
|
||||
use Symfony\Component\PropertyInfo\Tests\Fixtures\Php80Dummy;
|
||||
use Symfony\Component\PropertyInfo\Tests\Fixtures\PseudoTypeDummy;
|
||||
use Symfony\Component\PropertyInfo\Tests\Fixtures\TraitUsage\DummyUsedInTrait;
|
||||
use Symfony\Component\PropertyInfo\Tests\Fixtures\TraitUsage\DummyUsingTrait;
|
||||
use Symfony\Component\PropertyInfo\Type;
|
||||
|
||||
/**
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
class PhpDocExtractorTest extends TestCase
|
||||
{
|
||||
private PhpDocExtractor $extractor;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->extractor = new PhpDocExtractor();
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider typesProvider
|
||||
*/
|
||||
public function testExtract($property, ?array $type, $shortDescription, $longDescription)
|
||||
{
|
||||
$this->assertEquals($type, $this->extractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', $property));
|
||||
$this->assertSame($shortDescription, $this->extractor->getShortDescription('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', $property));
|
||||
$this->assertSame($longDescription, $this->extractor->getLongDescription('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', $property));
|
||||
}
|
||||
|
||||
public function testParamTagTypeIsOmitted()
|
||||
{
|
||||
$this->assertNull($this->extractor->getTypes(OmittedParamTagTypeDocBlock::class, 'omittedType'));
|
||||
}
|
||||
|
||||
public static function invalidTypesProvider()
|
||||
{
|
||||
return [
|
||||
'pub' => ['pub', null, null],
|
||||
'stat' => ['stat', null, null],
|
||||
'bar' => ['bar', 'Bar.', null],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider invalidTypesProvider
|
||||
*/
|
||||
public function testInvalid($property, $shortDescription, $longDescription)
|
||||
{
|
||||
$this->assertNull($this->extractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\InvalidDummy', $property));
|
||||
$this->assertSame($shortDescription, $this->extractor->getShortDescription('Symfony\Component\PropertyInfo\Tests\Fixtures\InvalidDummy', $property));
|
||||
$this->assertSame($longDescription, $this->extractor->getLongDescription('Symfony\Component\PropertyInfo\Tests\Fixtures\InvalidDummy', $property));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group legacy
|
||||
*/
|
||||
public function testEmptyParamAnnotation()
|
||||
{
|
||||
$this->assertNull($this->extractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\InvalidDummy', 'foo'));
|
||||
$this->assertSame('Foo.', $this->extractor->getShortDescription('Symfony\Component\PropertyInfo\Tests\Fixtures\InvalidDummy', 'foo'));
|
||||
$this->assertNull($this->extractor->getLongDescription('Symfony\Component\PropertyInfo\Tests\Fixtures\InvalidDummy', 'foo'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider typesWithNoPrefixesProvider
|
||||
*/
|
||||
public function testExtractTypesWithNoPrefixes($property, ?array $type = null)
|
||||
{
|
||||
$noPrefixExtractor = new PhpDocExtractor(null, [], [], []);
|
||||
|
||||
$this->assertEquals($type, $noPrefixExtractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', $property));
|
||||
}
|
||||
|
||||
public static function typesProvider()
|
||||
{
|
||||
return [
|
||||
['foo', null, 'Short description.', 'Long description.'],
|
||||
['bar', [new Type(Type::BUILTIN_TYPE_STRING)], 'This is bar', null],
|
||||
['baz', [new Type(Type::BUILTIN_TYPE_INT)], 'Should be used.', null],
|
||||
['foo2', [new Type(Type::BUILTIN_TYPE_FLOAT)], null, null],
|
||||
['foo3', [new Type(Type::BUILTIN_TYPE_CALLABLE)], null, null],
|
||||
['foo4', [new Type(Type::BUILTIN_TYPE_NULL)], null, null],
|
||||
['foo5', null, null, null],
|
||||
[
|
||||
'files',
|
||||
[
|
||||
new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_OBJECT, false, 'SplFileInfo')),
|
||||
new Type(Type::BUILTIN_TYPE_RESOURCE),
|
||||
],
|
||||
null,
|
||||
null,
|
||||
],
|
||||
['bal', [new Type(Type::BUILTIN_TYPE_OBJECT, false, 'DateTimeImmutable')], null, null],
|
||||
['parent', [new Type(Type::BUILTIN_TYPE_OBJECT, false, 'Symfony\Component\PropertyInfo\Tests\Fixtures\ParentDummy')], null, null],
|
||||
['collection', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_OBJECT, false, 'DateTimeImmutable'))], null, null],
|
||||
['nestedCollection', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_STRING, false)))], null, null],
|
||||
['mixedCollection', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, null, null)], null, null],
|
||||
['a', [new Type(Type::BUILTIN_TYPE_INT)], 'A.', null],
|
||||
['b', [new Type(Type::BUILTIN_TYPE_OBJECT, true, 'Symfony\Component\PropertyInfo\Tests\Fixtures\ParentDummy')], 'B.', null],
|
||||
['c', [new Type(Type::BUILTIN_TYPE_BOOL, true)], null, null],
|
||||
['ct', [new Type(Type::BUILTIN_TYPE_TRUE, true)], null, null],
|
||||
['cf', [new Type(Type::BUILTIN_TYPE_FALSE, true)], null, null],
|
||||
['d', [new Type(Type::BUILTIN_TYPE_BOOL)], null, null],
|
||||
['dt', [new Type(Type::BUILTIN_TYPE_TRUE)], null, null],
|
||||
['df', [new Type(Type::BUILTIN_TYPE_FALSE)], null, null],
|
||||
['e', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_RESOURCE))], null, null],
|
||||
['f', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_OBJECT, false, 'DateTimeImmutable'))], null, null],
|
||||
['g', [new Type(Type::BUILTIN_TYPE_ARRAY, true, null, true)], 'Nullable array.', null],
|
||||
['h', [new Type(Type::BUILTIN_TYPE_STRING, true)], null, null],
|
||||
['i', [new Type(Type::BUILTIN_TYPE_STRING, true), new Type(Type::BUILTIN_TYPE_INT, true)], null, null],
|
||||
['j', [new Type(Type::BUILTIN_TYPE_OBJECT, true, 'DateTimeImmutable')], null, null],
|
||||
['nullableCollectionOfNonNullableElements', [new Type(Type::BUILTIN_TYPE_ARRAY, true, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_INT, false))], null, null],
|
||||
['donotexist', null, null, null],
|
||||
['staticGetter', null, null, null],
|
||||
['staticSetter', null, null, null],
|
||||
['emptyVar', null, 'This should not be removed.', null],
|
||||
['arrayWithKeys', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_STRING), new Type(Type::BUILTIN_TYPE_STRING))], null, null],
|
||||
['arrayOfMixed', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_STRING), null)], null, null],
|
||||
['listOfStrings', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_STRING))], null, null],
|
||||
['self', [new Type(Type::BUILTIN_TYPE_OBJECT, false, Dummy::class)], null, null],
|
||||
['collectionAsObject', [new Type(Type::BUILTIN_TYPE_OBJECT, false, DummyCollection::class, true, [new Type(Type::BUILTIN_TYPE_INT)], [new Type(Type::BUILTIN_TYPE_STRING)])], null, null],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideCollectionTypes
|
||||
*/
|
||||
public function testExtractCollection($property, ?array $type, $shortDescription, $longDescription)
|
||||
{
|
||||
$this->testExtract($property, $type, $shortDescription, $longDescription);
|
||||
}
|
||||
|
||||
public static function provideCollectionTypes()
|
||||
{
|
||||
return [
|
||||
['iteratorCollection', [new Type(Type::BUILTIN_TYPE_OBJECT, false, 'Iterator', true, [new Type(Type::BUILTIN_TYPE_STRING), new Type(Type::BUILTIN_TYPE_INT)], new Type(Type::BUILTIN_TYPE_STRING))], null, null],
|
||||
['iteratorCollectionWithKey', [new Type(Type::BUILTIN_TYPE_OBJECT, false, 'Iterator', true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_STRING))], null, null],
|
||||
[
|
||||
'nestedIterators',
|
||||
[new Type(
|
||||
Type::BUILTIN_TYPE_OBJECT,
|
||||
false,
|
||||
'Iterator',
|
||||
true,
|
||||
new Type(Type::BUILTIN_TYPE_INT),
|
||||
new Type(Type::BUILTIN_TYPE_OBJECT, false, 'Iterator', true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_STRING))
|
||||
)],
|
||||
null,
|
||||
null,
|
||||
],
|
||||
[
|
||||
'arrayWithKeys',
|
||||
[new Type(
|
||||
Type::BUILTIN_TYPE_ARRAY,
|
||||
false,
|
||||
null,
|
||||
true,
|
||||
new Type(Type::BUILTIN_TYPE_STRING),
|
||||
new Type(Type::BUILTIN_TYPE_STRING)
|
||||
)],
|
||||
null,
|
||||
null,
|
||||
],
|
||||
[
|
||||
'arrayWithKeysAndComplexValue',
|
||||
[new Type(
|
||||
Type::BUILTIN_TYPE_ARRAY,
|
||||
false,
|
||||
null,
|
||||
true,
|
||||
new Type(Type::BUILTIN_TYPE_STRING),
|
||||
new Type(
|
||||
Type::BUILTIN_TYPE_ARRAY,
|
||||
true,
|
||||
null,
|
||||
true,
|
||||
new Type(Type::BUILTIN_TYPE_INT),
|
||||
new Type(Type::BUILTIN_TYPE_STRING, true)
|
||||
)
|
||||
)],
|
||||
null,
|
||||
null,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider typesWithCustomPrefixesProvider
|
||||
*/
|
||||
public function testExtractTypesWithCustomPrefixes($property, ?array $type = null)
|
||||
{
|
||||
$customExtractor = new PhpDocExtractor(null, ['add', 'remove'], ['is', 'can']);
|
||||
|
||||
$this->assertEquals($type, $customExtractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', $property));
|
||||
}
|
||||
|
||||
public static function typesWithCustomPrefixesProvider()
|
||||
{
|
||||
return [
|
||||
['foo', null, 'Short description.', 'Long description.'],
|
||||
['bar', [new Type(Type::BUILTIN_TYPE_STRING)], 'This is bar', null],
|
||||
['baz', [new Type(Type::BUILTIN_TYPE_INT)], 'Should be used.', null],
|
||||
['foo2', [new Type(Type::BUILTIN_TYPE_FLOAT)], null, null],
|
||||
['foo3', [new Type(Type::BUILTIN_TYPE_CALLABLE)], null, null],
|
||||
['foo4', [new Type(Type::BUILTIN_TYPE_NULL)], null, null],
|
||||
['foo5', null, null, null],
|
||||
[
|
||||
'files',
|
||||
[
|
||||
new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_OBJECT, false, 'SplFileInfo')),
|
||||
new Type(Type::BUILTIN_TYPE_RESOURCE),
|
||||
],
|
||||
null,
|
||||
null,
|
||||
],
|
||||
['bal', [new Type(Type::BUILTIN_TYPE_OBJECT, false, 'DateTimeImmutable')], null, null],
|
||||
['parent', [new Type(Type::BUILTIN_TYPE_OBJECT, false, 'Symfony\Component\PropertyInfo\Tests\Fixtures\ParentDummy')], null, null],
|
||||
['collection', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_OBJECT, false, 'DateTimeImmutable'))], null, null],
|
||||
['nestedCollection', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_STRING, false)))], null, null],
|
||||
['mixedCollection', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, null, null)], null, null],
|
||||
['a', null, 'A.', null],
|
||||
['b', null, 'B.', null],
|
||||
['c', [new Type(Type::BUILTIN_TYPE_BOOL, true)], null, null],
|
||||
['d', [new Type(Type::BUILTIN_TYPE_BOOL)], null, null],
|
||||
['e', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_RESOURCE))], null, null],
|
||||
['f', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_OBJECT, false, 'DateTimeImmutable'))], null, null],
|
||||
['g', [new Type(Type::BUILTIN_TYPE_ARRAY, true, null, true)], 'Nullable array.', null],
|
||||
['h', [new Type(Type::BUILTIN_TYPE_STRING, true)], null, null],
|
||||
['i', [new Type(Type::BUILTIN_TYPE_STRING, true), new Type(Type::BUILTIN_TYPE_INT, true)], null, null],
|
||||
['j', [new Type(Type::BUILTIN_TYPE_OBJECT, true, 'DateTimeImmutable')], null, null],
|
||||
['nullableCollectionOfNonNullableElements', [new Type(Type::BUILTIN_TYPE_ARRAY, true, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_INT, false))], null, null],
|
||||
['nonNullableCollectionOfNullableElements', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_INT, true))], null, null],
|
||||
['nullableCollectionOfMultipleNonNullableElementTypes', [new Type(Type::BUILTIN_TYPE_ARRAY, true, null, true, new Type(Type::BUILTIN_TYPE_INT), [new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_STRING)])], null, null],
|
||||
['donotexist', null, null, null],
|
||||
['staticGetter', null, null, null],
|
||||
['staticSetter', null, null, null],
|
||||
];
|
||||
}
|
||||
|
||||
public static function typesWithNoPrefixesProvider()
|
||||
{
|
||||
return [
|
||||
['foo', null, 'Short description.', 'Long description.'],
|
||||
['bar', [new Type(Type::BUILTIN_TYPE_STRING)], 'This is bar', null],
|
||||
['baz', [new Type(Type::BUILTIN_TYPE_INT)], 'Should be used.', null],
|
||||
['foo2', [new Type(Type::BUILTIN_TYPE_FLOAT)], null, null],
|
||||
['foo3', [new Type(Type::BUILTIN_TYPE_CALLABLE)], null, null],
|
||||
['foo4', [new Type(Type::BUILTIN_TYPE_NULL)], null, null],
|
||||
['foo5', null, null, null],
|
||||
[
|
||||
'files',
|
||||
[
|
||||
new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_OBJECT, false, 'SplFileInfo')),
|
||||
new Type(Type::BUILTIN_TYPE_RESOURCE),
|
||||
],
|
||||
null,
|
||||
null,
|
||||
],
|
||||
['bal', [new Type(Type::BUILTIN_TYPE_OBJECT, false, 'DateTimeImmutable')], null, null],
|
||||
['parent', [new Type(Type::BUILTIN_TYPE_OBJECT, false, 'Symfony\Component\PropertyInfo\Tests\Fixtures\ParentDummy')], null, null],
|
||||
['collection', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_OBJECT, false, 'DateTimeImmutable'))], null, null],
|
||||
['nestedCollection', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_STRING, false)))], null, null],
|
||||
['mixedCollection', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, null, null)], null, null],
|
||||
['a', null, 'A.', null],
|
||||
['b', null, 'B.', null],
|
||||
['c', null, null, null],
|
||||
['d', null, null, null],
|
||||
['e', null, null, null],
|
||||
['f', null, null, null],
|
||||
['g', [new Type(Type::BUILTIN_TYPE_ARRAY, true, null, true)], 'Nullable array.', null],
|
||||
['h', [new Type(Type::BUILTIN_TYPE_STRING, true)], null, null],
|
||||
['i', [new Type(Type::BUILTIN_TYPE_STRING, true), new Type(Type::BUILTIN_TYPE_INT, true)], null, null],
|
||||
['j', [new Type(Type::BUILTIN_TYPE_OBJECT, true, 'DateTimeImmutable')], null, null],
|
||||
['nullableCollectionOfNonNullableElements', [new Type(Type::BUILTIN_TYPE_ARRAY, true, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_INT, false))], null, null],
|
||||
['donotexist', null, null, null],
|
||||
['staticGetter', null, null, null],
|
||||
['staticSetter', null, null, null],
|
||||
];
|
||||
}
|
||||
|
||||
public function testReturnNullOnEmptyDocBlock()
|
||||
{
|
||||
$this->assertNull($this->extractor->getShortDescription(EmptyDocBlock::class, 'foo'));
|
||||
}
|
||||
|
||||
public static function dockBlockFallbackTypesProvider()
|
||||
{
|
||||
return [
|
||||
'pub' => [
|
||||
'pub', [new Type(Type::BUILTIN_TYPE_STRING)],
|
||||
],
|
||||
'protAcc' => [
|
||||
'protAcc', [new Type(Type::BUILTIN_TYPE_INT)],
|
||||
],
|
||||
'protMut' => [
|
||||
'protMut', [new Type(Type::BUILTIN_TYPE_BOOL)],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider dockBlockFallbackTypesProvider
|
||||
*/
|
||||
public function testDocBlockFallback($property, $types)
|
||||
{
|
||||
$this->assertEquals($types, $this->extractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\DockBlockFallback', $property));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider propertiesDefinedByTraitsProvider
|
||||
*/
|
||||
public function testPropertiesDefinedByTraits(string $property, Type $type)
|
||||
{
|
||||
$this->assertEquals([$type], $this->extractor->getTypes(DummyUsingTrait::class, $property));
|
||||
}
|
||||
|
||||
public static function propertiesDefinedByTraitsProvider(): array
|
||||
{
|
||||
return [
|
||||
['propertyInTraitPrimitiveType', new Type(Type::BUILTIN_TYPE_STRING)],
|
||||
['propertyInTraitObjectSameNamespace', new Type(Type::BUILTIN_TYPE_OBJECT, false, DummyUsedInTrait::class)],
|
||||
['propertyInTraitObjectDifferentNamespace', new Type(Type::BUILTIN_TYPE_OBJECT, false, Dummy::class)],
|
||||
['propertyInExternalTraitPrimitiveType', new Type(Type::BUILTIN_TYPE_STRING)],
|
||||
['propertyInExternalTraitObjectSameNamespace', new Type(Type::BUILTIN_TYPE_OBJECT, false, Dummy::class)],
|
||||
['propertyInExternalTraitObjectDifferentNamespace', new Type(Type::BUILTIN_TYPE_OBJECT, false, DummyUsedInTrait::class)],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider methodsDefinedByTraitsProvider
|
||||
*/
|
||||
public function testMethodsDefinedByTraits(string $property, Type $type)
|
||||
{
|
||||
$this->assertEquals([$type], $this->extractor->getTypes(DummyUsingTrait::class, $property));
|
||||
}
|
||||
|
||||
public static function methodsDefinedByTraitsProvider(): array
|
||||
{
|
||||
return [
|
||||
['methodInTraitPrimitiveType', new Type(Type::BUILTIN_TYPE_STRING)],
|
||||
['methodInTraitObjectSameNamespace', new Type(Type::BUILTIN_TYPE_OBJECT, false, DummyUsedInTrait::class)],
|
||||
['methodInTraitObjectDifferentNamespace', new Type(Type::BUILTIN_TYPE_OBJECT, false, Dummy::class)],
|
||||
['methodInExternalTraitPrimitiveType', new Type(Type::BUILTIN_TYPE_STRING)],
|
||||
['methodInExternalTraitObjectSameNamespace', new Type(Type::BUILTIN_TYPE_OBJECT, false, Dummy::class)],
|
||||
['methodInExternalTraitObjectDifferentNamespace', new Type(Type::BUILTIN_TYPE_OBJECT, false, DummyUsedInTrait::class)],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider propertiesStaticTypeProvider
|
||||
*/
|
||||
public function testPropertiesStaticType(string $class, string $property, Type $type)
|
||||
{
|
||||
$this->assertEquals([$type], $this->extractor->getTypes($class, $property));
|
||||
}
|
||||
|
||||
public static function propertiesStaticTypeProvider(): array
|
||||
{
|
||||
return [
|
||||
[ParentDummy::class, 'propertyTypeStatic', new Type(Type::BUILTIN_TYPE_OBJECT, false, ParentDummy::class)],
|
||||
[Dummy::class, 'propertyTypeStatic', new Type(Type::BUILTIN_TYPE_OBJECT, false, Dummy::class)],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider propertiesParentTypeProvider
|
||||
*/
|
||||
public function testPropertiesParentType(string $class, string $property, ?array $types)
|
||||
{
|
||||
$this->assertEquals($types, $this->extractor->getTypes($class, $property));
|
||||
}
|
||||
|
||||
public static function propertiesParentTypeProvider(): array
|
||||
{
|
||||
return [
|
||||
[ParentDummy::class, 'parentAnnotationNoParent', [new Type(Type::BUILTIN_TYPE_OBJECT, false, 'parent')]],
|
||||
[Dummy::class, 'parentAnnotation', [new Type(Type::BUILTIN_TYPE_OBJECT, false, ParentDummy::class)]],
|
||||
];
|
||||
}
|
||||
|
||||
public function testUnknownPseudoType()
|
||||
{
|
||||
$this->assertEquals([new Type(Type::BUILTIN_TYPE_OBJECT, false, 'scalar')], $this->extractor->getTypes(PseudoTypeDummy::class, 'unknownPseudoType'));
|
||||
}
|
||||
|
||||
public function testGenericInterface()
|
||||
{
|
||||
$this->assertNull($this->extractor->getTypes(Dummy::class, 'genericInterface'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider constructorTypesProvider
|
||||
*/
|
||||
public function testExtractConstructorTypes($property, ?array $type = null)
|
||||
{
|
||||
$this->assertEquals($type, $this->extractor->getTypesFromConstructor('Symfony\Component\PropertyInfo\Tests\Fixtures\ConstructorDummy', $property));
|
||||
}
|
||||
|
||||
public static function constructorTypesProvider()
|
||||
{
|
||||
return [
|
||||
['date', [new Type(Type::BUILTIN_TYPE_INT)]],
|
||||
['timezone', [new Type(Type::BUILTIN_TYPE_OBJECT, false, 'DateTimeZone')]],
|
||||
['dateObject', [new Type(Type::BUILTIN_TYPE_OBJECT, false, 'DateTimeInterface')]],
|
||||
['dateTime', null],
|
||||
['ddd', null],
|
||||
['mixed', null],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider pseudoTypesProvider
|
||||
*/
|
||||
public function testPseudoTypes($property, array $type)
|
||||
{
|
||||
$this->assertEquals($type, $this->extractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\PseudoTypesDummy', $property));
|
||||
}
|
||||
|
||||
public static function pseudoTypesProvider(): array
|
||||
{
|
||||
return [
|
||||
['classString', [new Type(Type::BUILTIN_TYPE_STRING, false, null)]],
|
||||
['classStringGeneric', [new Type(Type::BUILTIN_TYPE_STRING, false, null)]],
|
||||
['htmlEscapedString', [new Type(Type::BUILTIN_TYPE_STRING, false, null)]],
|
||||
['lowercaseString', [new Type(Type::BUILTIN_TYPE_STRING, false, null)]],
|
||||
['nonEmptyLowercaseString', [new Type(Type::BUILTIN_TYPE_STRING, false, null)]],
|
||||
['nonEmptyString', [new Type(Type::BUILTIN_TYPE_STRING, false, null)]],
|
||||
['numericString', [new Type(Type::BUILTIN_TYPE_STRING, false, null)]],
|
||||
['traitString', [new Type(Type::BUILTIN_TYPE_STRING, false, null)]],
|
||||
['positiveInt', [new Type(Type::BUILTIN_TYPE_INT, false, null)]],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider promotedPropertyProvider
|
||||
*/
|
||||
public function testExtractPromotedProperty(string $property, ?array $types)
|
||||
{
|
||||
$this->assertEquals($types, $this->extractor->getTypes(Php80Dummy::class, $property));
|
||||
}
|
||||
|
||||
public static function promotedPropertyProvider(): array
|
||||
{
|
||||
return [
|
||||
['promoted', null],
|
||||
['promotedAndMutated', [new Type(Type::BUILTIN_TYPE_STRING)]],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
class EmptyDocBlock
|
||||
{
|
||||
public $foo;
|
||||
}
|
||||
|
||||
class OmittedParamTagTypeDocBlock
|
||||
{
|
||||
/**
|
||||
* The type is omitted here to ensure that the extractor doesn't choke on missing types.
|
||||
*
|
||||
* @param $omittedTagType
|
||||
*/
|
||||
public function setOmittedType(array $omittedTagType)
|
||||
{
|
||||
}
|
||||
}
|
||||
583
vendor/symfony/property-info/Tests/Extractor/PhpStanExtractorTest.php
vendored
Normal file
583
vendor/symfony/property-info/Tests/Extractor/PhpStanExtractorTest.php
vendored
Normal file
@@ -0,0 +1,583 @@
|
||||
<?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\Tests\Extractor;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor;
|
||||
use Symfony\Component\PropertyInfo\Extractor\PhpStanExtractor;
|
||||
use Symfony\Component\PropertyInfo\Tests\Fixtures\Clazz;
|
||||
use Symfony\Component\PropertyInfo\Tests\Fixtures\ConstructorDummyWithoutDocBlock;
|
||||
use Symfony\Component\PropertyInfo\Tests\Fixtures\DefaultValue;
|
||||
use Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy;
|
||||
use Symfony\Component\PropertyInfo\Tests\Fixtures\DummyCollection;
|
||||
use Symfony\Component\PropertyInfo\Tests\Fixtures\DummyGeneric;
|
||||
use Symfony\Component\PropertyInfo\Tests\Fixtures\IFace;
|
||||
use Symfony\Component\PropertyInfo\Tests\Fixtures\ParentDummy;
|
||||
use Symfony\Component\PropertyInfo\Tests\Fixtures\Php80Dummy;
|
||||
use Symfony\Component\PropertyInfo\Tests\Fixtures\Php80PromotedDummy;
|
||||
use Symfony\Component\PropertyInfo\Tests\Fixtures\RootDummy\RootDummyItem;
|
||||
use Symfony\Component\PropertyInfo\Tests\Fixtures\TraitUsage\AnotherNamespace\DummyInAnotherNamespace;
|
||||
use Symfony\Component\PropertyInfo\Tests\Fixtures\TraitUsage\DummyUsedInTrait;
|
||||
use Symfony\Component\PropertyInfo\Tests\Fixtures\TraitUsage\DummyUsingTrait;
|
||||
use Symfony\Component\PropertyInfo\Type;
|
||||
|
||||
require_once __DIR__.'/../Fixtures/Extractor/DummyNamespace.php';
|
||||
|
||||
/**
|
||||
* @author Baptiste Leduc <baptiste.leduc@gmail.com>
|
||||
*/
|
||||
class PhpStanExtractorTest extends TestCase
|
||||
{
|
||||
private PhpStanExtractor $extractor;
|
||||
private PhpDocExtractor $phpDocExtractor;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->extractor = new PhpStanExtractor();
|
||||
$this->phpDocExtractor = new PhpDocExtractor();
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider typesProvider
|
||||
*/
|
||||
public function testExtract($property, ?array $type = null)
|
||||
{
|
||||
$this->assertEquals($type, $this->extractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', $property));
|
||||
}
|
||||
|
||||
public function testParamTagTypeIsOmitted()
|
||||
{
|
||||
$this->assertNull($this->extractor->getTypes(PhpStanOmittedParamTagTypeDocBlock::class, 'omittedType'));
|
||||
}
|
||||
|
||||
public static function invalidTypesProvider()
|
||||
{
|
||||
return [
|
||||
'pub' => ['pub'],
|
||||
'stat' => ['stat'],
|
||||
'foo' => ['foo'],
|
||||
'bar' => ['bar'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider invalidTypesProvider
|
||||
*/
|
||||
public function testInvalid($property)
|
||||
{
|
||||
$this->assertNull($this->extractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\InvalidDummy', $property));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider typesWithNoPrefixesProvider
|
||||
*/
|
||||
public function testExtractTypesWithNoPrefixes($property, ?array $type = null)
|
||||
{
|
||||
$noPrefixExtractor = new PhpStanExtractor([], [], []);
|
||||
|
||||
$this->assertEquals($type, $noPrefixExtractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', $property));
|
||||
}
|
||||
|
||||
public static function typesProvider()
|
||||
{
|
||||
return [
|
||||
['foo', null],
|
||||
['bar', [new Type(Type::BUILTIN_TYPE_STRING)]],
|
||||
['baz', [new Type(Type::BUILTIN_TYPE_INT)]],
|
||||
['foo2', [new Type(Type::BUILTIN_TYPE_FLOAT)]],
|
||||
['foo3', [new Type(Type::BUILTIN_TYPE_CALLABLE)]],
|
||||
['foo4', [new Type(Type::BUILTIN_TYPE_NULL)]],
|
||||
['foo5', null],
|
||||
[
|
||||
'files',
|
||||
[
|
||||
new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_OBJECT, false, 'SplFileInfo')),
|
||||
new Type(Type::BUILTIN_TYPE_RESOURCE),
|
||||
],
|
||||
],
|
||||
['bal', [new Type(Type::BUILTIN_TYPE_OBJECT, false, 'DateTimeImmutable')]],
|
||||
['parent', [new Type(Type::BUILTIN_TYPE_OBJECT, false, 'Symfony\Component\PropertyInfo\Tests\Fixtures\ParentDummy')]],
|
||||
['collection', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_OBJECT, false, 'DateTimeImmutable'))]],
|
||||
['nestedCollection', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_STRING, false)))]],
|
||||
['mixedCollection', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, [new Type(Type::BUILTIN_TYPE_INT)], null)]],
|
||||
['a', [new Type(Type::BUILTIN_TYPE_INT)]],
|
||||
['b', [new Type(Type::BUILTIN_TYPE_OBJECT, true, 'Symfony\Component\PropertyInfo\Tests\Fixtures\ParentDummy')]],
|
||||
['c', [new Type(Type::BUILTIN_TYPE_BOOL, true)]],
|
||||
['d', [new Type(Type::BUILTIN_TYPE_BOOL)]],
|
||||
['e', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_RESOURCE))]],
|
||||
['f', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_OBJECT, false, 'DateTimeImmutable'))]],
|
||||
['g', [new Type(Type::BUILTIN_TYPE_ARRAY, true, null, true)]],
|
||||
['h', [new Type(Type::BUILTIN_TYPE_STRING, true)]],
|
||||
['j', [new Type(Type::BUILTIN_TYPE_OBJECT, true, 'DateTimeImmutable')]],
|
||||
['nullableCollectionOfNonNullableElements', [new Type(Type::BUILTIN_TYPE_ARRAY, true, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_INT, false))]],
|
||||
['donotexist', null],
|
||||
['staticGetter', null],
|
||||
['staticSetter', null],
|
||||
['emptyVar', null],
|
||||
['arrayWithKeys', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_STRING), new Type(Type::BUILTIN_TYPE_STRING))]],
|
||||
['arrayOfMixed', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_STRING), null)]],
|
||||
['listOfStrings', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_STRING))]],
|
||||
['self', [new Type(Type::BUILTIN_TYPE_OBJECT, false, Dummy::class)]],
|
||||
['rootDummyItems', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_OBJECT, false, RootDummyItem::class))]],
|
||||
['rootDummyItem', [new Type(Type::BUILTIN_TYPE_OBJECT, false, RootDummyItem::class)]],
|
||||
['collectionAsObject', [new Type(Type::BUILTIN_TYPE_OBJECT, false, DummyCollection::class, true, [new Type(Type::BUILTIN_TYPE_INT)], [new Type(Type::BUILTIN_TYPE_STRING)])]],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideCollectionTypes
|
||||
*/
|
||||
public function testExtractCollection($property, ?array $type = null)
|
||||
{
|
||||
$this->testExtract($property, $type);
|
||||
}
|
||||
|
||||
public static function provideCollectionTypes()
|
||||
{
|
||||
return [
|
||||
['iteratorCollection', [new Type(Type::BUILTIN_TYPE_OBJECT, false, 'Iterator', true, null, new Type(Type::BUILTIN_TYPE_STRING))]],
|
||||
['iteratorCollectionWithKey', [new Type(Type::BUILTIN_TYPE_OBJECT, false, 'Iterator', true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_STRING))]],
|
||||
[
|
||||
'nestedIterators',
|
||||
[new Type(
|
||||
Type::BUILTIN_TYPE_OBJECT,
|
||||
false,
|
||||
'Iterator',
|
||||
true,
|
||||
new Type(Type::BUILTIN_TYPE_INT),
|
||||
new Type(Type::BUILTIN_TYPE_OBJECT, false, 'Iterator', true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_STRING))
|
||||
)],
|
||||
],
|
||||
[
|
||||
'arrayWithKeys',
|
||||
[new Type(
|
||||
Type::BUILTIN_TYPE_ARRAY,
|
||||
false,
|
||||
null,
|
||||
true,
|
||||
new Type(Type::BUILTIN_TYPE_STRING),
|
||||
new Type(Type::BUILTIN_TYPE_STRING)
|
||||
)],
|
||||
],
|
||||
[
|
||||
'arrayWithKeysAndComplexValue',
|
||||
[new Type(
|
||||
Type::BUILTIN_TYPE_ARRAY,
|
||||
false,
|
||||
null,
|
||||
true,
|
||||
new Type(Type::BUILTIN_TYPE_STRING),
|
||||
new Type(
|
||||
Type::BUILTIN_TYPE_ARRAY,
|
||||
true,
|
||||
null,
|
||||
true,
|
||||
new Type(Type::BUILTIN_TYPE_INT),
|
||||
new Type(Type::BUILTIN_TYPE_STRING, true)
|
||||
)
|
||||
)],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider typesWithCustomPrefixesProvider
|
||||
*/
|
||||
public function testExtractTypesWithCustomPrefixes($property, ?array $type = null)
|
||||
{
|
||||
$customExtractor = new PhpStanExtractor(['add', 'remove'], ['is', 'can']);
|
||||
|
||||
$this->assertEquals($type, $customExtractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', $property));
|
||||
}
|
||||
|
||||
public static function typesWithCustomPrefixesProvider()
|
||||
{
|
||||
return [
|
||||
['foo', null],
|
||||
['bar', [new Type(Type::BUILTIN_TYPE_STRING)]],
|
||||
['baz', [new Type(Type::BUILTIN_TYPE_INT)]],
|
||||
['foo2', [new Type(Type::BUILTIN_TYPE_FLOAT)]],
|
||||
['foo3', [new Type(Type::BUILTIN_TYPE_CALLABLE)]],
|
||||
['foo4', [new Type(Type::BUILTIN_TYPE_NULL)]],
|
||||
['foo5', null],
|
||||
[
|
||||
'files',
|
||||
[
|
||||
new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_OBJECT, false, 'SplFileInfo')),
|
||||
new Type(Type::BUILTIN_TYPE_RESOURCE),
|
||||
],
|
||||
],
|
||||
['bal', [new Type(Type::BUILTIN_TYPE_OBJECT, false, 'DateTimeImmutable')]],
|
||||
['parent', [new Type(Type::BUILTIN_TYPE_OBJECT, false, 'Symfony\Component\PropertyInfo\Tests\Fixtures\ParentDummy')]],
|
||||
['collection', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_OBJECT, false, 'DateTimeImmutable'))]],
|
||||
['nestedCollection', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_STRING, false)))]],
|
||||
['mixedCollection', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, [new Type(Type::BUILTIN_TYPE_INT)], null)]],
|
||||
['a', null],
|
||||
['b', null],
|
||||
['c', [new Type(Type::BUILTIN_TYPE_BOOL, true)]],
|
||||
['d', [new Type(Type::BUILTIN_TYPE_BOOL)]],
|
||||
['e', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_RESOURCE))]],
|
||||
['f', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_OBJECT, false, 'DateTimeImmutable'))]],
|
||||
['g', [new Type(Type::BUILTIN_TYPE_ARRAY, true, null, true)]],
|
||||
['h', [new Type(Type::BUILTIN_TYPE_STRING, true)]],
|
||||
['j', [new Type(Type::BUILTIN_TYPE_OBJECT, true, 'DateTimeImmutable')]],
|
||||
['nullableCollectionOfNonNullableElements', [new Type(Type::BUILTIN_TYPE_ARRAY, true, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_INT, false))]],
|
||||
['donotexist', null],
|
||||
['staticGetter', null],
|
||||
['staticSetter', null],
|
||||
];
|
||||
}
|
||||
|
||||
public static function typesWithNoPrefixesProvider()
|
||||
{
|
||||
return [
|
||||
['foo', null],
|
||||
['bar', [new Type(Type::BUILTIN_TYPE_STRING)]],
|
||||
['baz', [new Type(Type::BUILTIN_TYPE_INT)]],
|
||||
['foo2', [new Type(Type::BUILTIN_TYPE_FLOAT)]],
|
||||
['foo3', [new Type(Type::BUILTIN_TYPE_CALLABLE)]],
|
||||
['foo4', [new Type(Type::BUILTIN_TYPE_NULL)]],
|
||||
['foo5', null],
|
||||
[
|
||||
'files',
|
||||
[
|
||||
new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_OBJECT, false, 'SplFileInfo')),
|
||||
new Type(Type::BUILTIN_TYPE_RESOURCE),
|
||||
],
|
||||
],
|
||||
['bal', [new Type(Type::BUILTIN_TYPE_OBJECT, false, 'DateTimeImmutable')]],
|
||||
['parent', [new Type(Type::BUILTIN_TYPE_OBJECT, false, 'Symfony\Component\PropertyInfo\Tests\Fixtures\ParentDummy')]],
|
||||
['collection', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_OBJECT, false, 'DateTimeImmutable'))]],
|
||||
['nestedCollection', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_STRING, false)))]],
|
||||
['mixedCollection', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, [new Type(Type::BUILTIN_TYPE_INT)], null)]],
|
||||
['a', null],
|
||||
['b', null],
|
||||
['c', null],
|
||||
['d', null],
|
||||
['e', null],
|
||||
['f', null],
|
||||
['g', [new Type(Type::BUILTIN_TYPE_ARRAY, true, null, true)]],
|
||||
['h', [new Type(Type::BUILTIN_TYPE_STRING, true)]],
|
||||
['j', [new Type(Type::BUILTIN_TYPE_OBJECT, true, 'DateTimeImmutable')]],
|
||||
['nullableCollectionOfNonNullableElements', [new Type(Type::BUILTIN_TYPE_ARRAY, true, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_INT, false))]],
|
||||
['donotexist', null],
|
||||
['staticGetter', null],
|
||||
['staticSetter', null],
|
||||
];
|
||||
}
|
||||
|
||||
public static function dockBlockFallbackTypesProvider()
|
||||
{
|
||||
return [
|
||||
'pub' => [
|
||||
'pub', [new Type(Type::BUILTIN_TYPE_STRING)],
|
||||
],
|
||||
'protAcc' => [
|
||||
'protAcc', [new Type(Type::BUILTIN_TYPE_INT)],
|
||||
],
|
||||
'protMut' => [
|
||||
'protMut', [new Type(Type::BUILTIN_TYPE_BOOL)],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider dockBlockFallbackTypesProvider
|
||||
*/
|
||||
public function testDocBlockFallback($property, $types)
|
||||
{
|
||||
$this->assertEquals($types, $this->extractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\DockBlockFallback', $property));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider propertiesDefinedByTraitsProvider
|
||||
*/
|
||||
public function testPropertiesDefinedByTraits(string $property, Type $type)
|
||||
{
|
||||
$this->assertEquals([$type], $this->extractor->getTypes(DummyUsingTrait::class, $property));
|
||||
}
|
||||
|
||||
public static function propertiesDefinedByTraitsProvider(): array
|
||||
{
|
||||
return [
|
||||
['propertyInTraitPrimitiveType', new Type(Type::BUILTIN_TYPE_STRING)],
|
||||
['propertyInTraitObjectSameNamespace', new Type(Type::BUILTIN_TYPE_OBJECT, false, DummyUsedInTrait::class)],
|
||||
['propertyInTraitObjectDifferentNamespace', new Type(Type::BUILTIN_TYPE_OBJECT, false, Dummy::class)],
|
||||
['dummyInAnotherNamespace', new Type(Type::BUILTIN_TYPE_OBJECT, false, DummyInAnotherNamespace::class)],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider propertiesStaticTypeProvider
|
||||
*/
|
||||
public function testPropertiesStaticType(string $class, string $property, Type $type)
|
||||
{
|
||||
$this->assertEquals([$type], $this->extractor->getTypes($class, $property));
|
||||
}
|
||||
|
||||
public static function propertiesStaticTypeProvider(): array
|
||||
{
|
||||
return [
|
||||
[ParentDummy::class, 'propertyTypeStatic', new Type(Type::BUILTIN_TYPE_OBJECT, false, ParentDummy::class)],
|
||||
[Dummy::class, 'propertyTypeStatic', new Type(Type::BUILTIN_TYPE_OBJECT, false, Dummy::class)],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider propertiesParentTypeProvider
|
||||
*/
|
||||
public function testPropertiesParentType(string $class, string $property, ?array $types)
|
||||
{
|
||||
$this->assertEquals($types, $this->extractor->getTypes($class, $property));
|
||||
}
|
||||
|
||||
public static function propertiesParentTypeProvider(): array
|
||||
{
|
||||
return [
|
||||
[ParentDummy::class, 'parentAnnotationNoParent', [new Type(Type::BUILTIN_TYPE_OBJECT, false, 'parent')]],
|
||||
[Dummy::class, 'parentAnnotation', [new Type(Type::BUILTIN_TYPE_OBJECT, false, ParentDummy::class)]],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider constructorTypesProvider
|
||||
*/
|
||||
public function testExtractConstructorTypes($property, ?array $type = null)
|
||||
{
|
||||
$this->assertEquals($type, $this->extractor->getTypesFromConstructor('Symfony\Component\PropertyInfo\Tests\Fixtures\ConstructorDummy', $property));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider constructorTypesProvider
|
||||
*/
|
||||
public function testExtractConstructorTypesReturnNullOnEmptyDocBlock($property)
|
||||
{
|
||||
$this->assertNull($this->extractor->getTypesFromConstructor(ConstructorDummyWithoutDocBlock::class, $property));
|
||||
}
|
||||
|
||||
public static function constructorTypesProvider()
|
||||
{
|
||||
return [
|
||||
['date', [new Type(Type::BUILTIN_TYPE_INT)]],
|
||||
['timezone', [new Type(Type::BUILTIN_TYPE_OBJECT, false, 'DateTimeZone')]],
|
||||
['dateObject', [new Type(Type::BUILTIN_TYPE_OBJECT, false, 'DateTimeInterface')]],
|
||||
['dateTime', null],
|
||||
['ddd', null],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider unionTypesProvider
|
||||
*/
|
||||
public function testExtractorUnionTypes(string $property, ?array $types)
|
||||
{
|
||||
$this->assertEquals($types, $this->extractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\DummyUnionType', $property));
|
||||
}
|
||||
|
||||
public static function unionTypesProvider(): array
|
||||
{
|
||||
return [
|
||||
['a', [new Type(Type::BUILTIN_TYPE_STRING), new Type(Type::BUILTIN_TYPE_INT)]],
|
||||
['b', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, [new Type(Type::BUILTIN_TYPE_INT)], [new Type(Type::BUILTIN_TYPE_STRING), new Type(Type::BUILTIN_TYPE_INT)])]],
|
||||
['c', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, [], [new Type(Type::BUILTIN_TYPE_STRING), new Type(Type::BUILTIN_TYPE_INT)])]],
|
||||
['d', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, [new Type(Type::BUILTIN_TYPE_STRING), new Type(Type::BUILTIN_TYPE_INT)], [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, [], [new Type(Type::BUILTIN_TYPE_STRING)])])]],
|
||||
['e', [new Type(Type::BUILTIN_TYPE_OBJECT, true, Dummy::class, false, [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, [], [new Type(Type::BUILTIN_TYPE_STRING)])], [new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, [new Type(Type::BUILTIN_TYPE_INT)], [new Type(Type::BUILTIN_TYPE_OBJECT, false, \Traversable::class, true, [], [new Type(Type::BUILTIN_TYPE_OBJECT, false, DefaultValue::class)])])]), new Type(Type::BUILTIN_TYPE_OBJECT, false, ParentDummy::class)]],
|
||||
['f', null],
|
||||
['g', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, [], [new Type(Type::BUILTIN_TYPE_STRING), new Type(Type::BUILTIN_TYPE_INT)])]],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider pseudoTypesProvider
|
||||
*/
|
||||
public function testPseudoTypes($property, array $type)
|
||||
{
|
||||
$this->assertEquals($type, $this->extractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\PhpStanPseudoTypesDummy', $property));
|
||||
}
|
||||
|
||||
public static function pseudoTypesProvider(): array
|
||||
{
|
||||
return [
|
||||
['classString', [new Type(Type::BUILTIN_TYPE_STRING, false, null)]],
|
||||
['classStringGeneric', [new Type(Type::BUILTIN_TYPE_STRING, false, null)]],
|
||||
['htmlEscapedString', [new Type(Type::BUILTIN_TYPE_STRING, false, null)]],
|
||||
['lowercaseString', [new Type(Type::BUILTIN_TYPE_STRING, false, null)]],
|
||||
['nonEmptyLowercaseString', [new Type(Type::BUILTIN_TYPE_STRING, false, null)]],
|
||||
['nonEmptyString', [new Type(Type::BUILTIN_TYPE_STRING, false, null)]],
|
||||
['numericString', [new Type(Type::BUILTIN_TYPE_STRING, false, null)]],
|
||||
['traitString', [new Type(Type::BUILTIN_TYPE_STRING, false, null)]],
|
||||
['interfaceString', [new Type(Type::BUILTIN_TYPE_STRING, false, null)]],
|
||||
['literalString', [new Type(Type::BUILTIN_TYPE_STRING, false, null)]],
|
||||
['positiveInt', [new Type(Type::BUILTIN_TYPE_INT, false, null)]],
|
||||
['negativeInt', [new Type(Type::BUILTIN_TYPE_INT, false, null)]],
|
||||
['nonEmptyArray', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true)]],
|
||||
['nonEmptyList', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT))]],
|
||||
['scalar', [new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_FLOAT), new Type(Type::BUILTIN_TYPE_STRING), new Type(Type::BUILTIN_TYPE_BOOL)]],
|
||||
['number', [new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_FLOAT)]],
|
||||
['numeric', [new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_FLOAT), new Type(Type::BUILTIN_TYPE_STRING)]],
|
||||
['arrayKey', [new Type(Type::BUILTIN_TYPE_STRING), new Type(Type::BUILTIN_TYPE_INT)]],
|
||||
['double', [new Type(Type::BUILTIN_TYPE_FLOAT)]],
|
||||
];
|
||||
}
|
||||
|
||||
public function testDummyNamespace()
|
||||
{
|
||||
$this->assertEquals(
|
||||
[new Type(Type::BUILTIN_TYPE_OBJECT, false, 'Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy')],
|
||||
$this->extractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\DummyNamespace', 'dummy')
|
||||
);
|
||||
}
|
||||
|
||||
public function testDummyNamespaceWithProperty()
|
||||
{
|
||||
$phpStanTypes = $this->extractor->getTypes(\B\Dummy::class, 'property');
|
||||
$phpDocTypes = $this->phpDocExtractor->getTypes(\B\Dummy::class, 'property');
|
||||
|
||||
$this->assertEquals('A\Property', $phpStanTypes[0]->getClassName());
|
||||
$this->assertEquals($phpDocTypes[0]->getClassName(), $phpStanTypes[0]->getClassName());
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider intRangeTypeProvider
|
||||
*/
|
||||
public function testExtractorIntRangeType(string $property, ?array $types)
|
||||
{
|
||||
$this->assertEquals($types, $this->extractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\IntRangeDummy', $property));
|
||||
}
|
||||
|
||||
public static function intRangeTypeProvider(): array
|
||||
{
|
||||
return [
|
||||
['a', [new Type(Type::BUILTIN_TYPE_INT)]],
|
||||
['b', [new Type(Type::BUILTIN_TYPE_INT, true)]],
|
||||
['c', [new Type(Type::BUILTIN_TYPE_INT)]],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider php80TypesProvider
|
||||
*/
|
||||
public function testExtractPhp80Type(string $class, $property, ?array $type = null)
|
||||
{
|
||||
$this->assertEquals($type, $this->extractor->getTypes($class, $property, []));
|
||||
}
|
||||
|
||||
public static function php80TypesProvider()
|
||||
{
|
||||
return [
|
||||
[Php80Dummy::class, 'promotedWithDocCommentAndType', [new Type(Type::BUILTIN_TYPE_INT)]],
|
||||
[Php80Dummy::class, 'promotedWithDocComment', [new Type(Type::BUILTIN_TYPE_STRING)]],
|
||||
[Php80Dummy::class, 'promotedAndMutated', [new Type(Type::BUILTIN_TYPE_STRING)]],
|
||||
[Php80Dummy::class, 'promoted', null],
|
||||
[Php80Dummy::class, 'collection', [new Type(Type::BUILTIN_TYPE_ARRAY, collection: true, collectionValueType: new Type(Type::BUILTIN_TYPE_STRING))]],
|
||||
[Php80PromotedDummy::class, 'promoted', null],
|
||||
];
|
||||
}
|
||||
|
||||
public function testGenericInterface()
|
||||
{
|
||||
$this->assertEquals(
|
||||
[
|
||||
new Type(
|
||||
builtinType: Type::BUILTIN_TYPE_OBJECT,
|
||||
class: \BackedEnum::class,
|
||||
collectionValueType: new Type(
|
||||
builtinType: Type::BUILTIN_TYPE_STRING,
|
||||
)
|
||||
),
|
||||
],
|
||||
$this->extractor->getTypes(Dummy::class, 'genericInterface')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<Type> $expectedTypes
|
||||
* @dataProvider genericsProvider
|
||||
*/
|
||||
public function testGenericsLegacy(string $property, array $expectedTypes)
|
||||
{
|
||||
$this->assertEquals($expectedTypes, $this->extractor->getTypes(DummyGeneric::class, $property));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return iterable<array{0: string, 1: list<Type>}>
|
||||
*/
|
||||
public static function genericsProvider(): iterable
|
||||
{
|
||||
yield [
|
||||
'basicClass',
|
||||
[
|
||||
new Type(
|
||||
builtinType: Type::BUILTIN_TYPE_OBJECT,
|
||||
class: Clazz::class,
|
||||
collectionValueType: new Type(
|
||||
builtinType: Type::BUILTIN_TYPE_OBJECT,
|
||||
class: Dummy::class,
|
||||
)
|
||||
),
|
||||
],
|
||||
];
|
||||
yield [
|
||||
'nullableClass',
|
||||
[
|
||||
new Type(
|
||||
builtinType: Type::BUILTIN_TYPE_OBJECT,
|
||||
class: Clazz::class,
|
||||
nullable: true,
|
||||
collectionValueType: new Type(
|
||||
builtinType: Type::BUILTIN_TYPE_OBJECT,
|
||||
class: Dummy::class,
|
||||
)
|
||||
),
|
||||
],
|
||||
];
|
||||
yield [
|
||||
'basicInterface',
|
||||
[
|
||||
new Type(
|
||||
builtinType: Type::BUILTIN_TYPE_OBJECT,
|
||||
class: IFace::class,
|
||||
collectionValueType: new Type(
|
||||
builtinType: Type::BUILTIN_TYPE_OBJECT,
|
||||
class: Dummy::class,
|
||||
)
|
||||
),
|
||||
],
|
||||
];
|
||||
yield [
|
||||
'nullableInterface',
|
||||
[
|
||||
new Type(
|
||||
builtinType: Type::BUILTIN_TYPE_OBJECT,
|
||||
class: IFace::class,
|
||||
nullable: true,
|
||||
collectionValueType: new Type(
|
||||
builtinType: Type::BUILTIN_TYPE_OBJECT,
|
||||
class: Dummy::class,
|
||||
)
|
||||
),
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
class PhpStanOmittedParamTagTypeDocBlock
|
||||
{
|
||||
/**
|
||||
* The type is omitted here to ensure that the extractor doesn't choke on missing types.
|
||||
*
|
||||
* @param $omittedTagType
|
||||
*/
|
||||
public function setOmittedType(array $omittedTagType)
|
||||
{
|
||||
}
|
||||
}
|
||||
793
vendor/symfony/property-info/Tests/Extractor/ReflectionExtractorTest.php
vendored
Normal file
793
vendor/symfony/property-info/Tests/Extractor/ReflectionExtractorTest.php
vendored
Normal file
@@ -0,0 +1,793 @@
|
||||
<?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\Tests\Extractor;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor;
|
||||
use Symfony\Component\PropertyInfo\PropertyReadInfo;
|
||||
use Symfony\Component\PropertyInfo\PropertyWriteInfo;
|
||||
use Symfony\Component\PropertyInfo\Tests\Fixtures\AdderRemoverDummy;
|
||||
use Symfony\Component\PropertyInfo\Tests\Fixtures\AsymmetricVisibility;
|
||||
use Symfony\Component\PropertyInfo\Tests\Fixtures\DefaultValue;
|
||||
use Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy;
|
||||
use Symfony\Component\PropertyInfo\Tests\Fixtures\NotInstantiable;
|
||||
use Symfony\Component\PropertyInfo\Tests\Fixtures\Php71Dummy;
|
||||
use Symfony\Component\PropertyInfo\Tests\Fixtures\Php71DummyExtended;
|
||||
use Symfony\Component\PropertyInfo\Tests\Fixtures\Php71DummyExtended2;
|
||||
use Symfony\Component\PropertyInfo\Tests\Fixtures\Php74Dummy;
|
||||
use Symfony\Component\PropertyInfo\Tests\Fixtures\Php7Dummy;
|
||||
use Symfony\Component\PropertyInfo\Tests\Fixtures\Php7ParentDummy;
|
||||
use Symfony\Component\PropertyInfo\Tests\Fixtures\Php81Dummy;
|
||||
use Symfony\Component\PropertyInfo\Tests\Fixtures\SnakeCaseDummy;
|
||||
use Symfony\Component\PropertyInfo\Tests\Fixtures\VirtualProperties;
|
||||
use Symfony\Component\PropertyInfo\Type;
|
||||
|
||||
/**
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
class ReflectionExtractorTest extends TestCase
|
||||
{
|
||||
private ReflectionExtractor $extractor;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->extractor = new ReflectionExtractor();
|
||||
}
|
||||
|
||||
public function testGetProperties()
|
||||
{
|
||||
$this->assertSame(
|
||||
[
|
||||
'bal',
|
||||
'parent',
|
||||
'collection',
|
||||
'collectionAsObject',
|
||||
'nestedCollection',
|
||||
'mixedCollection',
|
||||
'B',
|
||||
'Guid',
|
||||
'g',
|
||||
'h',
|
||||
'i',
|
||||
'j',
|
||||
'nullableCollectionOfNonNullableElements',
|
||||
'nonNullableCollectionOfNullableElements',
|
||||
'nullableCollectionOfMultipleNonNullableElementTypes',
|
||||
'emptyVar',
|
||||
'iteratorCollection',
|
||||
'iteratorCollectionWithKey',
|
||||
'nestedIterators',
|
||||
'arrayWithKeys',
|
||||
'arrayWithKeysAndComplexValue',
|
||||
'arrayOfMixed',
|
||||
'listOfStrings',
|
||||
'parentAnnotation',
|
||||
'genericInterface',
|
||||
'foo',
|
||||
'foo2',
|
||||
'foo3',
|
||||
'foo4',
|
||||
'foo5',
|
||||
'files',
|
||||
'propertyTypeStatic',
|
||||
'parentAnnotationNoParent',
|
||||
'rootDummyItems',
|
||||
'rootDummyItem',
|
||||
'a',
|
||||
'DOB',
|
||||
'Id',
|
||||
'123',
|
||||
'self',
|
||||
'realParent',
|
||||
'xTotals',
|
||||
'YT',
|
||||
'date',
|
||||
'element',
|
||||
'c',
|
||||
'ct',
|
||||
'cf',
|
||||
'd',
|
||||
'dt',
|
||||
'df',
|
||||
'e',
|
||||
'f',
|
||||
],
|
||||
$this->extractor->getProperties('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy')
|
||||
);
|
||||
|
||||
$this->assertNull($this->extractor->getProperties('Symfony\Component\PropertyInfo\Tests\Fixtures\NoProperties'));
|
||||
}
|
||||
|
||||
public function testGetPropertiesWithCustomPrefixes()
|
||||
{
|
||||
$customExtractor = new ReflectionExtractor(['add', 'remove'], ['is', 'can']);
|
||||
|
||||
$this->assertSame(
|
||||
[
|
||||
'bal',
|
||||
'parent',
|
||||
'collection',
|
||||
'collectionAsObject',
|
||||
'nestedCollection',
|
||||
'mixedCollection',
|
||||
'B',
|
||||
'Guid',
|
||||
'g',
|
||||
'h',
|
||||
'i',
|
||||
'j',
|
||||
'nullableCollectionOfNonNullableElements',
|
||||
'nonNullableCollectionOfNullableElements',
|
||||
'nullableCollectionOfMultipleNonNullableElementTypes',
|
||||
'emptyVar',
|
||||
'iteratorCollection',
|
||||
'iteratorCollectionWithKey',
|
||||
'nestedIterators',
|
||||
'arrayWithKeys',
|
||||
'arrayWithKeysAndComplexValue',
|
||||
'arrayOfMixed',
|
||||
'listOfStrings',
|
||||
'parentAnnotation',
|
||||
'genericInterface',
|
||||
'foo',
|
||||
'foo2',
|
||||
'foo3',
|
||||
'foo4',
|
||||
'foo5',
|
||||
'files',
|
||||
'propertyTypeStatic',
|
||||
'parentAnnotationNoParent',
|
||||
'rootDummyItems',
|
||||
'rootDummyItem',
|
||||
'date',
|
||||
'c',
|
||||
'ct',
|
||||
'cf',
|
||||
'd',
|
||||
'dt',
|
||||
'df',
|
||||
'e',
|
||||
'f',
|
||||
],
|
||||
$customExtractor->getProperties('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy')
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetPropertiesWithNoPrefixes()
|
||||
{
|
||||
$noPrefixExtractor = new ReflectionExtractor([], [], []);
|
||||
|
||||
$this->assertSame(
|
||||
[
|
||||
'bal',
|
||||
'parent',
|
||||
'collection',
|
||||
'collectionAsObject',
|
||||
'nestedCollection',
|
||||
'mixedCollection',
|
||||
'B',
|
||||
'Guid',
|
||||
'g',
|
||||
'h',
|
||||
'i',
|
||||
'j',
|
||||
'nullableCollectionOfNonNullableElements',
|
||||
'nonNullableCollectionOfNullableElements',
|
||||
'nullableCollectionOfMultipleNonNullableElementTypes',
|
||||
'emptyVar',
|
||||
'iteratorCollection',
|
||||
'iteratorCollectionWithKey',
|
||||
'nestedIterators',
|
||||
'arrayWithKeys',
|
||||
'arrayWithKeysAndComplexValue',
|
||||
'arrayOfMixed',
|
||||
'listOfStrings',
|
||||
'parentAnnotation',
|
||||
'genericInterface',
|
||||
'foo',
|
||||
'foo2',
|
||||
'foo3',
|
||||
'foo4',
|
||||
'foo5',
|
||||
'files',
|
||||
'propertyTypeStatic',
|
||||
'parentAnnotationNoParent',
|
||||
'rootDummyItems',
|
||||
'rootDummyItem',
|
||||
],
|
||||
$noPrefixExtractor->getProperties('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider typesProvider
|
||||
*/
|
||||
public function testExtractors($property, ?array $type = null)
|
||||
{
|
||||
$this->assertEquals($type, $this->extractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', $property, []));
|
||||
}
|
||||
|
||||
public static function typesProvider()
|
||||
{
|
||||
return [
|
||||
['a', null],
|
||||
['b', [new Type(Type::BUILTIN_TYPE_OBJECT, true, 'Symfony\Component\PropertyInfo\Tests\Fixtures\ParentDummy')]],
|
||||
['c', [new Type(Type::BUILTIN_TYPE_BOOL)]],
|
||||
['d', [new Type(Type::BUILTIN_TYPE_BOOL)]],
|
||||
['e', null],
|
||||
['f', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_OBJECT, false, 'DateTimeImmutable'))]],
|
||||
['donotexist', null],
|
||||
['staticGetter', null],
|
||||
['staticSetter', null],
|
||||
['self', [new Type(Type::BUILTIN_TYPE_OBJECT, false, 'Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy')]],
|
||||
['realParent', [new Type(Type::BUILTIN_TYPE_OBJECT, false, 'Symfony\Component\PropertyInfo\Tests\Fixtures\ParentDummy')]],
|
||||
['date', [new Type(Type::BUILTIN_TYPE_OBJECT, false, \DateTimeImmutable::class)]],
|
||||
['dates', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_OBJECT, false, \DateTimeImmutable::class))]],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider php7TypesProvider
|
||||
*/
|
||||
public function testExtractPhp7Type(string $class, string $property, ?array $type = null)
|
||||
{
|
||||
$this->assertEquals($type, $this->extractor->getTypes($class, $property, []));
|
||||
}
|
||||
|
||||
public static function php7TypesProvider()
|
||||
{
|
||||
return [
|
||||
[Php7Dummy::class, 'foo', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true)]],
|
||||
[Php7Dummy::class, 'bar', [new Type(Type::BUILTIN_TYPE_INT)]],
|
||||
[Php7Dummy::class, 'baz', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_STRING))]],
|
||||
[Php7Dummy::class, 'buz', [new Type(Type::BUILTIN_TYPE_OBJECT, false, 'Symfony\Component\PropertyInfo\Tests\Fixtures\Php7Dummy')]],
|
||||
[Php7Dummy::class, 'biz', [new Type(Type::BUILTIN_TYPE_OBJECT, false, Php7ParentDummy::class)]],
|
||||
[Php7Dummy::class, 'donotexist', null],
|
||||
[Php7ParentDummy::class, 'parent', [new Type(Type::BUILTIN_TYPE_OBJECT, false, \stdClass::class)]],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider php71TypesProvider
|
||||
*/
|
||||
public function testExtractPhp71Type($property, ?array $type = null)
|
||||
{
|
||||
$this->assertEquals($type, $this->extractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\Php71Dummy', $property, []));
|
||||
}
|
||||
|
||||
public static function php71TypesProvider()
|
||||
{
|
||||
return [
|
||||
['foo', [new Type(Type::BUILTIN_TYPE_ARRAY, true, null, true)]],
|
||||
['buz', [new Type(Type::BUILTIN_TYPE_NULL)]],
|
||||
['bar', [new Type(Type::BUILTIN_TYPE_INT, true)]],
|
||||
['baz', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_STRING))]],
|
||||
['donotexist', null],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider php80TypesProvider
|
||||
*/
|
||||
public function testExtractPhp80Type($property, ?array $type = null)
|
||||
{
|
||||
$this->assertEquals($type, $this->extractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\Php80Dummy', $property, []));
|
||||
}
|
||||
|
||||
public static function php80TypesProvider()
|
||||
{
|
||||
return [
|
||||
['foo', [new Type(Type::BUILTIN_TYPE_ARRAY, true, null, true)]],
|
||||
['bar', [new Type(Type::BUILTIN_TYPE_INT, true)]],
|
||||
['timeout', [new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_FLOAT)]],
|
||||
['optional', [new Type(Type::BUILTIN_TYPE_INT, true), new Type(Type::BUILTIN_TYPE_FLOAT, true)]],
|
||||
['string', [new Type(Type::BUILTIN_TYPE_OBJECT, false, 'Stringable'), new Type(Type::BUILTIN_TYPE_STRING)]],
|
||||
['payload', null],
|
||||
['data', null],
|
||||
['mixedProperty', null],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider php81TypesProvider
|
||||
*/
|
||||
public function testExtractPhp81Type($property, ?array $type = null)
|
||||
{
|
||||
$this->assertEquals($type, $this->extractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\Php81Dummy', $property, []));
|
||||
}
|
||||
|
||||
public static function php81TypesProvider()
|
||||
{
|
||||
return [
|
||||
['nothing', null],
|
||||
['collection', [new Type(Type::BUILTIN_TYPE_OBJECT, false, 'Traversable'), new Type(Type::BUILTIN_TYPE_OBJECT, false, 'Countable')]],
|
||||
];
|
||||
}
|
||||
|
||||
public function testReadonlyPropertiesAreNotWriteable()
|
||||
{
|
||||
$this->assertFalse($this->extractor->isWritable(Php81Dummy::class, 'foo'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider php82TypesProvider
|
||||
*
|
||||
* @requires PHP 8.2
|
||||
*/
|
||||
public function testExtractPhp82Type($property, ?array $type = null)
|
||||
{
|
||||
$this->assertEquals($type, $this->extractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\Php82Dummy', $property, []));
|
||||
}
|
||||
|
||||
public static function php82TypesProvider(): iterable
|
||||
{
|
||||
yield ['nil', null];
|
||||
yield ['false', [new Type(Type::BUILTIN_TYPE_FALSE)]];
|
||||
yield ['true', [new Type(Type::BUILTIN_TYPE_TRUE)]];
|
||||
|
||||
// Nesting intersection and union types is not supported yet,
|
||||
// but we should make sure this kind of composite types does not crash the extractor.
|
||||
yield ['someCollection', null];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider defaultValueProvider
|
||||
*/
|
||||
public function testExtractWithDefaultValue($property, $type)
|
||||
{
|
||||
$this->assertEquals($type, $this->extractor->getTypes(DefaultValue::class, $property, []));
|
||||
}
|
||||
|
||||
public static function defaultValueProvider()
|
||||
{
|
||||
return [
|
||||
['defaultInt', [new Type(Type::BUILTIN_TYPE_INT, false)]],
|
||||
['defaultFloat', [new Type(Type::BUILTIN_TYPE_FLOAT, false)]],
|
||||
['defaultString', [new Type(Type::BUILTIN_TYPE_STRING, false)]],
|
||||
['defaultArray', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true)]],
|
||||
['defaultNull', null],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getReadableProperties
|
||||
*/
|
||||
public function testIsReadable($property, $expected)
|
||||
{
|
||||
$this->assertSame(
|
||||
$expected,
|
||||
$this->extractor->isReadable('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', $property, [])
|
||||
);
|
||||
}
|
||||
|
||||
public static function getReadableProperties()
|
||||
{
|
||||
return [
|
||||
['bar', false],
|
||||
['baz', false],
|
||||
['parent', true],
|
||||
['a', true],
|
||||
['b', false],
|
||||
['c', true],
|
||||
['d', true],
|
||||
['e', false],
|
||||
['f', false],
|
||||
['Id', true],
|
||||
['id', true],
|
||||
['Guid', true],
|
||||
['guid', false],
|
||||
['element', false],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getWritableProperties
|
||||
*/
|
||||
public function testIsWritable($property, $expected)
|
||||
{
|
||||
$this->assertSame(
|
||||
$expected,
|
||||
$this->extractor->isWritable(Dummy::class, $property, [])
|
||||
);
|
||||
}
|
||||
|
||||
public static function getWritableProperties()
|
||||
{
|
||||
return [
|
||||
['bar', false],
|
||||
['baz', false],
|
||||
['parent', true],
|
||||
['a', false],
|
||||
['b', true],
|
||||
['c', false],
|
||||
['d', false],
|
||||
['e', true],
|
||||
['f', true],
|
||||
['Id', false],
|
||||
['Guid', true],
|
||||
['guid', false],
|
||||
];
|
||||
}
|
||||
|
||||
public function testIsReadableSnakeCase()
|
||||
{
|
||||
$this->assertTrue($this->extractor->isReadable(SnakeCaseDummy::class, 'snake_property'));
|
||||
$this->assertTrue($this->extractor->isReadable(SnakeCaseDummy::class, 'snake_readonly'));
|
||||
}
|
||||
|
||||
public function testIsWriteableSnakeCase()
|
||||
{
|
||||
$this->assertTrue($this->extractor->isWritable(SnakeCaseDummy::class, 'snake_property'));
|
||||
$this->assertFalse($this->extractor->isWritable(SnakeCaseDummy::class, 'snake_readonly'));
|
||||
// Ensure that it's still possible to write to the property using the (old) snake name
|
||||
$this->assertTrue($this->extractor->isWritable(SnakeCaseDummy::class, 'snake_method'));
|
||||
}
|
||||
|
||||
public function testSingularize()
|
||||
{
|
||||
$this->assertTrue($this->extractor->isWritable(AdderRemoverDummy::class, 'analyses'));
|
||||
$this->assertTrue($this->extractor->isWritable(AdderRemoverDummy::class, 'feet'));
|
||||
$this->assertEquals(['analyses', 'feet'], $this->extractor->getProperties(AdderRemoverDummy::class));
|
||||
}
|
||||
|
||||
public function testPrivatePropertyExtractor()
|
||||
{
|
||||
$privateExtractor = new ReflectionExtractor(null, null, null, true, ReflectionExtractor::ALLOW_PUBLIC | ReflectionExtractor::ALLOW_PRIVATE | ReflectionExtractor::ALLOW_PROTECTED);
|
||||
$properties = $privateExtractor->getProperties(Dummy::class);
|
||||
|
||||
$this->assertContains('bar', $properties);
|
||||
$this->assertContains('baz', $properties);
|
||||
|
||||
$this->assertTrue($privateExtractor->isReadable(Dummy::class, 'bar'));
|
||||
$this->assertTrue($privateExtractor->isReadable(Dummy::class, 'baz'));
|
||||
|
||||
$protectedExtractor = new ReflectionExtractor(null, null, null, true, ReflectionExtractor::ALLOW_PUBLIC | ReflectionExtractor::ALLOW_PROTECTED);
|
||||
$properties = $protectedExtractor->getProperties(Dummy::class);
|
||||
|
||||
$this->assertNotContains('bar', $properties);
|
||||
$this->assertContains('baz', $properties);
|
||||
|
||||
$this->assertFalse($protectedExtractor->isReadable(Dummy::class, 'bar'));
|
||||
$this->assertTrue($protectedExtractor->isReadable(Dummy::class, 'baz'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getInitializableProperties
|
||||
*/
|
||||
public function testIsInitializable(string $class, string $property, bool $expected)
|
||||
{
|
||||
$this->assertSame($expected, $this->extractor->isInitializable($class, $property));
|
||||
}
|
||||
|
||||
public static function getInitializableProperties(): array
|
||||
{
|
||||
return [
|
||||
[Php71Dummy::class, 'string', true],
|
||||
[Php71Dummy::class, 'intPrivate', true],
|
||||
[Php71Dummy::class, 'notExist', false],
|
||||
[Php71DummyExtended2::class, 'intWithAccessor', true],
|
||||
[Php71DummyExtended2::class, 'intPrivate', false],
|
||||
[NotInstantiable::class, 'foo', false],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider constructorTypesProvider
|
||||
*/
|
||||
public function testExtractTypeConstructor(string $class, string $property, ?array $type = null)
|
||||
{
|
||||
/* Check that constructor extractions works by default, and if passed in via context.
|
||||
Check that null is returned if constructor extraction is disabled */
|
||||
$this->assertEquals($type, $this->extractor->getTypes($class, $property, []));
|
||||
$this->assertEquals($type, $this->extractor->getTypes($class, $property, ['enable_constructor_extraction' => true]));
|
||||
$this->assertNull($this->extractor->getTypes($class, $property, ['enable_constructor_extraction' => false]));
|
||||
}
|
||||
|
||||
public static function constructorTypesProvider(): array
|
||||
{
|
||||
return [
|
||||
// php71 dummy has following constructor: __construct(string $string, int $intPrivate)
|
||||
[Php71Dummy::class, 'string', [new Type(Type::BUILTIN_TYPE_STRING, false)]],
|
||||
[Php71Dummy::class, 'intPrivate', [new Type(Type::BUILTIN_TYPE_INT, false)]],
|
||||
// Php71DummyExtended2 adds int $intWithAccessor
|
||||
[Php71DummyExtended2::class, 'intWithAccessor', [new Type(Type::BUILTIN_TYPE_INT, false)]],
|
||||
[Php71DummyExtended2::class, 'intPrivate', [new Type(Type::BUILTIN_TYPE_INT, false)]],
|
||||
[DefaultValue::class, 'foo', null],
|
||||
];
|
||||
}
|
||||
|
||||
public function testNullOnPrivateProtectedAccessor()
|
||||
{
|
||||
$barAcessor = $this->extractor->getReadInfo(Dummy::class, 'bar');
|
||||
$barMutator = $this->extractor->getWriteInfo(Dummy::class, 'bar');
|
||||
$bazAcessor = $this->extractor->getReadInfo(Dummy::class, 'baz');
|
||||
$bazMutator = $this->extractor->getWriteInfo(Dummy::class, 'baz');
|
||||
|
||||
$this->assertNull($barAcessor);
|
||||
$this->assertEquals(PropertyWriteInfo::TYPE_NONE, $barMutator->getType());
|
||||
$this->assertNull($bazAcessor);
|
||||
$this->assertEquals(PropertyWriteInfo::TYPE_NONE, $bazMutator->getType());
|
||||
}
|
||||
|
||||
public function testTypedProperties()
|
||||
{
|
||||
$this->assertEquals([new Type(Type::BUILTIN_TYPE_OBJECT, false, Dummy::class)], $this->extractor->getTypes(Php74Dummy::class, 'dummy'));
|
||||
$this->assertEquals([new Type(Type::BUILTIN_TYPE_BOOL, true)], $this->extractor->getTypes(Php74Dummy::class, 'nullableBoolProp'));
|
||||
$this->assertEquals([new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_STRING))], $this->extractor->getTypes(Php74Dummy::class, 'stringCollection'));
|
||||
$this->assertEquals([new Type(Type::BUILTIN_TYPE_INT, true)], $this->extractor->getTypes(Php74Dummy::class, 'nullableWithDefault'));
|
||||
$this->assertEquals([new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true)], $this->extractor->getTypes(Php74Dummy::class, 'collection'));
|
||||
$this->assertEquals([new Type(Type::BUILTIN_TYPE_ARRAY, true, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_OBJECT, false, Dummy::class))], $this->extractor->getTypes(Php74Dummy::class, 'nullableTypedCollection'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider readAccessorProvider
|
||||
*/
|
||||
public function testGetReadAccessor($class, $property, $found, $type, $name, $visibility, $static)
|
||||
{
|
||||
$extractor = new ReflectionExtractor(null, null, null, true, ReflectionExtractor::ALLOW_PUBLIC | ReflectionExtractor::ALLOW_PROTECTED | ReflectionExtractor::ALLOW_PRIVATE);
|
||||
$readAcessor = $extractor->getReadInfo($class, $property);
|
||||
|
||||
if (!$found) {
|
||||
$this->assertNull($readAcessor);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->assertNotNull($readAcessor);
|
||||
$this->assertSame($type, $readAcessor->getType());
|
||||
$this->assertSame($name, $readAcessor->getName());
|
||||
$this->assertSame($visibility, $readAcessor->getVisibility());
|
||||
$this->assertSame($static, $readAcessor->isStatic());
|
||||
}
|
||||
|
||||
public static function readAccessorProvider(): array
|
||||
{
|
||||
return [
|
||||
[Dummy::class, 'bar', true, PropertyReadInfo::TYPE_PROPERTY, 'bar', PropertyReadInfo::VISIBILITY_PRIVATE, false],
|
||||
[Dummy::class, 'baz', true, PropertyReadInfo::TYPE_PROPERTY, 'baz', PropertyReadInfo::VISIBILITY_PROTECTED, false],
|
||||
[Dummy::class, 'bal', true, PropertyReadInfo::TYPE_PROPERTY, 'bal', PropertyReadInfo::VISIBILITY_PUBLIC, false],
|
||||
[Dummy::class, 'parent', true, PropertyReadInfo::TYPE_PROPERTY, 'parent', PropertyReadInfo::VISIBILITY_PUBLIC, false],
|
||||
[Dummy::class, 'static', true, PropertyReadInfo::TYPE_METHOD, 'getStatic', PropertyReadInfo::VISIBILITY_PUBLIC, true],
|
||||
[Dummy::class, 'foo', true, PropertyReadInfo::TYPE_PROPERTY, 'foo', PropertyReadInfo::VISIBILITY_PUBLIC, false],
|
||||
[Php71Dummy::class, 'foo', true, PropertyReadInfo::TYPE_METHOD, 'getFoo', PropertyReadInfo::VISIBILITY_PUBLIC, false],
|
||||
[Php71Dummy::class, 'buz', true, PropertyReadInfo::TYPE_METHOD, 'getBuz', PropertyReadInfo::VISIBILITY_PUBLIC, false],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider writeMutatorProvider
|
||||
*/
|
||||
public function testGetWriteMutator($class, $property, $allowConstruct, $found, $type, $name, $addName, $removeName, $visibility, $static)
|
||||
{
|
||||
$extractor = new ReflectionExtractor(null, null, null, true, ReflectionExtractor::ALLOW_PUBLIC | ReflectionExtractor::ALLOW_PROTECTED | ReflectionExtractor::ALLOW_PRIVATE);
|
||||
$writeMutator = $extractor->getWriteInfo($class, $property, [
|
||||
'enable_constructor_extraction' => $allowConstruct,
|
||||
'enable_getter_setter_extraction' => true,
|
||||
]);
|
||||
|
||||
if (!$found) {
|
||||
$this->assertEquals(PropertyWriteInfo::TYPE_NONE, $writeMutator->getType());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->assertNotNull($writeMutator);
|
||||
$this->assertSame($type, $writeMutator->getType());
|
||||
|
||||
if (PropertyWriteInfo::TYPE_ADDER_AND_REMOVER === $writeMutator->getType()) {
|
||||
$this->assertNotNull($writeMutator->getAdderInfo());
|
||||
$this->assertSame($addName, $writeMutator->getAdderInfo()->getName());
|
||||
$this->assertNotNull($writeMutator->getRemoverInfo());
|
||||
$this->assertSame($removeName, $writeMutator->getRemoverInfo()->getName());
|
||||
}
|
||||
|
||||
if (PropertyWriteInfo::TYPE_CONSTRUCTOR === $writeMutator->getType()) {
|
||||
$this->assertSame($name, $writeMutator->getName());
|
||||
}
|
||||
|
||||
if (PropertyWriteInfo::TYPE_PROPERTY === $writeMutator->getType()) {
|
||||
$this->assertSame($name, $writeMutator->getName());
|
||||
$this->assertSame($visibility, $writeMutator->getVisibility());
|
||||
$this->assertSame($static, $writeMutator->isStatic());
|
||||
}
|
||||
|
||||
if (PropertyWriteInfo::TYPE_METHOD === $writeMutator->getType()) {
|
||||
$this->assertSame($name, $writeMutator->getName());
|
||||
$this->assertSame($visibility, $writeMutator->getVisibility());
|
||||
$this->assertSame($static, $writeMutator->isStatic());
|
||||
}
|
||||
}
|
||||
|
||||
public static function writeMutatorProvider(): array
|
||||
{
|
||||
return [
|
||||
[Dummy::class, 'bar', false, true, PropertyWriteInfo::TYPE_PROPERTY, 'bar', null, null, PropertyWriteInfo::VISIBILITY_PRIVATE, false],
|
||||
[Dummy::class, 'baz', false, true, PropertyWriteInfo::TYPE_PROPERTY, 'baz', null, null, PropertyWriteInfo::VISIBILITY_PROTECTED, false],
|
||||
[Dummy::class, 'bal', false, true, PropertyWriteInfo::TYPE_PROPERTY, 'bal', null, null, PropertyWriteInfo::VISIBILITY_PUBLIC, false],
|
||||
[Dummy::class, 'parent', false, true, PropertyWriteInfo::TYPE_PROPERTY, 'parent', null, null, PropertyWriteInfo::VISIBILITY_PUBLIC, false],
|
||||
[Dummy::class, 'staticSetter', false, true, PropertyWriteInfo::TYPE_METHOD, 'staticSetter', null, null, PropertyWriteInfo::VISIBILITY_PUBLIC, true],
|
||||
[Dummy::class, 'foo', false, true, PropertyWriteInfo::TYPE_PROPERTY, 'foo', null, null, PropertyWriteInfo::VISIBILITY_PUBLIC, false],
|
||||
[Php71Dummy::class, 'bar', false, true, PropertyWriteInfo::TYPE_METHOD, 'setBar', null, null, PropertyWriteInfo::VISIBILITY_PUBLIC, false],
|
||||
[Php71Dummy::class, 'string', false, false, '', '', null, null, PropertyWriteInfo::VISIBILITY_PUBLIC, false],
|
||||
[Php71Dummy::class, 'string', true, true, PropertyWriteInfo::TYPE_CONSTRUCTOR, 'string', null, null, PropertyWriteInfo::VISIBILITY_PUBLIC, false],
|
||||
[Php71Dummy::class, 'baz', false, true, PropertyWriteInfo::TYPE_ADDER_AND_REMOVER, null, 'addBaz', 'removeBaz', PropertyWriteInfo::VISIBILITY_PUBLIC, false],
|
||||
[Php71DummyExtended::class, 'bar', false, true, PropertyWriteInfo::TYPE_METHOD, 'setBar', null, null, PropertyWriteInfo::VISIBILITY_PUBLIC, false],
|
||||
[Php71DummyExtended::class, 'string', false, false, -1, '', null, null, PropertyWriteInfo::VISIBILITY_PUBLIC, false],
|
||||
[Php71DummyExtended::class, 'string', true, true, PropertyWriteInfo::TYPE_CONSTRUCTOR, 'string', null, null, PropertyWriteInfo::VISIBILITY_PUBLIC, false],
|
||||
[Php71DummyExtended::class, 'baz', false, true, PropertyWriteInfo::TYPE_ADDER_AND_REMOVER, null, 'addBaz', 'removeBaz', PropertyWriteInfo::VISIBILITY_PUBLIC, false],
|
||||
[Php71DummyExtended2::class, 'bar', false, true, PropertyWriteInfo::TYPE_METHOD, 'setBar', null, null, PropertyWriteInfo::VISIBILITY_PUBLIC, false],
|
||||
[Php71DummyExtended2::class, 'string', false, false, '', '', null, null, PropertyWriteInfo::VISIBILITY_PUBLIC, false],
|
||||
[Php71DummyExtended2::class, 'string', true, false, '', '', null, null, PropertyWriteInfo::VISIBILITY_PUBLIC, false],
|
||||
[Php71DummyExtended2::class, 'baz', false, true, PropertyWriteInfo::TYPE_ADDER_AND_REMOVER, null, 'addBaz', 'removeBaz', PropertyWriteInfo::VISIBILITY_PUBLIC, false],
|
||||
];
|
||||
}
|
||||
|
||||
public function testDisabledAdderAndRemoverReturnsError()
|
||||
{
|
||||
$writeMutator = $this->extractor->getWriteInfo(Php71Dummy::class, 'baz', [
|
||||
'enable_adder_remover_extraction' => false,
|
||||
]);
|
||||
|
||||
self::assertNotNull($writeMutator);
|
||||
self::assertSame(PropertyWriteInfo::TYPE_NONE, $writeMutator->getType());
|
||||
self::assertSame([\sprintf('The property "baz" in class "%s" can be defined with the methods "addBaz()", "removeBaz()" but the new value must be an array or an instance of \Traversable', Php71Dummy::class)], $writeMutator->getErrors());
|
||||
}
|
||||
|
||||
public function testGetWriteInfoReadonlyProperties()
|
||||
{
|
||||
$writeMutatorConstructor = $this->extractor->getWriteInfo(Php81Dummy::class, 'foo', ['enable_constructor_extraction' => true]);
|
||||
$writeMutatorWithoutConstructor = $this->extractor->getWriteInfo(Php81Dummy::class, 'foo', ['enable_constructor_extraction' => false]);
|
||||
|
||||
$this->assertSame(PropertyWriteInfo::TYPE_CONSTRUCTOR, $writeMutatorConstructor->getType());
|
||||
$this->assertSame(PropertyWriteInfo::TYPE_NONE, $writeMutatorWithoutConstructor->getType());
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider extractConstructorTypesProvider
|
||||
*/
|
||||
public function testExtractConstructorTypes(string $property, ?array $type = null)
|
||||
{
|
||||
$this->assertEquals($type, $this->extractor->getTypesFromConstructor('Symfony\Component\PropertyInfo\Tests\Fixtures\ConstructorDummy', $property));
|
||||
}
|
||||
|
||||
public static function extractConstructorTypesProvider(): array
|
||||
{
|
||||
return [
|
||||
['timezone', [new Type(Type::BUILTIN_TYPE_OBJECT, false, 'DateTimeZone')]],
|
||||
['date', null],
|
||||
['dateObject', null],
|
||||
['dateTime', [new Type(Type::BUILTIN_TYPE_OBJECT, false, 'DateTimeImmutable')]],
|
||||
['ddd', null],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @requires PHP 8.4
|
||||
*/
|
||||
public function testAsymmetricVisibility()
|
||||
{
|
||||
$this->assertTrue($this->extractor->isReadable(AsymmetricVisibility::class, 'publicPrivate'));
|
||||
$this->assertTrue($this->extractor->isReadable(AsymmetricVisibility::class, 'publicProtected'));
|
||||
$this->assertFalse($this->extractor->isReadable(AsymmetricVisibility::class, 'protectedPrivate'));
|
||||
$this->assertFalse($this->extractor->isWritable(AsymmetricVisibility::class, 'publicPrivate'));
|
||||
$this->assertFalse($this->extractor->isWritable(AsymmetricVisibility::class, 'publicProtected'));
|
||||
$this->assertFalse($this->extractor->isWritable(AsymmetricVisibility::class, 'protectedPrivate'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @requires PHP 8.4
|
||||
*/
|
||||
public function testAsymmetricVisibilityAllowPublicOnly()
|
||||
{
|
||||
$extractor = new ReflectionExtractor(null, null, null, true, ReflectionExtractor::ALLOW_PUBLIC);
|
||||
|
||||
$this->assertTrue($extractor->isReadable(AsymmetricVisibility::class, 'publicPrivate'));
|
||||
$this->assertTrue($extractor->isReadable(AsymmetricVisibility::class, 'publicProtected'));
|
||||
$this->assertFalse($extractor->isReadable(AsymmetricVisibility::class, 'protectedPrivate'));
|
||||
$this->assertFalse($extractor->isWritable(AsymmetricVisibility::class, 'publicPrivate'));
|
||||
$this->assertFalse($extractor->isWritable(AsymmetricVisibility::class, 'publicProtected'));
|
||||
$this->assertFalse($extractor->isWritable(AsymmetricVisibility::class, 'protectedPrivate'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @requires PHP 8.4
|
||||
*/
|
||||
public function testAsymmetricVisibilityAllowProtectedOnly()
|
||||
{
|
||||
$extractor = new ReflectionExtractor(null, null, null, true, ReflectionExtractor::ALLOW_PROTECTED);
|
||||
|
||||
$this->assertFalse($extractor->isReadable(AsymmetricVisibility::class, 'publicPrivate'));
|
||||
$this->assertFalse($extractor->isReadable(AsymmetricVisibility::class, 'publicProtected'));
|
||||
$this->assertTrue($extractor->isReadable(AsymmetricVisibility::class, 'protectedPrivate'));
|
||||
$this->assertFalse($extractor->isWritable(AsymmetricVisibility::class, 'publicPrivate'));
|
||||
$this->assertTrue($extractor->isWritable(AsymmetricVisibility::class, 'publicProtected'));
|
||||
$this->assertFalse($extractor->isWritable(AsymmetricVisibility::class, 'protectedPrivate'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @requires PHP 8.4
|
||||
*/
|
||||
public function testAsymmetricVisibilityAllowPrivateOnly()
|
||||
{
|
||||
$extractor = new ReflectionExtractor(null, null, null, true, ReflectionExtractor::ALLOW_PRIVATE);
|
||||
|
||||
$this->assertFalse($extractor->isReadable(AsymmetricVisibility::class, 'publicPrivate'));
|
||||
$this->assertFalse($extractor->isReadable(AsymmetricVisibility::class, 'publicProtected'));
|
||||
$this->assertFalse($extractor->isReadable(AsymmetricVisibility::class, 'protectedPrivate'));
|
||||
$this->assertTrue($extractor->isWritable(AsymmetricVisibility::class, 'publicPrivate'));
|
||||
$this->assertFalse($extractor->isWritable(AsymmetricVisibility::class, 'publicProtected'));
|
||||
$this->assertTrue($extractor->isWritable(AsymmetricVisibility::class, 'protectedPrivate'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @requires PHP 8.4
|
||||
*/
|
||||
public function testVirtualProperties()
|
||||
{
|
||||
$this->assertTrue($this->extractor->isReadable(VirtualProperties::class, 'virtualNoSetHook'));
|
||||
$this->assertTrue($this->extractor->isReadable(VirtualProperties::class, 'virtualSetHookOnly'));
|
||||
$this->assertTrue($this->extractor->isReadable(VirtualProperties::class, 'virtualHook'));
|
||||
$this->assertFalse($this->extractor->isWritable(VirtualProperties::class, 'virtualNoSetHook'));
|
||||
$this->assertTrue($this->extractor->isWritable(VirtualProperties::class, 'virtualSetHookOnly'));
|
||||
$this->assertTrue($this->extractor->isWritable(VirtualProperties::class, 'virtualHook'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideAsymmetricVisibilityMutator
|
||||
* @requires PHP 8.4
|
||||
*/
|
||||
public function testAsymmetricVisibilityMutator(string $property, string $readVisibility, string $writeVisibility)
|
||||
{
|
||||
$extractor = new ReflectionExtractor(null, null, null, true, ReflectionExtractor::ALLOW_PUBLIC | ReflectionExtractor::ALLOW_PROTECTED | ReflectionExtractor::ALLOW_PRIVATE);
|
||||
$readMutator = $extractor->getReadInfo(AsymmetricVisibility::class, $property);
|
||||
$writeMutator = $extractor->getWriteInfo(AsymmetricVisibility::class, $property, [
|
||||
'enable_getter_setter_extraction' => true,
|
||||
]);
|
||||
|
||||
$this->assertSame(PropertyReadInfo::TYPE_PROPERTY, $readMutator->getType());
|
||||
$this->assertSame(PropertyWriteInfo::TYPE_PROPERTY, $writeMutator->getType());
|
||||
$this->assertSame($readVisibility, $readMutator->getVisibility());
|
||||
$this->assertSame($writeVisibility, $writeMutator->getVisibility());
|
||||
}
|
||||
|
||||
public static function provideAsymmetricVisibilityMutator(): iterable
|
||||
{
|
||||
yield ['publicPrivate', PropertyReadInfo::VISIBILITY_PUBLIC, PropertyWriteInfo::VISIBILITY_PRIVATE];
|
||||
yield ['publicProtected', PropertyReadInfo::VISIBILITY_PUBLIC, PropertyWriteInfo::VISIBILITY_PROTECTED];
|
||||
yield ['protectedPrivate', PropertyReadInfo::VISIBILITY_PROTECTED, PropertyWriteInfo::VISIBILITY_PRIVATE];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideVirtualPropertiesMutator
|
||||
* @requires PHP 8.4
|
||||
*/
|
||||
public function testVirtualPropertiesMutator(string $property, string $readVisibility, string $writeVisibility)
|
||||
{
|
||||
$extractor = new ReflectionExtractor(null, null, null, true, ReflectionExtractor::ALLOW_PUBLIC | ReflectionExtractor::ALLOW_PROTECTED | ReflectionExtractor::ALLOW_PRIVATE);
|
||||
$readMutator = $extractor->getReadInfo(VirtualProperties::class, $property);
|
||||
$writeMutator = $extractor->getWriteInfo(VirtualProperties::class, $property, [
|
||||
'enable_getter_setter_extraction' => true,
|
||||
]);
|
||||
|
||||
$this->assertSame(PropertyReadInfo::TYPE_PROPERTY, $readMutator->getType());
|
||||
$this->assertSame(PropertyWriteInfo::TYPE_PROPERTY, $writeMutator->getType());
|
||||
$this->assertSame($readVisibility, $readMutator->getVisibility());
|
||||
$this->assertSame($writeVisibility, $writeMutator->getVisibility());
|
||||
}
|
||||
|
||||
public static function provideVirtualPropertiesMutator(): iterable
|
||||
{
|
||||
yield ['virtualNoSetHook', PropertyReadInfo::VISIBILITY_PUBLIC, PropertyWriteInfo::VISIBILITY_PRIVATE];
|
||||
yield ['virtualSetHookOnly', PropertyReadInfo::VISIBILITY_PUBLIC, PropertyWriteInfo::VISIBILITY_PUBLIC];
|
||||
yield ['virtualHook', PropertyReadInfo::VISIBILITY_PUBLIC, PropertyWriteInfo::VISIBILITY_PUBLIC];
|
||||
}
|
||||
}
|
||||
63
vendor/symfony/property-info/Tests/Extractor/SerializerExtractorTest.php
vendored
Normal file
63
vendor/symfony/property-info/Tests/Extractor/SerializerExtractorTest.php
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
<?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\Tests\Extractor;
|
||||
|
||||
use Doctrine\Common\Annotations\AnnotationReader;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\PropertyInfo\Extractor\SerializerExtractor;
|
||||
use Symfony\Component\PropertyInfo\Tests\Fixtures\AdderRemoverDummy;
|
||||
use Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy;
|
||||
use Symfony\Component\PropertyInfo\Tests\Fixtures\IgnorePropertyDummy;
|
||||
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory;
|
||||
use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader;
|
||||
use Symfony\Component\Serializer\Mapping\Loader\AttributeLoader;
|
||||
|
||||
/**
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
class SerializerExtractorTest extends TestCase
|
||||
{
|
||||
private SerializerExtractor $extractor;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
if (class_exists(AttributeLoader::class)) {
|
||||
$classMetadataFactory = new ClassMetadataFactory(new AttributeLoader());
|
||||
} else {
|
||||
$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
|
||||
}
|
||||
$this->extractor = new SerializerExtractor($classMetadataFactory);
|
||||
}
|
||||
|
||||
public function testGetProperties()
|
||||
{
|
||||
$this->assertEquals(
|
||||
['collection'],
|
||||
$this->extractor->getProperties(Dummy::class, ['serializer_groups' => ['a']])
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetPropertiesWithIgnoredProperties()
|
||||
{
|
||||
$this->assertSame(['visibleProperty'], $this->extractor->getProperties(IgnorePropertyDummy::class, ['serializer_groups' => ['a']]));
|
||||
}
|
||||
|
||||
public function testGetPropertiesWithAnyGroup()
|
||||
{
|
||||
$this->assertSame(['analyses', 'feet'], $this->extractor->getProperties(AdderRemoverDummy::class, ['serializer_groups' => null]));
|
||||
}
|
||||
|
||||
public function testGetPropertiesWithNonExistentClassReturnsNull()
|
||||
{
|
||||
$this->assertSame(null, $this->extractor->getProperties('NonExistent'));
|
||||
}
|
||||
}
|
||||
29
vendor/symfony/property-info/Tests/Fixtures/AdderRemoverDummy.php
vendored
Normal file
29
vendor/symfony/property-info/Tests/Fixtures/AdderRemoverDummy.php
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
<?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\Tests\Fixtures;
|
||||
|
||||
/**
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
class AdderRemoverDummy
|
||||
{
|
||||
private $analyses;
|
||||
private $feet;
|
||||
|
||||
public function addAnalyse(Dummy $analyse)
|
||||
{
|
||||
}
|
||||
|
||||
public function removeFoot(Dummy $foot)
|
||||
{
|
||||
}
|
||||
}
|
||||
19
vendor/symfony/property-info/Tests/Fixtures/AsymmetricVisibility.php
vendored
Normal file
19
vendor/symfony/property-info/Tests/Fixtures/AsymmetricVisibility.php
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
<?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\Tests\Fixtures;
|
||||
|
||||
class AsymmetricVisibility
|
||||
{
|
||||
public private(set) mixed $publicPrivate;
|
||||
public protected(set) mixed $publicProtected;
|
||||
protected private(set) mixed $protectedPrivate;
|
||||
}
|
||||
40
vendor/symfony/property-info/Tests/Fixtures/ConstructorDummy.php
vendored
Normal file
40
vendor/symfony/property-info/Tests/Fixtures/ConstructorDummy.php
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
<?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\Tests\Fixtures;
|
||||
|
||||
/**
|
||||
* @author Dmitrii Poddubnyi <dpoddubny@gmail.com>
|
||||
*/
|
||||
class ConstructorDummy
|
||||
{
|
||||
/** @var string */
|
||||
private $timezone;
|
||||
|
||||
/** @var \DateTimeInterface */
|
||||
private $date;
|
||||
|
||||
/** @var int */
|
||||
private $dateTime;
|
||||
|
||||
/**
|
||||
* @param \DateTimeZone $timezone
|
||||
* @param int $date Timestamp
|
||||
* @param \DateTimeInterface $dateObject
|
||||
* @param mixed $mixed
|
||||
*/
|
||||
public function __construct(\DateTimeZone $timezone, $date, $dateObject, \DateTimeImmutable $dateTime, $mixed)
|
||||
{
|
||||
$this->timezone = $timezone->getName();
|
||||
$this->date = \DateTimeImmutable::createFromFormat('U', $date);
|
||||
$this->dateTime = $dateTime->getTimestamp();
|
||||
}
|
||||
}
|
||||
19
vendor/symfony/property-info/Tests/Fixtures/ConstructorDummyWithoutDocBlock.php
vendored
Normal file
19
vendor/symfony/property-info/Tests/Fixtures/ConstructorDummyWithoutDocBlock.php
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
<?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\Tests\Fixtures;
|
||||
|
||||
class ConstructorDummyWithoutDocBlock
|
||||
{
|
||||
public function __construct(\DateTimeZone $timezone, $date, $dateObject, \DateTimeImmutable $dateTime, $mixed)
|
||||
{
|
||||
}
|
||||
}
|
||||
24
vendor/symfony/property-info/Tests/Fixtures/DefaultValue.php
vendored
Normal file
24
vendor/symfony/property-info/Tests/Fixtures/DefaultValue.php
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
<?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\Tests\Fixtures;
|
||||
|
||||
/**
|
||||
* @author Tales Santos <tales.augusto.santos@gmail.com>
|
||||
*/
|
||||
class DefaultValue
|
||||
{
|
||||
public $defaultInt = 30;
|
||||
public $defaultFloat = 30.5;
|
||||
public $defaultString = 'foo';
|
||||
public $defaultArray = [];
|
||||
public $defaultNull = null;
|
||||
}
|
||||
62
vendor/symfony/property-info/Tests/Fixtures/DockBlockFallback.php
vendored
Normal file
62
vendor/symfony/property-info/Tests/Fixtures/DockBlockFallback.php
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
<?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\Tests\Fixtures;
|
||||
|
||||
/**
|
||||
* PhpDocExtractor should fallback from property -> accessor -> mutator when looking up docblocks.
|
||||
*
|
||||
* @author Martin Rademacher <mano@radebatz.net>
|
||||
*/
|
||||
class DockBlockFallback
|
||||
{
|
||||
/** @var string $pub */
|
||||
public $pub = 'pub';
|
||||
|
||||
protected $protAcc;
|
||||
protected $protMut;
|
||||
|
||||
public function getPub()
|
||||
{
|
||||
return $this->pub;
|
||||
}
|
||||
|
||||
public function setPub($pub)
|
||||
{
|
||||
$this->pub = $pub;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getProtAcc(): int
|
||||
{
|
||||
return $this->protAcc;
|
||||
}
|
||||
|
||||
public function setProt($protAcc)
|
||||
{
|
||||
$this->protAcc = $protAcc;
|
||||
}
|
||||
|
||||
public function getProtMut()
|
||||
{
|
||||
return $this->protMut;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $protMut
|
||||
*/
|
||||
public function setProtMut($protMut)
|
||||
{
|
||||
$this->protMut = $protMut;
|
||||
}
|
||||
}
|
||||
272
vendor/symfony/property-info/Tests/Fixtures/Dummy.php
vendored
Normal file
272
vendor/symfony/property-info/Tests/Fixtures/Dummy.php
vendored
Normal file
@@ -0,0 +1,272 @@
|
||||
<?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\Tests\Fixtures;
|
||||
|
||||
use Symfony\Component\Serializer\Annotation\Groups as GroupsAnnotation;
|
||||
use Symfony\Component\Serializer\Attribute\Groups;
|
||||
|
||||
/**
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
class Dummy extends ParentDummy
|
||||
{
|
||||
/**
|
||||
* @var string This is bar
|
||||
*/
|
||||
private $bar;
|
||||
|
||||
/**
|
||||
* Should be used.
|
||||
*
|
||||
* @var int Should be ignored
|
||||
*/
|
||||
protected $baz;
|
||||
|
||||
/**
|
||||
* @var \DateTimeImmutable
|
||||
*/
|
||||
public $bal;
|
||||
|
||||
/**
|
||||
* @var ParentDummy
|
||||
*/
|
||||
public $parent;
|
||||
|
||||
/**
|
||||
* @var \DateTimeImmutable[]
|
||||
* @GroupsAnnotation({"a", "b"})
|
||||
*/
|
||||
#[Groups(['a', 'b'])]
|
||||
public $collection;
|
||||
|
||||
/**
|
||||
* @var DummyCollection<int, string>
|
||||
*/
|
||||
public $collectionAsObject;
|
||||
|
||||
/**
|
||||
* @var string[][]
|
||||
*/
|
||||
public $nestedCollection;
|
||||
|
||||
/**
|
||||
* @var mixed[]
|
||||
*/
|
||||
public $mixedCollection;
|
||||
|
||||
/**
|
||||
* @var ParentDummy
|
||||
*/
|
||||
public $B;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $Id;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $Guid;
|
||||
|
||||
/**
|
||||
* Nullable array.
|
||||
*
|
||||
* @var array|null
|
||||
*/
|
||||
public $g;
|
||||
|
||||
/**
|
||||
* @var ?string
|
||||
*/
|
||||
public $h;
|
||||
|
||||
/**
|
||||
* @var string|int|null
|
||||
*/
|
||||
public $i;
|
||||
|
||||
/**
|
||||
* @var ?\DateTimeImmutable
|
||||
*/
|
||||
public $j;
|
||||
|
||||
/**
|
||||
* @var int[]|null
|
||||
*/
|
||||
public $nullableCollectionOfNonNullableElements;
|
||||
|
||||
/**
|
||||
* @var array<null|int>
|
||||
*/
|
||||
public $nonNullableCollectionOfNullableElements;
|
||||
|
||||
/**
|
||||
* @var null|array<int|string>
|
||||
*/
|
||||
public $nullableCollectionOfMultipleNonNullableElementTypes;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $xTotals;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $YT;
|
||||
|
||||
/**
|
||||
* This should not be removed.
|
||||
*
|
||||
* @var
|
||||
*/
|
||||
public $emptyVar;
|
||||
|
||||
/**
|
||||
* @var \Iterator<string>
|
||||
*/
|
||||
public $iteratorCollection;
|
||||
|
||||
/**
|
||||
* @var \Iterator<integer,string>
|
||||
*/
|
||||
public $iteratorCollectionWithKey;
|
||||
|
||||
/**
|
||||
* @var \Iterator<integer,\Iterator<integer,string>>
|
||||
*/
|
||||
public $nestedIterators;
|
||||
|
||||
/**
|
||||
* @var array<string,string>
|
||||
*/
|
||||
public $arrayWithKeys;
|
||||
|
||||
/**
|
||||
* @var array<string,array<integer,null|string>|null>
|
||||
*/
|
||||
public $arrayWithKeysAndComplexValue;
|
||||
|
||||
/**
|
||||
* @var array<string,mixed>
|
||||
*/
|
||||
public $arrayOfMixed;
|
||||
|
||||
/**
|
||||
* @var list<string>
|
||||
*/
|
||||
public $listOfStrings;
|
||||
|
||||
/**
|
||||
* @var parent
|
||||
*/
|
||||
public $parentAnnotation;
|
||||
|
||||
/**
|
||||
* @var \BackedEnum<string>
|
||||
*/
|
||||
public $genericInterface;
|
||||
|
||||
public static function getStatic()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public static function staticGetter()
|
||||
{
|
||||
}
|
||||
|
||||
public static function staticSetter(\DateTimeImmutable $d)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* A.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getA()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* B.
|
||||
*
|
||||
* @param ParentDummy|null $parent
|
||||
*/
|
||||
public function setB(?ParentDummy $parent = null)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Date of Birth.
|
||||
*
|
||||
* @return \DateTimeImmutable
|
||||
*/
|
||||
public function getDOB()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
}
|
||||
|
||||
public function get123()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @param self $self
|
||||
*/
|
||||
public function setSelf(self $self)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @param parent $realParent
|
||||
*/
|
||||
public function setRealParent(parent $realParent)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getXTotals()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getYT()
|
||||
{
|
||||
}
|
||||
|
||||
public function setDate(\DateTimeImmutable $date)
|
||||
{
|
||||
}
|
||||
|
||||
public function addDate(\DateTimeImmutable $date)
|
||||
{
|
||||
}
|
||||
|
||||
public function hasElement(string $element): bool
|
||||
{
|
||||
}
|
||||
}
|
||||
20
vendor/symfony/property-info/Tests/Fixtures/DummyCollection.php
vendored
Normal file
20
vendor/symfony/property-info/Tests/Fixtures/DummyCollection.php
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
<?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\Tests\Fixtures;
|
||||
|
||||
final class DummyCollection implements \IteratorAggregate
|
||||
{
|
||||
public function getIterator(): \Traversable
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
66
vendor/symfony/property-info/Tests/Fixtures/DummyExtractor.php
vendored
Normal file
66
vendor/symfony/property-info/Tests/Fixtures/DummyExtractor.php
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
<?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\Tests\Fixtures;
|
||||
|
||||
use Symfony\Component\PropertyInfo\Extractor\ConstructorArgumentTypeExtractorInterface;
|
||||
use Symfony\Component\PropertyInfo\PropertyAccessExtractorInterface;
|
||||
use Symfony\Component\PropertyInfo\PropertyDescriptionExtractorInterface;
|
||||
use Symfony\Component\PropertyInfo\PropertyInitializableExtractorInterface;
|
||||
use Symfony\Component\PropertyInfo\PropertyListExtractorInterface;
|
||||
use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
|
||||
use Symfony\Component\PropertyInfo\Type;
|
||||
|
||||
/**
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
class DummyExtractor implements PropertyListExtractorInterface, PropertyDescriptionExtractorInterface, PropertyTypeExtractorInterface, PropertyAccessExtractorInterface, PropertyInitializableExtractorInterface, ConstructorArgumentTypeExtractorInterface
|
||||
{
|
||||
public function getShortDescription($class, $property, array $context = []): ?string
|
||||
{
|
||||
return 'short';
|
||||
}
|
||||
|
||||
public function getLongDescription($class, $property, array $context = []): ?string
|
||||
{
|
||||
return 'long';
|
||||
}
|
||||
|
||||
public function getTypes($class, $property, array $context = []): ?array
|
||||
{
|
||||
return [new Type(Type::BUILTIN_TYPE_INT)];
|
||||
}
|
||||
|
||||
public function getTypesFromConstructor(string $class, string $property): ?array
|
||||
{
|
||||
return [new Type(Type::BUILTIN_TYPE_STRING)];
|
||||
}
|
||||
|
||||
public function isReadable($class, $property, array $context = []): ?bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function isWritable($class, $property, array $context = []): ?bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getProperties($class, array $context = []): ?array
|
||||
{
|
||||
return ['a', 'b'];
|
||||
}
|
||||
|
||||
public function isInitializable(string $class, string $property, array $context = []): ?bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
41
vendor/symfony/property-info/Tests/Fixtures/DummyGeneric.php
vendored
Normal file
41
vendor/symfony/property-info/Tests/Fixtures/DummyGeneric.php
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
<?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\Tests\Fixtures;
|
||||
|
||||
interface IFace {}
|
||||
|
||||
class Clazz {}
|
||||
|
||||
class DummyGeneric
|
||||
{
|
||||
|
||||
/**
|
||||
* @var Clazz<Dummy>
|
||||
*/
|
||||
public $basicClass;
|
||||
|
||||
/**
|
||||
* @var ?Clazz<Dummy>
|
||||
*/
|
||||
public $nullableClass;
|
||||
|
||||
/**
|
||||
* @var IFace<Dummy>
|
||||
*/
|
||||
public $basicInterface;
|
||||
|
||||
/**
|
||||
* @var ?IFace<Dummy>
|
||||
*/
|
||||
public $nullableInterface;
|
||||
|
||||
}
|
||||
23
vendor/symfony/property-info/Tests/Fixtures/DummyNamespace.php
vendored
Normal file
23
vendor/symfony/property-info/Tests/Fixtures/DummyNamespace.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\Tests\Fixtures;
|
||||
|
||||
use Symfony\Component\PropertyInfo\Tests as TestNamespace;
|
||||
|
||||
/**
|
||||
* @author Baptiste Leduc <baptiste.leduc@gmail.com>
|
||||
*/
|
||||
class DummyNamespace
|
||||
{
|
||||
/** @var TestNamespace\Fixtures\Dummy */
|
||||
private $dummy;
|
||||
}
|
||||
56
vendor/symfony/property-info/Tests/Fixtures/DummyTraitExternal.php
vendored
Normal file
56
vendor/symfony/property-info/Tests/Fixtures/DummyTraitExternal.php
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
<?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\Tests\Fixtures;
|
||||
|
||||
use Symfony\Component\PropertyInfo\Tests\Fixtures\TraitUsage\DummyUsedInTrait;
|
||||
|
||||
trait DummyTraitExternal
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $propertyInExternalTraitPrimitiveType;
|
||||
|
||||
/**
|
||||
* @var Dummy
|
||||
*/
|
||||
private $propertyInExternalTraitObjectSameNamespace;
|
||||
|
||||
/**
|
||||
* @var DummyUsedInTrait
|
||||
*/
|
||||
private $propertyInExternalTraitObjectDifferentNamespace;
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getMethodInExternalTraitPrimitiveType()
|
||||
{
|
||||
return 'value';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Dummy
|
||||
*/
|
||||
public function getMethodInExternalTraitObjectSameNamespace()
|
||||
{
|
||||
return new Dummy();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return DummyUsedInTrait
|
||||
*/
|
||||
public function getMethodInExternalTraitObjectDifferentNamespace()
|
||||
{
|
||||
return new DummyUsedInTrait();
|
||||
}
|
||||
}
|
||||
56
vendor/symfony/property-info/Tests/Fixtures/DummyUnionType.php
vendored
Normal file
56
vendor/symfony/property-info/Tests/Fixtures/DummyUnionType.php
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
<?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\Tests\Fixtures;
|
||||
|
||||
/**
|
||||
* @author Baptiste Leduc <baptiste.leduc@gmail.com>
|
||||
*/
|
||||
class DummyUnionType
|
||||
{
|
||||
private const TYPE_A = 'a';
|
||||
private const TYPE_B = 'b';
|
||||
|
||||
/**
|
||||
* @var string|int
|
||||
*/
|
||||
public $a;
|
||||
|
||||
/**
|
||||
* @var (string|int)[]
|
||||
*/
|
||||
public $b;
|
||||
|
||||
/**
|
||||
* @var array<string|int>
|
||||
*/
|
||||
public $c;
|
||||
|
||||
/**
|
||||
* @var array<string|int, array<string>>
|
||||
*/
|
||||
public $d;
|
||||
|
||||
/**
|
||||
* @var (Dummy<array<mixed, string>, (int | (\Traversable<DefaultValue>)[])> | ParentDummy | null)
|
||||
*/
|
||||
public $e;
|
||||
|
||||
/**
|
||||
* @var self::TYPE_*|null
|
||||
*/
|
||||
public $f;
|
||||
|
||||
/**
|
||||
* @var non-empty-array<string|int>
|
||||
*/
|
||||
public $g;
|
||||
}
|
||||
29
vendor/symfony/property-info/Tests/Fixtures/Extractor/DummyNamespace.php
vendored
Normal file
29
vendor/symfony/property-info/Tests/Fixtures/Extractor/DummyNamespace.php
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
<?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 A {
|
||||
class Property {
|
||||
|
||||
}
|
||||
|
||||
class Dummy {
|
||||
/**
|
||||
* @var Property
|
||||
*/
|
||||
public $property;
|
||||
}
|
||||
}
|
||||
|
||||
namespace B {
|
||||
class Dummy extends \A\Dummy {
|
||||
|
||||
}
|
||||
}
|
||||
36
vendor/symfony/property-info/Tests/Fixtures/IgnorePropertyDummy.php
vendored
Normal file
36
vendor/symfony/property-info/Tests/Fixtures/IgnorePropertyDummy.php
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\PropertyInfo\Tests\Fixtures;
|
||||
|
||||
use Symfony\Component\Serializer\Annotation\Groups as GroupsAnnotation;
|
||||
use Symfony\Component\Serializer\Annotation\Ignore as IgnoreAnnotation;
|
||||
use Symfony\Component\Serializer\Attribute\Groups;
|
||||
use Symfony\Component\Serializer\Attribute\Ignore;
|
||||
|
||||
/**
|
||||
* @author Vadim Borodavko <vadim.borodavko@gmail.com>
|
||||
*/
|
||||
class IgnorePropertyDummy
|
||||
{
|
||||
/**
|
||||
* @GroupsAnnotation({"a"})
|
||||
*/
|
||||
#[Groups(['a'])]
|
||||
public $visibleProperty;
|
||||
|
||||
/**
|
||||
* @GroupsAnnotation({"a"})
|
||||
* @IgnoreAnnotation
|
||||
*/
|
||||
#[Groups(['a']), Ignore]
|
||||
private $ignoredProperty;
|
||||
}
|
||||
30
vendor/symfony/property-info/Tests/Fixtures/IntRangeDummy.php
vendored
Normal file
30
vendor/symfony/property-info/Tests/Fixtures/IntRangeDummy.php
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\PropertyInfo\Tests\Fixtures;
|
||||
|
||||
class IntRangeDummy
|
||||
{
|
||||
/**
|
||||
* @var int<0, 100>
|
||||
*/
|
||||
public $a;
|
||||
|
||||
/**
|
||||
* @var int<min, 100>|null
|
||||
*/
|
||||
public $b;
|
||||
|
||||
/**
|
||||
* @var int<50, max>
|
||||
*/
|
||||
public $c;
|
||||
}
|
||||
50
vendor/symfony/property-info/Tests/Fixtures/InvalidDummy.php
vendored
Normal file
50
vendor/symfony/property-info/Tests/Fixtures/InvalidDummy.php
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
<?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\Tests\Fixtures;
|
||||
|
||||
/**
|
||||
* @author Martin Rademacher <mano@radebatz.net>
|
||||
*/
|
||||
class InvalidDummy
|
||||
{
|
||||
/**
|
||||
* @var
|
||||
*/
|
||||
public $pub;
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
public static function getStat()
|
||||
{
|
||||
return 'stat';
|
||||
}
|
||||
|
||||
/**
|
||||
* Foo.
|
||||
*
|
||||
* @param
|
||||
*/
|
||||
public function setFoo($foo)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Bar.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public function getBar()
|
||||
{
|
||||
return 'bar';
|
||||
}
|
||||
}
|
||||
19
vendor/symfony/property-info/Tests/Fixtures/NoProperties.php
vendored
Normal file
19
vendor/symfony/property-info/Tests/Fixtures/NoProperties.php
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
<?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\Tests\Fixtures;
|
||||
|
||||
/**
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
class NoProperties
|
||||
{
|
||||
}
|
||||
22
vendor/symfony/property-info/Tests/Fixtures/NotInstantiable.php
vendored
Normal file
22
vendor/symfony/property-info/Tests/Fixtures/NotInstantiable.php
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
<?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\Tests\Fixtures;
|
||||
|
||||
/**
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
class NotInstantiable
|
||||
{
|
||||
private function __construct(string $foo)
|
||||
{
|
||||
}
|
||||
}
|
||||
85
vendor/symfony/property-info/Tests/Fixtures/NullExtractor.php
vendored
Normal file
85
vendor/symfony/property-info/Tests/Fixtures/NullExtractor.php
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
<?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\Tests\Fixtures;
|
||||
|
||||
use Symfony\Component\PropertyInfo\PropertyAccessExtractorInterface;
|
||||
use Symfony\Component\PropertyInfo\PropertyDescriptionExtractorInterface;
|
||||
use Symfony\Component\PropertyInfo\PropertyInitializableExtractorInterface;
|
||||
use Symfony\Component\PropertyInfo\PropertyListExtractorInterface;
|
||||
use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
|
||||
|
||||
/**
|
||||
* Not able to guess anything.
|
||||
*
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
class NullExtractor implements PropertyListExtractorInterface, PropertyDescriptionExtractorInterface, PropertyTypeExtractorInterface, PropertyAccessExtractorInterface, PropertyInitializableExtractorInterface
|
||||
{
|
||||
public function getShortDescription($class, $property, array $context = []): ?string
|
||||
{
|
||||
$this->assertIsString($class);
|
||||
$this->assertIsString($property);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getLongDescription($class, $property, array $context = []): ?string
|
||||
{
|
||||
$this->assertIsString($class);
|
||||
$this->assertIsString($property);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getTypes($class, $property, array $context = []): ?array
|
||||
{
|
||||
$this->assertIsString($class);
|
||||
$this->assertIsString($property);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function isReadable($class, $property, array $context = []): ?bool
|
||||
{
|
||||
$this->assertIsString($class);
|
||||
$this->assertIsString($property);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function isWritable($class, $property, array $context = []): ?bool
|
||||
{
|
||||
$this->assertIsString($class);
|
||||
$this->assertIsString($property);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getProperties($class, array $context = []): ?array
|
||||
{
|
||||
$this->assertIsString($class);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function isInitializable(string $class, string $property, array $context = []): ?bool
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
private function assertIsString($string)
|
||||
{
|
||||
if (!\is_string($string)) {
|
||||
throw new \InvalidArgumentException(sprintf('"%s" expects strings, given "%s".', __CLASS__, get_debug_type($string)));
|
||||
}
|
||||
}
|
||||
}
|
||||
128
vendor/symfony/property-info/Tests/Fixtures/ParentDummy.php
vendored
Normal file
128
vendor/symfony/property-info/Tests/Fixtures/ParentDummy.php
vendored
Normal file
@@ -0,0 +1,128 @@
|
||||
<?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\Tests\Fixtures;
|
||||
|
||||
use Symfony\Component\PropertyInfo\Tests\Fixtures\RootDummy\RootDummyItem;
|
||||
|
||||
/**
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
class ParentDummy
|
||||
{
|
||||
/**
|
||||
* Short description.
|
||||
*
|
||||
* Long description.
|
||||
*/
|
||||
public $foo;
|
||||
|
||||
/**
|
||||
* @var float
|
||||
*/
|
||||
public $foo2;
|
||||
|
||||
/**
|
||||
* @var callable
|
||||
*/
|
||||
public $foo3;
|
||||
|
||||
/**
|
||||
* @var void
|
||||
*/
|
||||
public $foo4;
|
||||
|
||||
/**
|
||||
* @var mixed
|
||||
*/
|
||||
public $foo5;
|
||||
|
||||
/**
|
||||
* @var \SplFileInfo[]|resource
|
||||
*/
|
||||
public $files;
|
||||
|
||||
/**
|
||||
* @var static
|
||||
*/
|
||||
public $propertyTypeStatic;
|
||||
|
||||
/**
|
||||
* @var parent
|
||||
*/
|
||||
public $parentAnnotationNoParent;
|
||||
|
||||
/**
|
||||
* @var RootDummyItem[]
|
||||
*/
|
||||
public $rootDummyItems;
|
||||
|
||||
/**
|
||||
* @var \Symfony\Component\PropertyInfo\Tests\Fixtures\RootDummy\RootDummyItem
|
||||
*/
|
||||
public $rootDummyItem;
|
||||
|
||||
/**
|
||||
* @return bool|null
|
||||
*/
|
||||
public function isC()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true|null
|
||||
*/
|
||||
public function isCt()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return false|null
|
||||
*/
|
||||
public function isCf()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function canD()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true
|
||||
*/
|
||||
public function canDt()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return false
|
||||
*/
|
||||
public function canDf()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @param resource $e
|
||||
*/
|
||||
public function addE($e)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DateTimeImmutable $f
|
||||
*/
|
||||
public function removeF(\DateTimeImmutable $f)
|
||||
{
|
||||
}
|
||||
}
|
||||
57
vendor/symfony/property-info/Tests/Fixtures/Php71Dummy.php
vendored
Normal file
57
vendor/symfony/property-info/Tests/Fixtures/Php71Dummy.php
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
<?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\Tests\Fixtures;
|
||||
|
||||
/**
|
||||
* @author Teoh Han Hui <teohhanhui@gmail.com>
|
||||
*/
|
||||
class Php71Dummy
|
||||
{
|
||||
public function __construct(string $string, int $intPrivate)
|
||||
{
|
||||
}
|
||||
|
||||
public function getFoo(): ?array
|
||||
{
|
||||
}
|
||||
|
||||
public function getBuz(): void
|
||||
{
|
||||
}
|
||||
|
||||
public function setBar(?int $bar)
|
||||
{
|
||||
}
|
||||
|
||||
public function addBaz(string $baz)
|
||||
{
|
||||
}
|
||||
|
||||
public function removeBaz(string $baz)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class Php71DummyExtended extends Php71Dummy
|
||||
{
|
||||
}
|
||||
|
||||
class Php71DummyExtended2 extends Php71Dummy
|
||||
{
|
||||
public function __construct(int $intWithAccessor)
|
||||
{
|
||||
}
|
||||
|
||||
public function getIntWithAccessor()
|
||||
{
|
||||
}
|
||||
}
|
||||
40
vendor/symfony/property-info/Tests/Fixtures/Php74Dummy.php
vendored
Normal file
40
vendor/symfony/property-info/Tests/Fixtures/Php74Dummy.php
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
<?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\Tests\Fixtures;
|
||||
|
||||
/**
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
class Php74Dummy
|
||||
{
|
||||
public Dummy $dummy;
|
||||
private ?bool $nullableBoolProp;
|
||||
/** @var string[] */
|
||||
private array $stringCollection;
|
||||
private ?int $nullableWithDefault = 1;
|
||||
public array $collection = [];
|
||||
|
||||
/** @var Dummy[]|null */
|
||||
public ?array $nullableTypedCollection = null;
|
||||
|
||||
public function addStringCollection(string $string): void
|
||||
{
|
||||
}
|
||||
|
||||
public function removeStringCollection(string $string): void
|
||||
{
|
||||
}
|
||||
|
||||
public function addNullableTypedCollection(Dummy $dummy): void
|
||||
{
|
||||
}
|
||||
}
|
||||
38
vendor/symfony/property-info/Tests/Fixtures/Php7Dummy.php
vendored
Normal file
38
vendor/symfony/property-info/Tests/Fixtures/Php7Dummy.php
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\PropertyInfo\Tests\Fixtures;
|
||||
|
||||
/**
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
class Php7Dummy extends Php7ParentDummy
|
||||
{
|
||||
public function getFoo(): array
|
||||
{
|
||||
}
|
||||
|
||||
public function setBar(int $bar)
|
||||
{
|
||||
}
|
||||
|
||||
public function addBaz(string $baz)
|
||||
{
|
||||
}
|
||||
|
||||
public function getBuz(): self
|
||||
{
|
||||
}
|
||||
|
||||
public function getBiz(): parent
|
||||
{
|
||||
}
|
||||
}
|
||||
19
vendor/symfony/property-info/Tests/Fixtures/Php7ParentDummy.php
vendored
Normal file
19
vendor/symfony/property-info/Tests/Fixtures/Php7ParentDummy.php
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
<?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\Tests\Fixtures;
|
||||
|
||||
class Php7ParentDummy extends \stdClass
|
||||
{
|
||||
public function getParent(): parent
|
||||
{
|
||||
}
|
||||
}
|
||||
67
vendor/symfony/property-info/Tests/Fixtures/Php80Dummy.php
vendored
Normal file
67
vendor/symfony/property-info/Tests/Fixtures/Php80Dummy.php
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
<?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\Tests\Fixtures;
|
||||
|
||||
class Php80Dummy
|
||||
{
|
||||
public mixed $mixedProperty;
|
||||
|
||||
/**
|
||||
* @param string $promotedAndMutated
|
||||
* @param string $promotedWithDocComment
|
||||
* @param string $promotedWithDocCommentAndType
|
||||
* @param array<string> $collection
|
||||
*/
|
||||
public function __construct(
|
||||
private mixed $promoted,
|
||||
private mixed $promotedAndMutated,
|
||||
/**
|
||||
* Comment without @var.
|
||||
*/
|
||||
private mixed $promotedWithDocComment,
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private mixed $promotedWithDocCommentAndType,
|
||||
private array $collection,
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
public function getFoo(): array|null
|
||||
{
|
||||
}
|
||||
|
||||
public function setBar(int|null $bar)
|
||||
{
|
||||
}
|
||||
|
||||
public function setTimeout(int|float $timeout)
|
||||
{
|
||||
}
|
||||
|
||||
public function getOptional(): int|float|null
|
||||
{
|
||||
}
|
||||
|
||||
public function setString(string|\Stringable $string)
|
||||
{
|
||||
}
|
||||
|
||||
public function setPayload(mixed $payload)
|
||||
{
|
||||
}
|
||||
|
||||
public function getData(): mixed
|
||||
{
|
||||
}
|
||||
}
|
||||
24
vendor/symfony/property-info/Tests/Fixtures/Php80PromotedDummy.php
vendored
Normal file
24
vendor/symfony/property-info/Tests/Fixtures/Php80PromotedDummy.php
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
<?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\Tests\Fixtures;
|
||||
|
||||
class Php80PromotedDummy
|
||||
{
|
||||
public function __construct(private string $promoted)
|
||||
{
|
||||
}
|
||||
|
||||
public function getPromoted(): string
|
||||
{
|
||||
return $this->promoted;
|
||||
}
|
||||
}
|
||||
28
vendor/symfony/property-info/Tests/Fixtures/Php81Dummy.php
vendored
Normal file
28
vendor/symfony/property-info/Tests/Fixtures/Php81Dummy.php
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
<?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\Tests\Fixtures;
|
||||
|
||||
class Php81Dummy
|
||||
{
|
||||
public function __construct(public readonly string $foo)
|
||||
{
|
||||
}
|
||||
|
||||
public function getNothing(): never
|
||||
{
|
||||
throw new \Exception('Oops');
|
||||
}
|
||||
|
||||
public function getCollection(): \Traversable&\Countable
|
||||
{
|
||||
}
|
||||
}
|
||||
23
vendor/symfony/property-info/Tests/Fixtures/Php82Dummy.php
vendored
Normal file
23
vendor/symfony/property-info/Tests/Fixtures/Php82Dummy.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\Tests\Fixtures;
|
||||
|
||||
class Php82Dummy
|
||||
{
|
||||
public null $nil = null;
|
||||
|
||||
public false $false = false;
|
||||
|
||||
public true $true = true;
|
||||
|
||||
public (\Traversable&\Countable)|null $someCollection = null;
|
||||
}
|
||||
45
vendor/symfony/property-info/Tests/Fixtures/PhpStanPseudoTypesDummy.php
vendored
Normal file
45
vendor/symfony/property-info/Tests/Fixtures/PhpStanPseudoTypesDummy.php
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
<?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\Tests\Fixtures;
|
||||
|
||||
/**
|
||||
* @author Emil Masiakowski <emil.masiakowski@gmail.com>
|
||||
*/
|
||||
class PhpStanPseudoTypesDummy extends PseudoTypesDummy
|
||||
{
|
||||
/** @var negative-int */
|
||||
public $negativeInt;
|
||||
|
||||
/** @var non-empty-array */
|
||||
public $nonEmptyArray;
|
||||
|
||||
/** @var non-empty-list */
|
||||
public $nonEmptyList;
|
||||
|
||||
/** @var interface-string */
|
||||
public $interfaceString;
|
||||
|
||||
/** @var scalar */
|
||||
public $scalar;
|
||||
|
||||
/** @var array-key */
|
||||
public $arrayKey;
|
||||
|
||||
/** @var number */
|
||||
public $number;
|
||||
|
||||
/** @var numeric */
|
||||
public $numeric;
|
||||
|
||||
/** @var double */
|
||||
public $double;
|
||||
}
|
||||
20
vendor/symfony/property-info/Tests/Fixtures/PseudoTypeDummy.php
vendored
Normal file
20
vendor/symfony/property-info/Tests/Fixtures/PseudoTypeDummy.php
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
<?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\Tests\Fixtures;
|
||||
|
||||
class PseudoTypeDummy
|
||||
{
|
||||
/**
|
||||
* @var scalar
|
||||
*/
|
||||
public $unknownPseudoType;
|
||||
}
|
||||
48
vendor/symfony/property-info/Tests/Fixtures/PseudoTypesDummy.php
vendored
Normal file
48
vendor/symfony/property-info/Tests/Fixtures/PseudoTypesDummy.php
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
<?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\Tests\Fixtures;
|
||||
|
||||
/**
|
||||
* @author Emil Masiakowski <emil.masiakowski@gmail.com>
|
||||
*/
|
||||
class PseudoTypesDummy
|
||||
{
|
||||
/** @var class-string */
|
||||
public $classString;
|
||||
|
||||
/** @var class-string<\stdClass> */
|
||||
public $classStringGeneric;
|
||||
|
||||
/** @var html-escaped-string */
|
||||
public $htmlEscapedString;
|
||||
|
||||
/** @var lowercase-string */
|
||||
public $lowercaseString;
|
||||
|
||||
/** @var non-empty-lowercase-string */
|
||||
public $nonEmptyLowercaseString;
|
||||
|
||||
/** @var non-empty-string */
|
||||
public $nonEmptyString;
|
||||
|
||||
/** @var numeric-string */
|
||||
public $numericString;
|
||||
|
||||
/** @var trait-string */
|
||||
public $traitString;
|
||||
|
||||
/** @var positive-int */
|
||||
public $positiveInt;
|
||||
|
||||
/** @var literal-string */
|
||||
public $literalString;
|
||||
}
|
||||
16
vendor/symfony/property-info/Tests/Fixtures/RootDummy/RootDummyItem.php
vendored
Normal file
16
vendor/symfony/property-info/Tests/Fixtures/RootDummy/RootDummyItem.php
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
<?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\Tests\Fixtures\RootDummy;
|
||||
|
||||
class RootDummyItem
|
||||
{
|
||||
}
|
||||
39
vendor/symfony/property-info/Tests/Fixtures/SnakeCaseDummy.php
vendored
Normal file
39
vendor/symfony/property-info/Tests/Fixtures/SnakeCaseDummy.php
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\PropertyInfo\Tests\Fixtures;
|
||||
|
||||
class SnakeCaseDummy
|
||||
{
|
||||
private string $snake_property;
|
||||
private string $snake_readOnly;
|
||||
private string $snake_method;
|
||||
|
||||
public function getSnakeProperty()
|
||||
{
|
||||
return $this->snake_property;
|
||||
}
|
||||
|
||||
public function getSnakeReadOnly()
|
||||
{
|
||||
return $this->snake_readOnly;
|
||||
}
|
||||
|
||||
public function setSnakeProperty($snake_property)
|
||||
{
|
||||
$this->snake_property = $snake_property;
|
||||
}
|
||||
|
||||
public function setSnake_method($snake_method)
|
||||
{
|
||||
$this->snake_method = $snake_method;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\PropertyInfo\Tests\Fixtures\TraitUsage\AnotherNamespace;
|
||||
|
||||
class DummyInAnotherNamespace
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\PropertyInfo\Tests\Fixtures\TraitUsage\AnotherNamespace;
|
||||
|
||||
trait DummyTraitInAnotherNamespace
|
||||
{
|
||||
/**
|
||||
* @var DummyInAnotherNamespace
|
||||
*/
|
||||
public $dummyInAnotherNamespace;
|
||||
}
|
||||
59
vendor/symfony/property-info/Tests/Fixtures/TraitUsage/DummyTrait.php
vendored
Normal file
59
vendor/symfony/property-info/Tests/Fixtures/TraitUsage/DummyTrait.php
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\PropertyInfo\Tests\Fixtures\TraitUsage;
|
||||
|
||||
use Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy;
|
||||
use Symfony\Component\PropertyInfo\Tests\Fixtures\DummyTraitExternal;
|
||||
|
||||
trait DummyTrait
|
||||
{
|
||||
use DummyTraitExternal;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $propertyInTraitPrimitiveType;
|
||||
|
||||
/**
|
||||
* @var DummyUsedInTrait
|
||||
*/
|
||||
private $propertyInTraitObjectSameNamespace;
|
||||
|
||||
/**
|
||||
* @var Dummy
|
||||
*/
|
||||
private $propertyInTraitObjectDifferentNamespace;
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getMethodInTraitPrimitiveType()
|
||||
{
|
||||
return 'value';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return DummyUsedInTrait
|
||||
*/
|
||||
public function getMethodInTraitObjectSameNamespace()
|
||||
{
|
||||
return new DummyUsedInTrait();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Dummy
|
||||
*/
|
||||
public function getMethodInTraitObjectDifferentNamespace()
|
||||
{
|
||||
return new Dummy();
|
||||
}
|
||||
}
|
||||
16
vendor/symfony/property-info/Tests/Fixtures/TraitUsage/DummyUsedInTrait.php
vendored
Normal file
16
vendor/symfony/property-info/Tests/Fixtures/TraitUsage/DummyUsedInTrait.php
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
<?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\Tests\Fixtures\TraitUsage;
|
||||
|
||||
class DummyUsedInTrait
|
||||
{
|
||||
}
|
||||
20
vendor/symfony/property-info/Tests/Fixtures/TraitUsage/DummyUsingTrait.php
vendored
Normal file
20
vendor/symfony/property-info/Tests/Fixtures/TraitUsage/DummyUsingTrait.php
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
<?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\Tests\Fixtures\TraitUsage;
|
||||
|
||||
use Symfony\Component\PropertyInfo\Tests\Fixtures\TraitUsage\AnotherNamespace\DummyTraitInAnotherNamespace;
|
||||
|
||||
class DummyUsingTrait
|
||||
{
|
||||
use DummyTrait;
|
||||
use DummyTraitInAnotherNamespace;
|
||||
}
|
||||
19
vendor/symfony/property-info/Tests/Fixtures/VirtualProperties.php
vendored
Normal file
19
vendor/symfony/property-info/Tests/Fixtures/VirtualProperties.php
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
<?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\Tests\Fixtures;
|
||||
|
||||
class VirtualProperties
|
||||
{
|
||||
public bool $virtualNoSetHook { get => true; }
|
||||
public bool $virtualSetHookOnly { set => $value; }
|
||||
public bool $virtualHook { get => true; set => $value; }
|
||||
}
|
||||
70
vendor/symfony/property-info/Tests/PropertyInfoCacheExtractorTest.php
vendored
Normal file
70
vendor/symfony/property-info/Tests/PropertyInfoCacheExtractorTest.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\Tests;
|
||||
|
||||
use Symfony\Component\Cache\Adapter\ArrayAdapter;
|
||||
use Symfony\Component\PropertyInfo\PropertyInfoCacheExtractor;
|
||||
|
||||
/**
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
class PropertyInfoCacheExtractorTest extends AbstractPropertyInfoExtractorTest
|
||||
{
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->propertyInfo = new PropertyInfoCacheExtractor($this->propertyInfo, new ArrayAdapter());
|
||||
}
|
||||
|
||||
public function testGetShortDescription()
|
||||
{
|
||||
parent::testGetShortDescription();
|
||||
parent::testGetShortDescription();
|
||||
}
|
||||
|
||||
public function testGetLongDescription()
|
||||
{
|
||||
parent::testGetLongDescription();
|
||||
parent::testGetLongDescription();
|
||||
}
|
||||
|
||||
public function testGetTypes()
|
||||
{
|
||||
parent::testGetTypes();
|
||||
parent::testGetTypes();
|
||||
}
|
||||
|
||||
public function testIsReadable()
|
||||
{
|
||||
parent::testIsReadable();
|
||||
parent::testIsReadable();
|
||||
}
|
||||
|
||||
public function testIsWritable()
|
||||
{
|
||||
parent::testIsWritable();
|
||||
parent::testIsWritable();
|
||||
}
|
||||
|
||||
public function testGetProperties()
|
||||
{
|
||||
parent::testGetProperties();
|
||||
parent::testGetProperties();
|
||||
}
|
||||
|
||||
public function testIsInitializable()
|
||||
{
|
||||
parent::testIsInitializable();
|
||||
parent::testIsInitializable();
|
||||
}
|
||||
}
|
||||
19
vendor/symfony/property-info/Tests/PropertyInfoExtractorTest.php
vendored
Normal file
19
vendor/symfony/property-info/Tests/PropertyInfoExtractorTest.php
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
<?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\Tests;
|
||||
|
||||
/**
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
class PropertyInfoExtractorTest extends AbstractPropertyInfoExtractorTest
|
||||
{
|
||||
}
|
||||
88
vendor/symfony/property-info/Tests/TypeTest.php
vendored
Normal file
88
vendor/symfony/property-info/Tests/TypeTest.php
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
<?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\Tests;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\PropertyInfo\Type;
|
||||
|
||||
/**
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
class TypeTest extends TestCase
|
||||
{
|
||||
public function testConstruct()
|
||||
{
|
||||
$type = new Type('object', true, 'ArrayObject', true, new Type('int'), new Type('string'));
|
||||
|
||||
$this->assertEquals(Type::BUILTIN_TYPE_OBJECT, $type->getBuiltinType());
|
||||
$this->assertTrue($type->isNullable());
|
||||
$this->assertEquals('ArrayObject', $type->getClassName());
|
||||
$this->assertTrue($type->isCollection());
|
||||
|
||||
$collectionKeyTypes = $type->getCollectionKeyTypes();
|
||||
$this->assertIsArray($collectionKeyTypes);
|
||||
$this->assertContainsOnlyInstancesOf('Symfony\Component\PropertyInfo\Type', $collectionKeyTypes);
|
||||
$this->assertEquals(Type::BUILTIN_TYPE_INT, $collectionKeyTypes[0]->getBuiltinType());
|
||||
|
||||
$collectionValueTypes = $type->getCollectionValueTypes();
|
||||
$this->assertIsArray($collectionValueTypes);
|
||||
$this->assertContainsOnlyInstancesOf('Symfony\Component\PropertyInfo\Type', $collectionValueTypes);
|
||||
$this->assertEquals(Type::BUILTIN_TYPE_STRING, $collectionValueTypes[0]->getBuiltinType());
|
||||
}
|
||||
|
||||
public function testIterable()
|
||||
{
|
||||
$type = new Type('iterable');
|
||||
$this->assertSame('iterable', $type->getBuiltinType());
|
||||
}
|
||||
|
||||
public function testInvalidType()
|
||||
{
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('"foo" is not a valid PHP type.');
|
||||
new Type('foo');
|
||||
}
|
||||
|
||||
public function testArrayCollection()
|
||||
{
|
||||
$type = new Type('array', false, null, true, [new Type('int'), new Type('string')], [new Type('object', false, \ArrayObject::class, true), new Type('array', false, null, true)]);
|
||||
|
||||
$this->assertEquals(Type::BUILTIN_TYPE_ARRAY, $type->getBuiltinType());
|
||||
$this->assertFalse($type->isNullable());
|
||||
$this->assertTrue($type->isCollection());
|
||||
|
||||
[$firstKeyType, $secondKeyType] = $type->getCollectionKeyTypes();
|
||||
$this->assertEquals(Type::BUILTIN_TYPE_INT, $firstKeyType->getBuiltinType());
|
||||
$this->assertFalse($firstKeyType->isNullable());
|
||||
$this->assertFalse($firstKeyType->isCollection());
|
||||
$this->assertEquals(Type::BUILTIN_TYPE_STRING, $secondKeyType->getBuiltinType());
|
||||
$this->assertFalse($secondKeyType->isNullable());
|
||||
$this->assertFalse($secondKeyType->isCollection());
|
||||
|
||||
[$firstValueType, $secondValueType] = $type->getCollectionValueTypes();
|
||||
$this->assertEquals(Type::BUILTIN_TYPE_OBJECT, $firstValueType->getBuiltinType());
|
||||
$this->assertEquals(\ArrayObject::class, $firstValueType->getClassName());
|
||||
$this->assertFalse($firstValueType->isNullable());
|
||||
$this->assertTrue($firstValueType->isCollection());
|
||||
$this->assertEquals(Type::BUILTIN_TYPE_ARRAY, $secondValueType->getBuiltinType());
|
||||
$this->assertFalse($secondValueType->isNullable());
|
||||
$this->assertTrue($secondValueType->isCollection());
|
||||
}
|
||||
|
||||
public function testInvalidCollectionValueArgument()
|
||||
{
|
||||
$this->expectException(\TypeError::class);
|
||||
$this->expectExceptionMessage('"Symfony\Component\PropertyInfo\Type::validateCollectionArgument()": Argument #5 ($collectionKeyType) must be of type "Symfony\Component\PropertyInfo\Type[]", "Symfony\Component\PropertyInfo\Type" or "null", array value "array" given.');
|
||||
|
||||
new Type('array', false, null, true, [new \stdClass()], [new Type('string')]);
|
||||
}
|
||||
}
|
||||
165
vendor/symfony/property-info/Type.php
vendored
Normal file
165
vendor/symfony/property-info/Type.php
vendored
Normal file
@@ -0,0 +1,165 @@
|
||||
<?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_RESOURCE = 'resource';
|
||||
public const BUILTIN_TYPE_OBJECT = 'object';
|
||||
public const BUILTIN_TYPE_ARRAY = 'array';
|
||||
public const BUILTIN_TYPE_NULL = 'null';
|
||||
public const BUILTIN_TYPE_FALSE = 'false';
|
||||
public const BUILTIN_TYPE_TRUE = 'true';
|
||||
public const BUILTIN_TYPE_CALLABLE = 'callable';
|
||||
public const BUILTIN_TYPE_ITERABLE = 'iterable';
|
||||
|
||||
/**
|
||||
* List of PHP builtin types.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
public static $builtinTypes = [
|
||||
self::BUILTIN_TYPE_INT,
|
||||
self::BUILTIN_TYPE_FLOAT,
|
||||
self::BUILTIN_TYPE_STRING,
|
||||
self::BUILTIN_TYPE_BOOL,
|
||||
self::BUILTIN_TYPE_RESOURCE,
|
||||
self::BUILTIN_TYPE_OBJECT,
|
||||
self::BUILTIN_TYPE_ARRAY,
|
||||
self::BUILTIN_TYPE_CALLABLE,
|
||||
self::BUILTIN_TYPE_FALSE,
|
||||
self::BUILTIN_TYPE_TRUE,
|
||||
self::BUILTIN_TYPE_NULL,
|
||||
self::BUILTIN_TYPE_ITERABLE,
|
||||
];
|
||||
|
||||
/**
|
||||
* List of PHP builtin collection types.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
public static $builtinCollectionTypes = [
|
||||
self::BUILTIN_TYPE_ARRAY,
|
||||
self::BUILTIN_TYPE_ITERABLE,
|
||||
];
|
||||
|
||||
private string $builtinType;
|
||||
private bool $nullable;
|
||||
private ?string $class;
|
||||
private bool $collection;
|
||||
private array $collectionKeyType;
|
||||
private array $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, array|self|null $collectionKeyType = null, array|self|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(array|self|null $collectionArgument, int $argumentIndex, string $argumentName): ?array
|
||||
{
|
||||
if (null === $collectionArgument) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (\is_array($collectionArgument)) {
|
||||
foreach ($collectionArgument as $type) {
|
||||
if (!$type instanceof self) {
|
||||
throw new \TypeError(sprintf('"%s()": Argument #%d (%s) must be of type "%s[]", "%s" or "null", array value "%s" given.', __METHOD__, $argumentIndex, $argumentName, self::class, self::class, get_debug_type($collectionArgument)));
|
||||
}
|
||||
}
|
||||
|
||||
return $collectionArgument;
|
||||
}
|
||||
|
||||
return [$collectionArgument];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets built-in type.
|
||||
*
|
||||
* Can be bool, int, float, string, array, object, resource, null, callback or iterable.
|
||||
*/
|
||||
public function getBuiltinType(): string
|
||||
{
|
||||
return $this->builtinType;
|
||||
}
|
||||
|
||||
public function isNullable(): bool
|
||||
{
|
||||
return $this->nullable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the class name.
|
||||
*
|
||||
* Only applicable if the built-in type is object.
|
||||
*/
|
||||
public function getClassName(): ?string
|
||||
{
|
||||
return $this->class;
|
||||
}
|
||||
|
||||
public function isCollection(): bool
|
||||
{
|
||||
return $this->collection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets collection key types.
|
||||
*
|
||||
* Only applicable for a collection type.
|
||||
*
|
||||
* @return Type[]
|
||||
*/
|
||||
public function getCollectionKeyTypes(): array
|
||||
{
|
||||
return $this->collectionKeyType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets collection value types.
|
||||
*
|
||||
* Only applicable for a collection type.
|
||||
*
|
||||
* @return Type[]
|
||||
*/
|
||||
public function getCollectionValueTypes(): array
|
||||
{
|
||||
return $this->collectionValueType;
|
||||
}
|
||||
}
|
||||
198
vendor/symfony/property-info/Util/PhpDocTypeHelper.php
vendored
Normal file
198
vendor/symfony/property-info/Util/PhpDocTypeHelper.php
vendored
Normal file
@@ -0,0 +1,198 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\PropertyInfo\Util;
|
||||
|
||||
use phpDocumentor\Reflection\PseudoType;
|
||||
use phpDocumentor\Reflection\PseudoTypes\ConstExpression;
|
||||
use phpDocumentor\Reflection\PseudoTypes\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\Integer;
|
||||
use phpDocumentor\Reflection\Types\Null_;
|
||||
use phpDocumentor\Reflection\Types\Nullable;
|
||||
use phpDocumentor\Reflection\Types\String_;
|
||||
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
|
||||
{
|
||||
if ($varType instanceof ConstExpression) {
|
||||
// It's safer to fall back to other extractors here, as resolving const types correctly is not easy at the moment
|
||||
return [];
|
||||
}
|
||||
|
||||
$types = [];
|
||||
$nullable = false;
|
||||
|
||||
if ($varType instanceof Nullable) {
|
||||
$nullable = true;
|
||||
$varType = $varType->getActualType();
|
||||
}
|
||||
|
||||
if (!$varType instanceof 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 ($type instanceof ConstExpression) {
|
||||
// It's safer to fall back to other extractors here, as resolving const types correctly is not easy at the moment
|
||||
return [];
|
||||
}
|
||||
|
||||
// If null is present, all types are nullable
|
||||
if ($type instanceof Null_) {
|
||||
$nullable = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($type instanceof Nullable) {
|
||||
$nullable = true;
|
||||
$type = $type->getActualType();
|
||||
}
|
||||
|
||||
$varTypes[] = $type;
|
||||
}
|
||||
|
||||
foreach ($varTypes as $varType) {
|
||||
$type = $this->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): ?Type
|
||||
{
|
||||
$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);
|
||||
|
||||
$collection = \is_a($class, \Traversable::class, true) || \is_a($class, \ArrayAccess::class, true);
|
||||
|
||||
// it's safer to fall back to other extractors if the generic type is too abstract
|
||||
if (!$collection && !class_exists($class)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$keys = $this->getTypes($type->getKeyType());
|
||||
$values = $this->getTypes($type->getValueType());
|
||||
|
||||
return new Type($phpType, $nullable, $class, $collection, $keys, $values);
|
||||
}
|
||||
|
||||
// Cannot guess
|
||||
if (!$docType || 'mixed' === $docType) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (str_ends_with($docType, '[]') && $type instanceof Array_) {
|
||||
$collectionKeyTypes = new Type(Type::BUILTIN_TYPE_INT);
|
||||
$collectionValueTypes = $this->getTypes($type->getValueType());
|
||||
|
||||
return new Type(Type::BUILTIN_TYPE_ARRAY, $nullable, null, true, $collectionKeyTypes, $collectionValueTypes);
|
||||
}
|
||||
|
||||
if ((str_starts_with($docType, 'list<') || str_starts_with($docType, 'array<')) && $type instanceof Array_) {
|
||||
// array<value> is converted to x[] which is handled above
|
||||
// so it's only necessary to handle array<key, value> here
|
||||
$collectionKeyTypes = $this->getTypes($type->getKeyType());
|
||||
$collectionValueTypes = $this->getTypes($type->getValueType());
|
||||
|
||||
return new Type(Type::BUILTIN_TYPE_ARRAY, $nullable, null, true, $collectionKeyTypes, $collectionValueTypes);
|
||||
}
|
||||
|
||||
if ($type instanceof PseudoType) {
|
||||
if ($type->underlyingType() instanceof Integer) {
|
||||
return new Type(Type::BUILTIN_TYPE_INT, $nullable, null);
|
||||
} elseif ($type->underlyingType() instanceof String_) {
|
||||
return new Type(Type::BUILTIN_TYPE_STRING, $nullable, null);
|
||||
}
|
||||
}
|
||||
|
||||
$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
|
||||
{
|
||||
return match ($docType) {
|
||||
'integer' => 'int',
|
||||
'boolean' => 'bool',
|
||||
// real is not part of the PHPDoc standard, so we ignore it
|
||||
'double' => 'float',
|
||||
'callback' => 'callable',
|
||||
'void' => 'null',
|
||||
default => $docType,
|
||||
};
|
||||
}
|
||||
|
||||
private function 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, '\\')];
|
||||
}
|
||||
}
|
||||
211
vendor/symfony/property-info/Util/PhpStanTypeHelper.php
vendored
Normal file
211
vendor/symfony/property-info/Util/PhpStanTypeHelper.php
vendored
Normal file
@@ -0,0 +1,211 @@
|
||||
<?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) {
|
||||
if ('class-string' === $node->type->name) {
|
||||
return [new Type(Type::BUILTIN_TYPE_STRING)];
|
||||
}
|
||||
|
||||
[$mainType] = $this->extractTypes($node->type, $nameScope);
|
||||
|
||||
if (Type::BUILTIN_TYPE_INT === $mainType->getBuiltinType()) {
|
||||
return [$mainType];
|
||||
}
|
||||
|
||||
$collection = $mainType->isCollection() || \is_a($mainType->getClassName(), \Traversable::class, true) || \is_a($mainType->getClassName(), \ArrayAccess::class, true);
|
||||
|
||||
// it's safer to fall back to other extractors if the generic type is too abstract
|
||||
if (!$collection && !class_exists($mainType->getClassName()) && !interface_exists($mainType->getClassName(), false)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$collectionKeyTypes = $mainType->getCollectionKeyTypes();
|
||||
$collectionKeyValues = [];
|
||||
if (1 === \count($node->genericTypes)) {
|
||||
foreach ($this->extractTypes($node->genericTypes[0], $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(), $collection, $collectionKeyTypes, $collectionKeyValues)];
|
||||
}
|
||||
if ($node instanceof ArrayShapeNode) {
|
||||
return [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true)];
|
||||
}
|
||||
if ($node instanceof ArrayTypeNode) {
|
||||
return [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, [new Type(Type::BUILTIN_TYPE_INT)], $this->extractTypes($node->type, $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))];
|
||||
}
|
||||
|
||||
return match ($node->name) {
|
||||
'integer',
|
||||
'positive-int',
|
||||
'negative-int' => [new Type(Type::BUILTIN_TYPE_INT)],
|
||||
'double' => [new Type(Type::BUILTIN_TYPE_FLOAT)],
|
||||
'list',
|
||||
'non-empty-list' => [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT))],
|
||||
'non-empty-array' => [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true)],
|
||||
'mixed' => [], // mixed seems to be ignored in all other extractors
|
||||
'parent' => [new Type(Type::BUILTIN_TYPE_OBJECT, false, $node->name)],
|
||||
'static',
|
||||
'self' => [new Type(Type::BUILTIN_TYPE_OBJECT, false, $nameScope->resolveRootClass())],
|
||||
'class-string',
|
||||
'html-escaped-string',
|
||||
'lowercase-string',
|
||||
'non-empty-lowercase-string',
|
||||
'non-empty-string',
|
||||
'numeric-string',
|
||||
'trait-string',
|
||||
'interface-string',
|
||||
'literal-string' => [new Type(Type::BUILTIN_TYPE_STRING)],
|
||||
'void' => [new Type(Type::BUILTIN_TYPE_NULL)],
|
||||
'scalar' => [new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_FLOAT), new Type(Type::BUILTIN_TYPE_STRING), new Type(Type::BUILTIN_TYPE_BOOL)],
|
||||
'number' => [new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_FLOAT)],
|
||||
'numeric' => [new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_FLOAT), new Type(Type::BUILTIN_TYPE_STRING)],
|
||||
'array-key' => [new Type(Type::BUILTIN_TYPE_STRING), new Type(Type::BUILTIN_TYPE_INT)],
|
||||
default => [new Type(Type::BUILTIN_TYPE_OBJECT, false, $nameScope->resolveStringName($node->name))],
|
||||
};
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
31
vendor/symfony/property-info/phpunit.xml.dist
vendored
Normal file
31
vendor/symfony/property-info/phpunit.xml.dist
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/9.3/phpunit.xsd"
|
||||
backupGlobals="false"
|
||||
colors="true"
|
||||
bootstrap="vendor/autoload.php"
|
||||
failOnRisky="true"
|
||||
failOnWarning="true"
|
||||
>
|
||||
<php>
|
||||
<ini name="error_reporting" value="-1" />
|
||||
</php>
|
||||
|
||||
<testsuites>
|
||||
<testsuite name="Symfony Property Info Component Test Suite">
|
||||
<directory>./Tests/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<coverage>
|
||||
<include>
|
||||
<directory>./</directory>
|
||||
</include>
|
||||
<exclude>
|
||||
<directory>./Resources</directory>
|
||||
<directory>./Tests</directory>
|
||||
<directory>./vendor</directory>
|
||||
</exclude>
|
||||
</coverage>
|
||||
</phpunit>
|
||||
Reference in New Issue
Block a user