Skip to content

Commit 89e20c8

Browse files
mtarldKorbeil
andcommitted
[TypeInfo][PropertyInfo] Deprecate PropertyInfo Type
Co-authored-by: Baptiste Leduc <[email protected]>
1 parent 5feec6a commit 89e20c8

File tree

4 files changed

+220
-61
lines changed

4 files changed

+220
-61
lines changed

src/Symfony/Component/PropertyInfo/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
CHANGELOG
22
=========
33

4+
7.1
5+
---
6+
7+
* Deprecate the `Type` class, use `Symfony\Component\TypeInfo\Type` class of `symfony/type-info` component instead
8+
49
6.4
510
---
611

src/Symfony/Component/PropertyInfo/Type.php

Lines changed: 206 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -11,46 +11,57 @@
1111

1212
namespace Symfony\Component\PropertyInfo;
1313

14+
use Symfony\Component\TypeInfo\Type as TypeInfoType;
15+
use Symfony\Component\TypeInfo\Type\BuiltinType;
16+
use Symfony\Component\TypeInfo\Type\GenericType;
17+
use Symfony\Component\TypeInfo\Type\IntersectionType;
18+
use Symfony\Component\TypeInfo\Type\ObjectType;
19+
use Symfony\Component\TypeInfo\Type\UnionType;
20+
21+
trigger_deprecation('symfony/property-info', '7.1', 'The "%s" class is deprecated. Use "%s" of "symfony/type-info" component instead.', Type::class, TypeInfoType::class);
22+
1423
/**
1524
* Type value object (immutable).
1625
*
1726
* @author Kévin Dunglas <[email protected]>
1827
*
1928
* @final
29+
*
30+
* @deprecated since Symfony 7.1, use "Symfony\Component\TypeInfo\Type" of "symfony/type-info" component instead.
2031
*/
2132
class Type
2233
{
23-
public const BUILTIN_TYPE_INT = 'int';
24-
public const BUILTIN_TYPE_FLOAT = 'float';
25-
public const BUILTIN_TYPE_STRING = 'string';
26-
public const BUILTIN_TYPE_BOOL = 'bool';
27-
public const BUILTIN_TYPE_RESOURCE = 'resource';
28-
public const BUILTIN_TYPE_OBJECT = 'object';
29-
public const BUILTIN_TYPE_ARRAY = 'array';
30-
public const BUILTIN_TYPE_NULL = 'null';
31-
public const BUILTIN_TYPE_FALSE = 'false';
32-
public const BUILTIN_TYPE_TRUE = 'true';
33-
public const BUILTIN_TYPE_CALLABLE = 'callable';
34-
public const BUILTIN_TYPE_ITERABLE = 'iterable';
34+
public const BUILTIN_TYPE_INT = TypeInfoType::BUILTIN_TYPE_INT;
35+
public const BUILTIN_TYPE_FLOAT = TypeInfoType::BUILTIN_TYPE_FLOAT;
36+
public const BUILTIN_TYPE_STRING = TypeInfoType::BUILTIN_TYPE_STRING;
37+
public const BUILTIN_TYPE_BOOL = TypeInfoType::BUILTIN_TYPE_BOOL;
38+
public const BUILTIN_TYPE_RESOURCE = TypeInfoType::BUILTIN_TYPE_RESOURCE;
39+
public const BUILTIN_TYPE_OBJECT = TypeInfoType::BUILTIN_TYPE_OBJECT;
40+
public const BUILTIN_TYPE_ARRAY = TypeInfoType::BUILTIN_TYPE_ARRAY;
41+
public const BUILTIN_TYPE_NULL = TypeInfoType::BUILTIN_TYPE_NULL;
42+
public const BUILTIN_TYPE_FALSE = TypeInfoType::BUILTIN_TYPE_FALSE;
43+
public const BUILTIN_TYPE_TRUE = TypeInfoType::BUILTIN_TYPE_TRUE;
44+
public const BUILTIN_TYPE_CALLABLE = TypeInfoType::BUILTIN_TYPE_CALLABLE;
45+
public const BUILTIN_TYPE_ITERABLE = TypeInfoType::BUILTIN_TYPE_ITERABLE;
3546

3647
/**
3748
* List of PHP builtin types.
3849
*
3950
* @var string[]
4051
*/
4152
public static array $builtinTypes = [
42-
self::BUILTIN_TYPE_INT,
43-
self::BUILTIN_TYPE_FLOAT,
44-
self::BUILTIN_TYPE_STRING,
45-
self::BUILTIN_TYPE_BOOL,
46-
self::BUILTIN_TYPE_RESOURCE,
47-
self::BUILTIN_TYPE_OBJECT,
48-
self::BUILTIN_TYPE_ARRAY,
49-
self::BUILTIN_TYPE_CALLABLE,
50-
self::BUILTIN_TYPE_FALSE,
51-
self::BUILTIN_TYPE_TRUE,
52-
self::BUILTIN_TYPE_NULL,
53-
self::BUILTIN_TYPE_ITERABLE,
53+
TypeInfoType::BUILTIN_TYPE_INT,
54+
TypeInfoType::BUILTIN_TYPE_FLOAT,
55+
TypeInfoType::BUILTIN_TYPE_STRING,
56+
TypeInfoType::BUILTIN_TYPE_BOOL,
57+
TypeInfoType::BUILTIN_TYPE_RESOURCE,
58+
TypeInfoType::BUILTIN_TYPE_OBJECT,
59+
TypeInfoType::BUILTIN_TYPE_ARRAY,
60+
TypeInfoType::BUILTIN_TYPE_CALLABLE,
61+
TypeInfoType::BUILTIN_TYPE_FALSE,
62+
TypeInfoType::BUILTIN_TYPE_TRUE,
63+
TypeInfoType::BUILTIN_TYPE_NULL,
64+
TypeInfoType::BUILTIN_TYPE_ITERABLE,
5465
];
5566

5667
/**
@@ -59,54 +70,59 @@ class Type
5970
* @var string[]
6071
*/
6172
public static array $builtinCollectionTypes = [
62-
self::BUILTIN_TYPE_ARRAY,
63-
self::BUILTIN_TYPE_ITERABLE,
73+
TypeInfoType::BUILTIN_TYPE_ARRAY,
74+
TypeInfoType::BUILTIN_TYPE_ITERABLE,
6475
];
6576

66-
private string $builtinType;
67-
private bool $nullable;
68-
private ?string $class;
69-
private bool $collection;
70-
private array $collectionKeyType;
71-
private array $collectionValueType;
77+
private TypeInfoType $internalType;
7278

7379
/**
7480
* @param Type[]|Type|null $collectionKeyType
7581
* @param Type[]|Type|null $collectionValueType
7682
*
7783
* @throws \InvalidArgumentException
7884
*/
79-
public function __construct(string $builtinType, bool $nullable = false, string $class = null, bool $collection = false, array|Type $collectionKeyType = null, array|Type $collectionValueType = null)
85+
public function __construct(string $builtinType, bool $nullable = false, string $class = null, bool $collection = false, array|self $collectionKeyType = null, array|self $collectionValueType = null)
8086
{
81-
if (!\in_array($builtinType, self::$builtinTypes)) {
82-
throw new \InvalidArgumentException(sprintf('"%s" is not a valid PHP type.', $builtinType));
83-
}
87+
$genericTypes = [];
8488

85-
$this->builtinType = $builtinType;
86-
$this->nullable = $nullable;
87-
$this->class = $class;
88-
$this->collection = $collection;
89-
$this->collectionKeyType = $this->validateCollectionArgument($collectionKeyType, 5, '$collectionKeyType') ?? [];
90-
$this->collectionValueType = $this->validateCollectionArgument($collectionValueType, 6, '$collectionValueType') ?? [];
91-
}
89+
$collectionKeyType = $this->validateCollectionArgument($collectionKeyType, 5, '$collectionKeyType') ?? [];
90+
$collectionValueType = $this->validateCollectionArgument($collectionValueType, 6, '$collectionValueType') ?? [];
9291

93-
private function validateCollectionArgument(array|Type|null $collectionArgument, int $argumentIndex, string $argumentName): ?array
94-
{
95-
if (null === $collectionArgument) {
96-
return null;
92+
if (null !== $collectionKeyType && [] !== $collectionKeyType) {
93+
if (\is_array($collectionKeyType)) {
94+
$collectionKeyType = array_unique(array_map(fn ($t): TypeInfoType => $t->getTypeInfoType(), $collectionKeyType));
95+
$genericTypes[] = \count($collectionKeyType) > 1 ? TypeInfoType::union(...$collectionKeyType) : $collectionKeyType[0];
96+
} else {
97+
$genericTypes[] = $collectionKeyType->getTypeInfoType();
98+
}
9799
}
98100

99-
if (\is_array($collectionArgument)) {
100-
foreach ($collectionArgument as $type) {
101-
if (!$type instanceof self) {
102-
throw new \TypeError(sprintf('"%s()": Argument #%d (%s) must be of type "%s[]", "%s" or "null", array value "%s" given.', __METHOD__, $argumentIndex, $argumentName, self::class, self::class, get_debug_type($collectionArgument)));
103-
}
101+
if (null !== $collectionValueType && [] !== $collectionValueType) {
102+
if ([] === $genericTypes) {
103+
$genericTypes[] = TypeInfoType::int();
104104
}
105105

106-
return $collectionArgument;
106+
if (\is_array($collectionValueType)) {
107+
$collectionValueType = array_unique(array_map(fn ($t): TypeInfoType => $t->getTypeInfoType(), $collectionValueType));
108+
$genericTypes[] = \count($collectionValueType) > 1 ? TypeInfoType::union(...$collectionValueType) : $collectionValueType[0];
109+
} else {
110+
$genericTypes[] = $collectionValueType->getTypeInfoType();
111+
}
107112
}
108113

109-
return [$collectionArgument];
114+
$this->internalType = null !== $class ? new ObjectType($class) : new BuiltinType($builtinType);
115+
$this->internalType->isCollection = $collection;
116+
117+
if (\count($genericTypes)) {
118+
$this->internalType = TypeInfoType::generic($this->internalType, ...$genericTypes);
119+
$this->internalType->isCollection = $collection;
120+
}
121+
122+
if ($nullable) {
123+
$this->internalType = TypeInfoType::nullable($this->internalType);
124+
$this->internalType->isCollection = $collection;
125+
}
110126
}
111127

112128
/**
@@ -116,12 +132,15 @@ private function validateCollectionArgument(array|Type|null $collectionArgument,
116132
*/
117133
public function getBuiltinType(): string
118134
{
119-
return $this->builtinType;
135+
$internalType = $this->unwrapNullableType($this->internalType);
136+
$internalType = $this->unwrapGenericType($internalType);
137+
138+
return $internalType instanceof BuiltinType ? $internalType->getBuiltinType() : TypeInfoType::BUILTIN_TYPE_OBJECT;
120139
}
121140

122141
public function isNullable(): bool
123142
{
124-
return $this->nullable;
143+
return $this->internalType->isNullable();
125144
}
126145

127146
/**
@@ -131,12 +150,22 @@ public function isNullable(): bool
131150
*/
132151
public function getClassName(): ?string
133152
{
134-
return $this->class;
153+
$internalType = $this->unwrapNullableType($this->internalType);
154+
$internalType = $this->unwrapGenericType($internalType);
155+
156+
if (!$internalType instanceof ObjectType) {
157+
return null;
158+
}
159+
160+
return $internalType->getClassName();
135161
}
136162

137163
public function isCollection(): bool
138164
{
139-
return $this->collection;
165+
$internalType = $this->unwrapNullableType($this->internalType);
166+
$internalType = $this->unwrapGenericType($internalType);
167+
168+
return $internalType->isCollection;
140169
}
141170

142171
/**
@@ -148,7 +177,22 @@ public function isCollection(): bool
148177
*/
149178
public function getCollectionKeyTypes(): array
150179
{
151-
return $this->collectionKeyType;
180+
$internalType = $this->unwrapNullableType($this->internalType);
181+
182+
if (!$internalType instanceof GenericType) {
183+
return [];
184+
}
185+
186+
if (null === ($collectionKeyType = $internalType->getGenericTypes()[0] ?? null)) {
187+
return [];
188+
}
189+
190+
$collectionKeyType = $this->convertFromTypeInfoType($collectionKeyType);
191+
if (!\is_array($collectionKeyType)) {
192+
$collectionKeyType = [$collectionKeyType];
193+
}
194+
195+
return $collectionKeyType;
152196
}
153197

154198
/**
@@ -160,6 +204,108 @@ public function getCollectionKeyTypes(): array
160204
*/
161205
public function getCollectionValueTypes(): array
162206
{
163-
return $this->collectionValueType;
207+
$internalType = $this->unwrapNullableType($this->internalType);
208+
209+
if (!$internalType instanceof GenericType) {
210+
return [];
211+
}
212+
213+
if (null === ($collectionValueType = $internalType->getGenericTypes()[1] ?? null)) {
214+
return [];
215+
}
216+
217+
$collectionValueType = $this->convertFromTypeInfoType($collectionValueType);
218+
if (!\is_array($collectionValueType)) {
219+
$collectionValueType = [$collectionValueType];
220+
}
221+
222+
return $collectionValueType;
223+
}
224+
225+
private function getTypeInfoType(): TypeInfoType
226+
{
227+
return $this->internalType;
228+
}
229+
230+
private function convertFromTypeInfoType(TypeInfoType $typeInfoType): self|array
231+
{
232+
if ($typeInfoType instanceof UnionType) {
233+
return array_map($this->convertFromTypeInfoType(...), $typeInfoType->getTypes());
234+
}
235+
236+
if ($typeInfoType instanceof IntersectionType) {
237+
return array_map($this->convertFromTypeInfoType(...), $typeInfoType->getTypes());
238+
}
239+
240+
$builtinType = TypeInfoType::BUILTIN_TYPE_MIXED;
241+
$className = null;
242+
$collectionKeyType = $collectionValueType = null;
243+
244+
if ($typeInfoType instanceof ObjectType) {
245+
$builtinType = TypeInfoType::BUILTIN_TYPE_OBJECT;
246+
$className = $typeInfoType->getClassName();
247+
}
248+
249+
if ($typeInfoType instanceof GenericType) {
250+
/** @var BuiltinType $nestedType */
251+
$nestedType = $this->unwrapNullableType($typeInfoType->getType());
252+
$builtinType = $nestedType->getBuiltinType();
253+
254+
$genericTypes = $typeInfoType->getGenericTypes();
255+
$collectionKeyType = isset($genericTypes[0]) ? $this->convertFromTypeInfoType($genericTypes[0]) : null;
256+
$collectionValueType = isset($genericTypes[1]) ? $this->convertFromTypeInfoType($genericTypes[1]) : null;
257+
}
258+
259+
if ($typeInfoType instanceof BuiltinType) {
260+
$builtinType = $typeInfoType->getBuiltinType();
261+
}
262+
263+
return new self(
264+
builtinType: $builtinType,
265+
nullable: $typeInfoType->isNullable(),
266+
class: $className,
267+
collection: $typeInfoType->isCollection,
268+
collectionKeyType: $collectionKeyType,
269+
collectionValueType: $collectionValueType,
270+
);
271+
}
272+
273+
private function validateCollectionArgument(array|self|null $collectionArgument, int $argumentIndex, string $argumentName): ?array
274+
{
275+
if (null === $collectionArgument) {
276+
return null;
277+
}
278+
279+
if (\is_array($collectionArgument)) {
280+
foreach ($collectionArgument as $type) {
281+
if (!$type instanceof self) {
282+
throw new \TypeError(sprintf('"%s()": Argument #%d (%s) must be of type "%s[]", "%s" or "null", array value "%s" given.', __METHOD__, $argumentIndex, $argumentName, self::class, self::class, get_debug_type($collectionArgument)));
283+
}
284+
}
285+
286+
return $collectionArgument;
287+
}
288+
289+
return [$collectionArgument];
290+
}
291+
292+
private function unwrapNullableType(TypeInfoType $type): TypeInfoType
293+
{
294+
if (!$type instanceof UnionType) {
295+
return $type;
296+
}
297+
298+
$unionTypes = $type->getTypes();
299+
300+
return (string) TypeInfoType::null() === (string) $unionTypes[0] ? $unionTypes[1] : $unionTypes[0];
301+
}
302+
303+
private function unwrapGenericType(TypeInfoType $type): TypeInfoType
304+
{
305+
if (!$type instanceof GenericType) {
306+
return $type;
307+
}
308+
309+
return $type->getType();
164310
}
165311
}

src/Symfony/Component/PropertyInfo/composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@
2424
],
2525
"require": {
2626
"php": ">=8.2",
27-
"symfony/string": "^6.4|^7.0"
27+
"symfony/string": "^6.4|^7.0",
28+
"symfony/type-info": "^7.1"
2829
},
2930
"require-dev": {
3031
"symfony/serializer": "^6.4|^7.0",

src/Symfony/Component/TypeInfo/Type.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,13 @@ abstract class Type implements \Stringable
5454
self::BUILTIN_TYPE_MIXED,
5555
];
5656

57+
/**
58+
* BC Layer for Symfony\Component\PropertyInfo\Type.
59+
*
60+
* @internal
61+
*/
62+
public bool $isCollection = false;
63+
5764
public function isBuiltinType(string $builtinType): bool
5865
{
5966
if (!\in_array($builtinType, self::BUILTIN_TYPES)) {

0 commit comments

Comments
 (0)