Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 76bcfb5

Browse files
committed
convert legacy types to TypeInfo types if getType() is not implemented
1 parent 7abc106 commit 76bcfb5

File tree

5 files changed

+279
-2
lines changed

5 files changed

+279
-2
lines changed

src/Symfony/Component/PropertyInfo/PropertyInfoCacheExtractor.php

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\PropertyInfo;
1313

1414
use Psr\Cache\CacheItemPoolInterface;
15+
use Symfony\Component\PropertyInfo\Util\LegacyTypeConverter;
1516
use Symfony\Component\TypeInfo\Type;
1617

1718
/**
@@ -61,7 +62,40 @@ public function getProperties(string $class, array $context = []): ?array
6162
*/
6263
public function getType(string $class, string $property, array $context = []): ?Type
6364
{
64-
return $this->extract('getType', [$class, $property, $context]);
65+
try {
66+
$serializedArguments = serialize([$class, $property, $context]);
67+
} catch (\Exception) {
68+
// If arguments are not serializable, skip the cache
69+
if (method_exists($this->propertyInfoExtractor, 'getType')) {
70+
return $this->propertyInfoExtractor->getType($class, $property, $context);
71+
}
72+
73+
return LegacyTypeConverter::toTypeInfoType($this->propertyInfoExtractor->getTypes($class, $property, $context));
74+
}
75+
76+
// Calling rawurlencode escapes special characters not allowed in PSR-6's keys
77+
$key = rawurlencode('getType.'.$serializedArguments);
78+
79+
if (\array_key_exists($key, $this->arrayCache)) {
80+
return $this->arrayCache[$key];
81+
}
82+
83+
$item = $this->cacheItemPool->getItem($key);
84+
85+
if ($item->isHit()) {
86+
return $this->arrayCache[$key] = $item->get();
87+
}
88+
89+
if (method_exists($this->propertyInfoExtractor, 'getType')) {
90+
$value = $this->propertyInfoExtractor->getType($class, $property, $context);
91+
} else {
92+
$value = LegacyTypeConverter::toTypeInfoType($this->propertyInfoExtractor->getTypes($class, $property, $context));
93+
}
94+
95+
$item->set($value);
96+
$this->cacheItemPool->save($item);
97+
98+
return $this->arrayCache[$key] = $value;
6599
}
66100

67101
public function getTypes(string $class, string $property, array $context = []): ?array

src/Symfony/Component/PropertyInfo/PropertyInfoExtractor.php

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Component\PropertyInfo;
1313

14+
use Symfony\Component\PropertyInfo\Util\LegacyTypeConverter;
1415
use Symfony\Component\TypeInfo\Type;
1516

1617
/**
@@ -58,7 +59,23 @@ public function getLongDescription(string $class, string $property, array $conte
5859
*/
5960
public function getType(string $class, string $property, array $context = []): ?Type
6061
{
61-
return $this->extract($this->typeExtractors, 'getType', [$class, $property, $context]);
62+
foreach ($this->typeExtractors as $extractor) {
63+
if (!method_exists($extractor, 'getType')) {
64+
$legacyTypes = $extractor->getTypes($class, $property, $context);
65+
66+
if (null !== $legacyTypes) {
67+
return LegacyTypeConverter::toTypeInfoType($legacyTypes);
68+
}
69+
70+
continue;
71+
}
72+
73+
if (null !== $value = $extractor->getType($class, $property, $context)) {
74+
return $value;
75+
}
76+
}
77+
78+
return null;
6279
}
6380

6481
public function getTypes(string $class, string $property, array $context = []): ?array

src/Symfony/Component/PropertyInfo/Tests/PropertyInfoCacheExtractorTest.php

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,13 @@
1212
namespace Symfony\Component\PropertyInfo\Tests;
1313

1414
use Symfony\Component\Cache\Adapter\ArrayAdapter;
15+
use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor;
1516
use Symfony\Component\PropertyInfo\PropertyInfoCacheExtractor;
17+
use Symfony\Component\PropertyInfo\PropertyInfoExtractorInterface;
18+
use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
19+
use Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy;
20+
use Symfony\Component\PropertyInfo\Tests\Fixtures\ParentDummy;
21+
use Symfony\Component\TypeInfo\Type;
1622

