1111
1212namespace 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 */
2132class 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}
0 commit comments