8000 bug #59012 [PropertyInfo] Fix interface handling in `PhpStanTypeHelpe… · symfony/symfony@c439a58 · GitHub
[go: up one dir, main page]

Skip to content

Commit c439a58

Browse files
committed
bug #59012 [PropertyInfo] Fix interface handling in PhpStanTypeHelper (janedbal)
This PR was squashed before being merged into the 6.4 branch. Discussion ---------- [PropertyInfo] Fix interface handling in `PhpStanTypeHelper` | Q | A | ------------- | --- | Branch? | 7.1 | Bug fix? | yes | New feature? | no | Deprecations? | no | License | MIT Previously, deserializing object with `Interface<T>` property was failing on undefined array key in `PhpStanTypeHelper` (generic `Clazz<T>` works just fine): ``` ErrorException: Undefined array key 0 /app/backend/vendor/symfony/property-info/Util/PhpStanTypeHelper.php:171 /app/backend/vendor/symfony/property-info/Util/PhpStanTypeHelper.php:49 /app/backend/vendor/symfony/property-info/Extractor/PhpStanExtractor.php:130 /app/backend/vendor/symfony/property-info/PropertyInfoExtractor.php:93 /app/backend/vendor/symfony/property-info/PropertyInfoExtractor.php:66 /app/backend/vendor/symfony/property-info/PropertyInfoCacheExtractor.php:102 /app/backend/vendor/symfony/property-info/PropertyInfoCacheExtractor.php:69 /app/backend/vendor/symfony/serializer/Normalizer/AbstractObjectNormalizer.php:669 /app/backend/vendor/symfony/serializer/Normalizer/AbstractObjectNormalizer.php:646 /app/backend/vendor/symfony/serializer/Normalizer/AbstractNormalizer.php:386 /app/backend/vendor/symfony/serializer/Normalizer/AbstractObjectNormalizer.php:243 /app/backend/vendor/symfony/serializer/Normalizer/AbstractObjectNormalizer.php:349 /app/backend/vendor/symfony/serializer/Serializer.php:247 /app/backend/vendor/symfony/serializer/Serializer.php:152 ``` Commits ------- 88e7a72 [PropertyInfo] Fix interface handling in `PhpStanTypeHelper`
2 parents 33a5a3e + 88e7a72 commit c439a58

File tree

3 files changed

+127
-2
lines changed

3 files changed

+127
-2
lines changed

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

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,13 @@
1414
use PHPUnit\Framework\TestCase;
1515
use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor;
1616
use Symfony\Component\PropertyInfo\Extractor\PhpStanExtractor;
17+
use Symfony\Component\PropertyInfo\Tests\Fixtures\Clazz;
1718
use Symfony\Component\PropertyInfo\Tests\Fixtures\Cons 8000 tructorDummyWithoutDocBlock;
1819
use Symfony\Component\PropertyInfo\Tests\Fixtures\DefaultValue;
1920
use Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy;
2021
use Symfony\Component\PropertyInfo\Tests\Fixtures\DummyCollection;
22+
use Symfony\Component\PropertyInfo\Tests\Fixtures\DummyGeneric;
23+
use Symfony\Component\PropertyInfo\Tests\Fixtures\IFace;
2124
use Symfony\Component\PropertyInfo\Tests\Fixtures\ParentDummy;
2225
use Symfony\Component\PropertyInfo\Tests\Fixtures\Php80Dummy;
2326
use Symfony\Component\PropertyInfo\Tests\Fixtures\Php80PromotedDummy;
@@ -482,7 +485,88 @@ public static function php80TypesProvider()
482485

483486
public function testGenericInterface()
484487
{
485-
$this->assertNull($this->extractor->getTypes(Dummy::class, 'genericInterface'));
488+
$this->assertEquals(
489+
[
490+
new Type(
491+
builtinType: Type::BUILTIN_TYPE_OBJECT,
492+
class: \BackedEnum::class,
493+
collectionValueType: new Type(
494+
builtinType: Type::BUILTIN_TYPE_STRING,
495+
)
496+
),
497+
],
498+
$this->extractor->getTypes(Dummy::class, 'genericInterface')
499+
);
500+
}
501+
502+
/**
503+
* @param list<Type> $expectedTypes
504+
* @dataProvider genericsProvider
505+
*/
506+
public function testGenericsLegacy(string $property, array $expectedTypes)
507+
{
508+
$this->assertEquals($expectedTypes, $this->extractor->getTypes(DummyGeneric::class, $property));
509+
}
510+
511+
/**
512+
* @return iterable<array{0: string, 1: list<Type>}>
513+
*/
514+
public static function genericsProvider(): iterable
515+
{
516+
yield [
517+
'basicClass',
518+
[
519+
new Type(
520+
builtinType: Type::BUILTIN_TYPE_OBJECT,
521+
class: Clazz::class,
522+
collectionValueType: new Type(
523+
builtinType: Type::BUILTIN_TYPE_OBJECT,
524+
class: Dummy::class,
525+
)
526+
),
527+
],
528+
];
529+
yield [
530+
'nullableClass',
531+
[
532+
new Type(
533+
builtinType: Type::BUILTIN_TYPE_OBJECT,
534+
class: Clazz::class,
535+
nullable: true,
536+
collectionValueType: new Type(
537+
builtinType: Type::BUILTIN_TYPE_OBJECT,
538+
class: Dummy::class,
539+
)
540+
),
541+
],
542+
];
543+
yield [
544+
'basicInterface',
545+
[
546+
new Type(
547+
builtinType: Type::BUILTIN_TYPE_OBJECT,
548+
class: IFace::class,
549+
collectionValueType: new Type(
550+
builtinType: Type::BUILTIN_TYPE_OBJECT,
551+
class: Dummy::class,
552+
)
553+
),
554+
],
555+
];
556+
yield [
557+
'nullableInterface',
558+
[
559+
new Type(
560+
builtinType: Type::BUILTIN_TYPE_OBJECT,
561+
class: IFace::class,
562+
nullable: true,
563+
collectionValueType: new Type(
564+
builtinType: Type::BUILTIN_TYPE_OBJECT,
565+
class: Dummy::class,
566+
)
567+
),
568+
],
569+
];
486570
}
487571
}
488572

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\PropertyInfo\Tests\Fixtures;
13+
14+
interface IFace {}
15+
16+
class Clazz {}
17+
18+
class DummyGeneric
19+
{
20+
21+
/**
22+
* @var Clazz<Dummy>
23+
*/
24+
public $basicClass;
25+
26+
/**
27+
* @var ?Clazz<Dummy>
28+
*/
29+
public $nullableClass;
30+
31+
/**
32+
* @var IFace<Dummy>
33+
*/
34+
public $basicInterface;
35+
36+
/**
37+
* @var ?IFace<Dummy>
38+
*/
39+
public $nullableInterface;
40+
41+
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ private function extractTypes(TypeNode $node, NameScope $nameScope): array
128128
$collection = $mainType->isCollection() || \is_a($mainType->getClassName(), \Traversable::class, true) || \is_a($mainType->getClassName(), \ArrayAccess::class, true);
129129

130130
// it's safer to fall back to other extractors if the generic type is too abstract
131-
if (!$collection && !class_exists($mainType->getClassName())) {
131+
if (!$collection && !class_exists($mainType->getClassName()) && !interface_exists($mainType->getClassName(), false)) {
132132
return [];
133133
}
134134

0 commit comments

Comments
 (0)
0