8000 [PropertyAccessor] Fix unexpected collection when generics · symfony/symfony@9ecc19d · GitHub
[go: up one dir, main page]

Skip to content

Commit 9ecc19d

Browse files
committed
[PropertyAccessor] Fix unexpected collection when generics
1 parent 998fa9d commit 9ecc19d

File tree

8 files changed

+39
-7
lines changed

8 files changed

+39
-7
lines changed

src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php

+5
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,11 @@ public function testUnknownPseudoType()
419419
$this->assertEquals([new Type(Type::BUILTIN_TYPE_OBJECT, false, 'scalar')], $this->extractor->getTypes(PseudoTypeDummy::class, 'unknownPseudoType'));
420420
}
421421

422+
public function testGenericInterface()
423+
{
424+
$this->assertNull($this->extractor->getTypes(Dummy::class, 'genericInterface'));
425+
}
426+
422427
protected static function isPhpDocumentorV5()
423428
{
424429
if (class_exists(InvalidTag::class)) {

src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpStanExtractorTest.php

+6-1
Original file line numberDiff line numberDiff line change
@@ -388,7 +388,7 @@ public static function unionTypesProvider(): array
388388
['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)])]],
389389
['c', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, [], [new Type(Type::BUILTIN_TYPE_STRING), new Type(Type::BUILTIN_TYPE_INT)])]],
390390
['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)])])]],
391-
['e', [new Type(Type::BUILTIN_TYPE_OBJECT, true, Dummy::class, true, [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_STRING, false, null, true, [], [new Type(Type::BUILTIN_TYPE_OBJECT, false, DefaultValue::class)])])]), new Type(Type::BUILTIN_TYPE_OBJECT, false, ParentDummy::class)]],
391+
['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)]],
392392
['f', null],
393393
['g', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, [], [new Type(Type::BUILTIN_TYPE_STRING), new Type(Type::BUILTIN_TYPE_INT)])]],
394394
];
@@ -427,6 +427,11 @@ public static function intRangeTypeProvider(): array
427427
['c', [new Type(Type::BUILTIN_TYPE_INT)]],
428428
];
429429
}
430+
431+
public function testGenericInterface()
432+
{
433+
$this->assertNull($this->extractor->getTypes(Dummy::class, 'genericInterface'));
434+
}
430435
}
431436

432437
class PhpStanOmittedParamTagTypeDocBlock

src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php

+3
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ public function testGetProperties()
7373
'arrayOfMixed',
7474
'listOfStrings',
7575
'parentAnnotation',
76+
'genericInterface',
7677
'foo',
7778
'foo2',
7879
'foo3',
@@ -137,6 +138,7 @@ public function testGetPropertiesWithCustomPrefixes()
137138
'arrayOfMixed',
138139
'listOfStrings',
139140
'parentAnnotation',
141+
'genericInterface',
140142
'foo',
141143
'foo2',
142144
'foo3',
@@ -190,6 +192,7 @@ public function testGetPropertiesWithNoPrefixes()
190192
'arrayOfMixed',
191193
'listOfStrings',
192194
'parentAnnotation',
195+
'genericInterface',
193196
'foo',
194197
'foo2',
195198
'foo3',

src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php

+5
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,11 @@ class Dummy extends ParentDummy
165165
*/
166166
public $parentAnnotation;
167167

168+
/**
169+
* @var \BackedEnum<string>
170+
*/
171+
public $genericInterface;
172+
168173
public static function getStatic()
169174
{
170175
}

src/Symfony/Component/PropertyInfo/Tests/Fixtures/DummyUnionType.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ class DummyUnionType
4040
public $d;
4141

4242
/**
43-
* @var (Dummy<array<mixed, string>, (int | (string<DefaultValue>)[])> | ParentDummy | null)
43+
* @var (Dummy<array<mixed, string>, (int | (\Traversable<DefaultValue>)[])> | ParentDummy | null)
4444
*/
4545
public $e;
4646

src/Symfony/Component/PropertyInfo/Util/PhpDocTypeHelper.php

+10-3
Original file line numberDiff line numberDiff line change
@@ -102,9 +102,9 @@ public function getTypes(DocType $varType): array
102102
/**
103103
* Creates a {@see Type} from a PHPDoc type.
104104
*/
105-
private function createType(DocType $type, bool $nullable, ?string $docType = null): ?Type
105+
private function createType(DocType $type, bool $nullable): ?Type
106106
{
107-
$docType = $docType ?? (string) $type;
107+
$docType = (string) $type;
108108

109109
if ($type instanceof Collection) {
110110
$fqsen = $type->getFqsen();
@@ -115,10 +115,17 @@ private function createType(DocType $type, bool $nullable, ?string $docType = nu
115115

116116
[$phpType, $class] = $this->getPhpTypeAndClass((string) $fqsen);
117117

118+
$collection = \in_array($class, [\Traversable::class, \Iterator::class, \IteratorAggregate::class, \ArrayAccess::class, \Generator::class], true);
119+
120+
// it's safer to fall back to other extractors if the generic type is too abstract
121+
if (!$collection && !class_exists($class)) {
122+
return null;
123+
}
124+
118125
$keys = $this->getTypes($type->getKeyType());
119126
$values = $this->getTypes($type->getValueType());
120127

121-
return new Type($phpType, $nullable, $class, true, $keys, $values);
128+
return new Type($phpType, $nullable, $class, $collection, $keys, $values);
122129
}
123130

124131
// Cannot guess

src/Symfony/Component/PropertyInfo/Util/PhpStanTypeHelper.php

+8-1
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,13 @@ private function extractTypes(TypeNode $node, NameScope $nameScope): array
121121
return [$mainType];
122122
}
123123

124+
$collection = $mainType->isCollection() || \in_array($mainType->getClassName(), [\Traversable::class, \Iterator::class, \IteratorAggregate::class, \ArrayAccess::class, \Generator::class], true);
125+
126+
// it's safer to fall back to other extractors if the generic type is too abstract
127+
if (!$collection && !class_exists($mainType->getClassName())) {
128+
return [];
129+
}
130+
124131
$collectionKeyTypes = $mainType->getCollectionKeyTypes();
125132
$collectionKeyValues = [];
126133
if (1 === \count($node->genericTypes)) {
@@ -136,7 +143,7 @@ private function extractTypes(TypeNode $node, NameScope $nameScope): array
136143
}
137144
}
138145

139-
return [new Type($mainType->getBuiltinType(), $mainType->isNullable(), $mainType->getClassName(), true, $collectionKeyTypes, $collectionKeyValues)];
146+
return [new Type($mainType->getBuiltinType(), $mainType->isNullable(), $mainType->getClassName(), $collection, $collectionKeyTypes, $collectionKeyValues)];
140147
}
141148
if ($node instanceof ArrayShapeNode) {
142149
return [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true)];

src/Symfony/Component/Serializer/Tests/DeserializeNestedArrayOfObjectsTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ class ZooWithKeyTypes
156156
public $animalsString = [];
157157
/** @var array<int|string, Animal> */
158158
public $animalsUnion = [];
159-
/** @var \stdClass<Animal> */
159+
/** @var \Traversable<Animal> */
160160
public $animalsGenerics = [];
161161
}
162162

0 commit comments

Comments
 (0)
0