1723
/**
1824
* @author Kévin Dunglas <[email protected]>
@@ -76,4 +82,89 @@ public function testIsInitializable()
7682
parent::testIsInitializable();
7783
parent::testIsInitializable();
7884
}
85+
86+
/**
87+
* @group legacy
88+
* @dataProvider provideNestedExtractorWithoutGetTypeImplementationData
89+
*/
90+
public function testNestedExtractorWithoutGetTypeImplementation(string $property, ?Type $expectedType)
91+
{
92+
$propertyInfoCacheExtractor = new PropertyInfoCacheExtractor(new class() implements PropertyInfoExtractorInterface {
93+
private PropertyTypeExtractorInterface $propertyTypeExtractor;
94+
95+
public function __construct()
96+
{
97+
$this->propertyTypeExtractor = new PhpDocExtractor();
98+
}
99+
100+
public function getTypes(string $class, string $property, array $context = []): ?array
101+
{
102+
return $this->propertyTypeExtractor->getTypes($class, $property, $context);
103+
}
104+
105+
public function isReadable(string $class, string $property, array $context = []): ?bool
106+
{
107+
return null;
108+
}
109+
110+
public function isWritable(string $class, string $property, array $context = []): ?bool
111+
{
112+
return null;
113+
}
114+
115+
public function getShortDescription(string $class, string $property, array $context = []): ?string
116+
{
117+
return null;
118+
}
119+
120+
public function getLongDescription(string $class, string $property, array $context = []): ?string
121+
{
122+
return null;
123+
}
124+
125+
public function getProperties(string $class, array $context = []): ?array
126+
{
127+
return null;
128+
}
129+
}, new ArrayAdapter());
130+
131+
if (null === $expectedType) {
132+
$this->assertNull($propertyInfoCacheExtractor->getType(Dummy::class, $property));
133+
} else {
134+
$this->assertEquals($expectedType, $propertyInfoCacheExtractor->getType(Dummy::class, $property));
135+
}
136+
}
137+
138+
public function provideNestedExtractorWithoutGetTypeImplementationData()
139+
{
140+
yield ['bar', Type::string()];
141+
yield ['baz', Type::int()];
142+
yield ['bal', Type::object(\DateTimeImmutable::class)];
143+
yield ['parent', Type::object(ParentDummy::class)];
144+
yield ['collection', Type::array(Type::object(\DateTimeImmutable::class), Type::int())];
145+
yield ['nestedCollection', Type::array(Type::array(Type::string(), Type::int()), Type::int())];
146+
yield ['mixedCollection', Type::array()];
147+
yield ['B', Type::object(ParentDummy::class)];
148+
yield ['Id', Type::int()];
149+
yield ['Guid', Type::string()];
150+
yield ['g', Type::nullable(Type::array())];
151+
yield ['h', Type::nullable(Type::string())];
152+
yield ['i', Type::nullable(Type::union(Type::string(), Type::int()))];
153+
yield ['j', Type::nullable(Type::object(\DateTimeImmutable::class))];
154+
yield ['nullableCollectionOfNonNullableElements', Type::nullable(Type::array(Type::int(), Type::int()))];
155+
yield ['nonNullableCollectionOfNullableElements', Type::array(Type::nullable(Type::int()), Type::int())];
156+
yield ['nullableCollectionOfMultipleNonNullableElementTypes', Type::nullable(Type::array(Type::union(Type::int(), Type::string()), Type::int()))];
157+
yield ['xTotals', Type::array()];
158+
yield ['YT', Type::string()];
159+
yield ['emptyVar', null];
160+
yield ['iteratorCollection', Type::collection(Type::object(\Iterator::class), Type::string(), Type::union(Type::string(), Type::int()))];
161+
yield ['iteratorCollectionWithKey', Type::collection(Type::object(\Iterator::class), Type::string(), Type::int())];
162+
yield ['nestedIterators', Type::collection(Type::object(\Iterator::class), Type::collection(Type::object(\Iterator::class), Type::string(), Type::int()), Type::int())];
163+
yield ['arrayWithKeys', Type::array(Type::string(), Type::string())];
164+
yield ['arrayWithKeysAndComplexValue', Type::array(Type::nullable(Type::array(Type::nullable(Type::string()), Type::int())), Type::string())];
165+
yield ['arrayOfMixed', Type::array(Type::mixed(), Type::string())];
166+
yield ['noDocBlock', null];
167+
yield ['listOfStrings', Type::array(Type::string(), Type::int())];
168+
yield ['parentAnnotation', Type::object(ParentDummy::class)];
169+
}
79170
}

src/Symfony/Component/PropertyInfo/Tests/PropertyInfoExtractorTest.php

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,75 @@
1111

1212
namespace Symfony\Component\PropertyInfo\Tests;
1313

14+
use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor;
15+
use Symfony\Component\PropertyInfo\PropertyInfoExtractor;
16+
use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
17+
use Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy;
18+
use Symfony\Component\PropertyInfo\Tests\Fixtures\ParentDummy;
19+
use Symfony\Component\TypeInfo\Type;
20+
1421
/**
1522
* @author Kévin Dunglas <[email protected]>
1623
*/
1724
class PropertyInfoExtractorTest extends AbstractPropertyInfoExtractorTest
1825
{
26+
/**
27+
* @group legacy
28+
* @dataProvider provideNestedExtractorWithoutGetTypeImplementationData
29+
*/
30+
public function testNestedExtractorWithoutGetTypeImplementation(string $property, ?Type $expectedType)
31+
{
32+
$propertyInfoExtractor = new PropertyInfoExtractor([], [new class() implements PropertyTypeExtractorInterface {
33+
private PropertyTypeExtractorInterface $propertyTypeExtractor;
34+
35+
public function __construct()
36+
{
37+
$this->propertyTypeExtractor = new PhpDocExtractor();
38+
}
39+
40+
public function getTypes(string $class, string $property, array $context = []): ?array
41+
{
42+
return $this->propertyTypeExtractor->getTypes($class, $property, $context);
43+
}
44+
}]);
45+
46+
if (null === $expectedType) {
47+
$this->assertNull($propertyInfoExtractor->getType(Dummy::class, $property));
48+
} else {
49+
$this->assertEquals($expectedType, $propertyInfoExtractor->getType(Dummy::class, $property));
50+
}
51+
}
52+
53+
public function provideNestedExtractorWithoutGetTypeImplementationData()
54+
{
55+
yield ['bar', Type::string()];
56+
yield ['baz', Type::int()];
57+
yield ['bal', Type::object(\DateTimeImmutable::class)];
58+
yield ['parent', Type::object(ParentDummy::class)];
59+
yield ['collection', Type::array(Type::object(\DateTimeImmutable::class), Type::int())];
60+
yield ['nestedCollection', Type::array(Type::array(Type::string(), Type::int()), Type::int())];
61+
yield ['mixedCollection', Type::array()];
62+
yield ['B', Type::object(ParentDummy::class)];
63+
yield ['Id', Type::int()];
64+
yield ['Guid', Type::string()];
65+
yield ['g', Type::nullable(Type::array())];
66+
yield ['h', Type::nullable(Type::string())];
67+
yield ['i', Type::nullable(Type::union(Type::string(), Type::int()))];
68+
yield ['j', Type::nullable(Type::object(\DateTimeImmutable::class))];
69+
yield ['nullableCollectionOfNonNullableElements', Type::nullable(Type::array(Type::int(), Type::int()))];
70+
yield ['nonNullableCollectionOfNullableElements', Type::array(Type::nullable(Type::int()), Type::int())];
71+
yield ['nullableCollectionOfMultipleNonNullableElementTypes', Type::nullable(Type::array(Type::union(Type::int(), Type::string()), Type::int()))];
72+
yield ['xTotals', Type::array()];
73+
yield ['YT', Type::string()];
74+
yield ['emptyVar', null];
75+
yield ['iteratorCollection', Type::collection(Type::object(\Iterator::class), Type::string(), Type::union(Type::string(), Type::int()))];
76+
yield ['iteratorCollectionWithKey', Type::collection(Type::object(\Iterator::class), Type::string(), Type::int())];
77+
yield ['nestedIterators', Type::collection(Type::object(\Iterator::class), Type::collection(Type::object(\Iterator::class), Type::string(), Type::int()), Type::int())];
78+
yield ['arrayWithKeys', Type::array(Type::string(), Type::string())];
79+
yield ['arrayWithKeysAndComplexValue', Type::array(Type::nullable(Type::array(Type::nullable(Type::string()), Type::int())), Type::string())];
80+
yield ['arrayOfMixed', Type::array(Type::mixed(), Type::string())];
81+
yield ['noDocBlock', null];
82+
yield ['listOfStrings', Type::array(Type::string(), Type::int())];
83+
yield ['parentAnnotation', Type::object(ParentDummy::class)];
84+
}
1985
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
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\Util;
13+
14+
use Symfony\Component\PropertyInfo\Type as LegacyType;
15+
use Symfony\Component\TypeInfo\Type;
16+
17+
/**
18+
* @internal
19+
*/
20+
class LegacyTypeConverter
21+
{
22+
/**
23+
* @param LegacyType[]|null $legacyTypes
24+
*/
25+
public static function toTypeInfoType(?array $legacyTypes): ?Type
26+
{
27+
if (null === $legacyTypes || [] === $legacyTypes) {
28+
return null;
29+
}
30+
31+
$nullable = false;
32+
$types = [];
33+
34+
foreach ($legacyTypes as $legacyType) {
35+
if ($legacyType->isCollection() && !\in_array($legacyType->getBuiltinType(), [LegacyType::BUILTIN_TYPE_ARRAY, LegacyType::BUILTIN_TYPE_ITERABLE], true)) {
36+
$typeInfoType = Type::collection(Type::object(\Iterator::class), self::toTypeInfoType($legacyType->getCollectionValueTypes()), self::toTypeInfoType($legacyType->getCollectionKeyTypes()));
37+
} else {
38+
$typeInfoType = match ($legacyType->getBuiltinType()) {
39+
LegacyType::BUILTIN_TYPE_ARRAY => Type::array(self::toTypeInfoType($legacyType->getCollectionValueTypes()), self::toTypeInfoType($legacyType->getCollectionKeyTypes())),
40+
LegacyType::BUILTIN_TYPE_BOOL => Type::bool(),
41+
LegacyType::BUILTIN_TYPE_CALLABLE => Type::callable(),
42+
LegacyType::BUILTIN_TYPE_FALSE => Type::false(),
43+
LegacyType::BUILTIN_TYPE_FLOAT => Type::float(),
44+
LegacyType::BUILTIN_TYPE_INT => Type::int(),
45+
LegacyType::BUILTIN_TYPE_ITERABLE => Type::iterable(self::toTypeInfoType($legacyType->getCollectionValueTypes()), self::toTypeInfoType($legacyType->getCollectionKeyTypes())),
46+
LegacyType::BUILTIN_TYPE_OBJECT => Type::object($legacyType->getClassName()),
47+
LegacyType::BUILTIN_TYPE_RESOURCE => Type::resource(),
48+
LegacyType::BUILTIN_TYPE_STRING => Type::string(),
49+
LegacyType::BUILTIN_TYPE_TRUE => Type::true(),
50+
default => null,
51+
};
52+
}
53+
54+
if (LegacyType::BUILTIN_TYPE_NULL === $legacyType->getBuiltinType() || $legacyType->isNullable()) {
55+
$nullable = true;
56+
}
57+
58+
if (null !== $typeInfoType) {
59+
$types[] = $typeInfoType;
60+
}
61+
}
62+
63+
if (1 === \count($types)) {
64+
return $nullable ? Type::nullable($types[0]) : $types[0];
65+
}
66+
67+
return $nullable ? Type::nullable(Type::union(...$types)) : Type::union(...$types);
68+
}
69+
}

0 commit comments

Comments
 (0)