Skip to content

Commit e31aeeb

Browse files
committed
[PropertyAccessor] Fix unexpected collection when generics
1 parent 2c9352d commit e31aeeb

File tree

8 files changed

+39
-7
lines changed

8 files changed

+39
-7
lines changed

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,11 @@ public function testUnknownPseudoType()
428428
$this->assertEquals([new Type(Type::BUILTIN_TYPE_OBJECT, false, 'scalar')], $this->extractor->getTypes(PseudoTypeDummy::class, 'unknownPseudoType'));
429429
}
430430

431+
public function testGenericInterface()
432+
{
433+
$this->assertNull($this->extractor->getTypes(Dummy::class, 'genericInterface'));
434+
}
435+
431436
protected static function isPhpDocumentorV5()
432437
{
433438
if (class_exists(InvalidTag::class)) {

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

Lines changed: 6 additions & 1 deletion
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

Lines changed: 3 additions & 0 deletions
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

Lines changed: 5 additions & 0 deletions
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

Lines changed: 1 addition & 1 deletion
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

Lines changed: 10 additions & 3 deletions
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 = \is_a($class, \Traversable::class, true) || \is_a($class, \ArrayAccess::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

Lines changed: 8 additions & 1 deletion
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

Lines changed: 1 addition & 1 deletion
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy