Skip to content

Commit d6a29ae

Browse files
bug #44578 [PropertyInfo] Fix phpstan extractor issues (ostrolucky)
This PR was squashed before being merged into the 5.4 branch. Discussion ---------- [PropertyInfo] Fix phpstan extractor issues | Q | A | ------------- | --- | Branch? | 5.4 | Bug fix? | yes | New feature? | no | Deprecations? | no | Tickets | Fix #44491 | License | MIT | Doc PR | Since this crashes denormalization for codebase that was upgraded from 5.3 to 5.4, I am classifying this as a bugfix. This PR fixes 2 issues. One is with docblocks like `/** `@var` Foo::*|null */`, where Extractor returns `null` only and afterwards Serializer fails to accept anything else. Second is `/** `@var` non-empty-array<...> */` where extractor doesn't recognize that `non-empty-array` is still an array and afterwards Serializer fails complaining that `array` is not an `non-empty-array` Commits ------- 0302128 [PropertyInfo] Fix phpstan extractor issues
2 parents d17ae34 + 0302128 commit d6a29ae

File tree

4 files changed

+42
-1
lines changed

4 files changed

+42
-1
lines changed

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,7 @@ public function constructorTypesProvider()
355355
/**
356356
* @dataProvider unionTypesProvider
357357
*/
358-
public function testExtractorUnionTypes(string $property, array $types)
358+
public function testExtractorUnionTypes(string $property, ?array $types)
359359
{
360360
$this->assertEquals($types, $this->extractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\DummyUnionType', $property));
361361
}
@@ -368,6 +368,8 @@ public function unionTypesProvider(): array
368368
['c', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, [], [new Type(Type::BUILTIN_TYPE_STRING), new Type(Type::BUILTIN_TYPE_INT)])]],
369369
['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)])])]],
370370
['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)]],
371+
['f', null],
372+
['g', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, [], [new Type(Type::BUILTIN_TYPE_STRING), new Type(Type::BUILTIN_TYPE_INT)])]],
371373
];
372374
}
373375

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
*/
1717
class DummyUnionType
1818
{
19+
private const TYPE_A = 'a';
20+
private const TYPE_B = 'b';
21+
1922
/**
2023
* @var string|int
2124
*/
@@ -40,4 +43,14 @@ class DummyUnionType
4043
* @var (Dummy<array<mixed, string>, (int | (string<DefaultValue>)[])> | ParentDummy | null)
4144
*/
4245
public $e;
46+
47+
/**
48+
* @var self::TYPE_*|null
49+
*/
50+
public $f;
51+
52+
/**
53+
* @var non-empty-array<string|int>
54+
*/
55+
public $g;
4356
}

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
use PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode;
2020
use PHPStan\PhpDocParser\Ast\Type\CallableTypeNode;
2121
use PHPStan\PhpDocParser\Ast\Type\CallableTypeParameterNode;
22+
use PHPStan\PhpDocParser\Ast\Type\ConstTypeNode;
2223
use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode;
2324
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
2425
use PHPStan\PhpDocParser\Ast\Type\NullableTypeNode;
@@ -102,6 +103,10 @@ private function extractTypes(TypeNode $node, NameScope $nameScope): array
102103
if ($node instanceof UnionTypeNode) {
103104
$types = [];
104105
foreach ($node->types as $type) {
106+
if ($type instanceof ConstTypeNode) {
107+
// It's safer to fall back to other extractors here, as resolving const types correctly is not easy at the moment
108+
return [];
109+
}
105110
foreach ($this->extractTypes($type, $nameScope) as $subType) {
106111
$types[] = $subType;
107112
}
@@ -160,7 +165,10 @@ private function extractTypes(TypeNode $node, NameScope $nameScope): array
160165
case 'integer':
161166
return [new Type(Type::BUILTIN_TYPE_INT)];
162167
case 'list':
168+
case 'non-empty-list':
163169
return [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT))];
170+
case 'non-empty-array':
171+
return [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true)];
164172
case 'mixed':
165173
return []; // mixed seems to be ignored in all other extractors
166174
case 'parent':

src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@
1212
namespace Symfony\Component\Serializer\Tests\Normalizer;
1313

1414
use Doctrine\Common\Annotations\AnnotationReader;
15+
use PHPStan\PhpDocParser\Parser\PhpDocParser;
1516
use PHPUnit\Framework\TestCase;
1617
use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor;
18+
use Symfony\Component\PropertyInfo\Extractor\PhpStanExtractor;
1719
use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor;
1820
use Symfony\Component\PropertyInfo\PropertyInfoExtractor;
1921
use Symfony\Component\Serializer\Exception\LogicException;
@@ -721,6 +723,22 @@ public function testAcceptJsonNumber()
721723
$this->assertSame(10.0, $serializer->denormalize(['number' => 10], JsonNumber::class, 'jsonld')->number);
722724
}
723725

726+
public function testDoesntHaveIssuesWithUnionConstTypes()
727+
{
728+
if (!class_exists(PhpStanExtractor::class) || !class_exists(PhpDocParser::class)) {
729+
$this->markTestSkipped('phpstan/phpdoc-parser required for this test');
730+
}
731+
732+
$extractor = new PropertyInfoExtractor([], [new PhpStanExtractor(), new PhpDocExtractor(), new ReflectionExtractor()]);
733+
$normalizer = new ObjectNormalizer(null, null, null, $extractor);
734+
$serializer = new Serializer([new ArrayDenormalizer(), new DateTimeNormalizer(), $normalizer]);
735+
736+
$this->assertSame('bar', $serializer->denormalize(['foo' => 'bar'], \get_class(new class() {
737+
/** @var self::*|null */
738+
public $foo;
739+
}))->foo);
740+
}
741+
724742
public function testExtractAttributesRespectsFormat()
725743
{
726744
$normalizer = new FormatAndContextAwareNormalizer();

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