584 lines
26 KiB
PHP
584 lines
26 KiB
PHP
<?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)
|
|
{
|
|
}
|
|
}
|