15
15
use Symfony \Component \PropertyInfo \PropertyListExtractorInterface ;
16
16
use Symfony \Component \PropertyInfo \PropertyTypeExtractorInterface ;
17
17
use Symfony \Component \PropertyInfo \Type as PropertyInfoType ;
18
+ use Symfony \Component \TypeInfo \Type as TypeInfoType ;
19
+ use Symfony \Component \TypeInfo \Type \CollectionType ;
20
+ use Symfony \Component \TypeInfo \Type \IntersectionType ;
21
+ use Symfony \Component \TypeInfo \Type \ObjectType ;
22
+ use Symfony \Component \TypeInfo \Type \UnionType ;
23
+ use Symfony \Component \TypeInfo \TypeIdentifier ;
18
24
use Symfony \Component \Validator \Constraints \All ;
19
25
use Symfony \Component \Validator \Constraints \NotBlank ;
20
26
use Symfony \Component \Validator \Constraints \NotNull ;
@@ -57,7 +63,7 @@ public function loadClassMetadata(ClassMetadata $metadata): bool
57
63
continue ;
58
64
}
59
65
60
- $ types = $ this ->typeExtractor -> getTypes ($ className , $ property );
66
+ $ types = $ this ->getPropertyTypes ($ className , $ property );
61
67
if (null === $ types ) {
62
68
continue ;
63
69
}
@@ -95,42 +101,92 @@ public function loadClassMetadata(ClassMetadata $metadata): bool
95
101
}
96
102
97
103
$ loaded = true ;
98
- $ builtinTypes = [];
99
- $ nullable = false ;
100
- $ scalar = true ;
101
- foreach ($ types as $ type ) {
102
- $ builtinTypes [] = $ type ->getBuiltinType ();
103
-
104
- if ($ scalar && !\in_array ($ type ->getBuiltinType (), [PropertyInfoType::BUILTIN_TYPE_INT , PropertyInfoType::BUILTIN_TYPE_FLOAT , PropertyInfoType::BUILTIN_TYPE_STRING , PropertyInfoType::BUILTIN_TYPE_BOOL ], true )) {
105
- $ scalar = false ;
104
+
105
+ // BC layer for PropertyTypeExtractorInterface::getTypes().
106
+ // Can be removed as soon as PropertyTypeExtractorInterface::getTypes() is removed (8.0).
107
+ if (\is_array ($ types )) {
108
+ $ builtinTypes = [];
109
+ $ nullable = false ;
110
+ $ scalar = true ;
111
+
112
+ foreach ($ types as $ type ) {
113
+ $ builtinTypes [] = $ type ->getBuiltinType ();
114
+
115
+ if ($ scalar && !\in_array ($ type ->getBuiltinType (), ['int ' , 'float ' , 'string ' , 'bool ' ], true )) {
116
+ $ scalar = false ;
117
+ }
118
+
119
+ if (!$ nullable && $ type ->isNullable ()) {
120
+ $ nullable = true ;
121
+ }
122
+ }
123
+
124
+ if (!$ hasTypeConstraint ) {
125
+ if (1 === \count ($ builtinTypes )) {
126
+ if ($ types [0 ]->isCollection () && \count ($ collectionValueType = $ types [0 ]->getCollectionValueTypes ()) > 0 ) {
127
+ [$ collectionValueType ] = $ collectionValueType ;
128
+ $ this ->handleAllConstraintLegacy ($ property , $ allConstraint , $ collectionValueType , $ metadata );
129
+ }
130
+
131
+ $ metadata ->addPropertyConstraint ($ property , $ this ->getTypeConstraintLegacy ($ builtinTypes [0 ], $ types [0 ]));
132
+ } elseif ($ scalar ) {
133
+ $ metadata ->addPropertyConstraint ($ property , new Type (['type ' => 'scalar ' ]));
134
+ }
135
+ }
136
+
137
+ if (!$ nullable && !$ hasNotBlankConstraint && !$ hasNotNullConstraint ) {
138
+ $ metadata ->addPropertyConstraint ($ property , new NotNull ());
106
139
}
140
+ } else {
141
+ if ($ hasTypeConstraint ) {
142
+ continue ;
143
+ }
144
+
145
+ $ type = $ types ;
146
+ $ nullable = false ;
107
147
108
- if (! $ nullable && $ type ->isNullable ()) {
148
+ if ($ type instanceof UnionType && $ type ->isNullable ()) {
109
149
$ nullable = true ;
150
+ $ type = $ type ->asNonNullable ();
110
151
}
111
- }
112
- if (!$ hasTypeConstraint ) {
113
- if (1 === \count ($ builtinTypes )) {
114
- if ($ types [0 ]->isCollection () && \count ($ collectionValueType = $ types [0 ]->getCollectionValueTypes ()) > 0 ) {
115
- [$ collectionValueType ] = $ collectionValueType ;
116
- $ this ->handleAllConstraint ($ property , $ allConstraint , $ collectionValueType , $ metadata );
117
- }
118
152
119
- $ metadata ->addPropertyConstraint ($ property , $ this ->getTypeConstraint ($ builtinTypes [0 ], $ types [0 ]));
120
- } elseif ($ scalar ) {
121
- $ metadata ->addPropertyConstraint ($ property , new Type (['type ' => 'scalar ' ]));
153
+ if ($ type instanceof CollectionType) {
154
+ $ this ->handleAllConstraint ($ property , $ allConstraint , $ type ->getCollectionValueType (), $ metadata );
155
+ }
156
+
157
+ if (null !== $ typeConstraint = $ this ->getTypeConstraint ($ type )) {
158
+ $ metadata ->addPropertyConstraint ($ property , $ typeConstraint );
122
159
}
123
- }
124
160
125
- if (!$ nullable && !$ hasNotBlankConstraint && !$ hasNotNullConstraint ) {
126
- $ metadata ->addPropertyConstraint ($ property , new NotNull ());
161
+ if (!$ nullable && !$ hasNotBlankConstraint && !$ hasNotNullConstraint ) {
162
+ $ metadata ->addPropertyConstraint ($ property , new NotNull ());
163
+ }
127
164
}
128
165
}
129
166
130
167
return $ loaded ;
131
168
}
132
169
133
- private function getTypeConstraint (string $ builtinType , PropertyInfoType $ type ): Type
170
+ /**
171
+ * BC layer for PropertyTypeExtractorInterface::getTypes().
172
+ * Can be removed as soon as PropertyTypeExtractorInterface::getTypes() is removed (8.0).
173
+ *
174
+ * @return TypeInfoType|list<PropertyInfoType>|null
175
+ */
176
+ private function getPropertyTypes (string $ className , string $ property ): TypeInfoType |array |null
177
+ {
178
+ if (method_exists ($ this ->typeExtractor , 'getType ' )) {
179
+ return $ this ->typeExtractor ->getType ($ className , $ property );
180
+ }
181
+
182
+ return $ this ->typeExtractor ->getTypes ($ className , $ property );
183
+ }
184
+
185
+ /**
186
+ * BC layer for PropertyTypeExtractorInterface::getTypes().
187
+ * Can be removed as soon as PropertyTypeExtractorInterface::getTypes() is removed (8.0).
188
+ */
189
+ private function getTypeConstraintLegacy (string $ builtinType , PropertyInfoType $ type ): Type
134
190
{
135
191
if (PropertyInfoType::BUILTIN_TYPE_OBJECT === $ builtinType && null !== $ className = $ type ->getClassName ()) {
136
192
return new Type (['type ' => $ className ]);
@@ -139,7 +195,64 @@ private function getTypeConstraint(string $builtinType, PropertyInfoType $type):
139
195
return new Type (['type ' => $ builtinType ]);
140
196
}
141
197
142
- private function handleAllConstraint (string $ property , ?All $ allConstraint , PropertyInfoType $ propertyInfoType , ClassMetadata $ metadata ): void
198
+ private function getTypeConstraint (TypeInfoType $ type ): ?Type
199
+ {
200
+ if ($ type instanceof UnionType || $ type instanceof IntersectionType) {
201
+ return ($ type ->isA (TypeIdentifier::INT ) || $ type ->isA (TypeIdentifier::FLOAT ) || $ type ->isA (TypeIdentifier::STRING ) || $ type ->isA (TypeIdentifier::BOOL )) ? new Type (['type ' => 'scalar ' ]) : null ;
202
+ }
203
+
204
+ $ baseType = $ type ->getBaseType ();
205
+
206
+ if ($ baseType instanceof ObjectType) {
207
+ return new Type (['type ' => $ baseType ->getClassName ()]);
208
+ }
209
+
210
+ if (TypeIdentifier::MIXED !== $ baseType ->getTypeIdentifier ()) {
211
+ return new Type (['type ' => $ baseType ->getTypeIdentifier ()->value ]);
212
+ }
213
+
214
+ return null ;
215
+ }
216
+
217
+ private function handleAllConstraint (string $ property , ?All $ allConstraint , TypeInfoType $ type , ClassMetadata $ metadata ): void
218
+ {
219
+ $ containsTypeConstraint = false ;
220
+ $ containsNotNullConstraint = false ;
221
+ if (null !== $ allConstraint ) {
222
+ foreach ($ allConstraint ->constraints as $ constraint ) {
223
+ if ($ constraint instanceof Type) {
224
+ $ containsTypeConstraint = true ;
225
+ } elseif ($ constraint instanceof NotNull) {
226
+ $ containsNotNullConstraint = true ;
227
+ }
228
+ }
229
+ }
230
+
231
+ $ constraints = [];
232
+ if (!$ containsNotNullConstraint && !$ type ->isNullable ()) {
233
+ $ constraints [] = new NotNull ();
234
+ }
235
+
236
+ if (!$ containsTypeConstraint && null !== $ typeConstraint = $ this ->getTypeConstraint ($ type )) {
237
+ $ constraints [] = $ typeConstraint ;
238
+ }
239
+
240
+ if (!$ constraints ) {
241
+ return ;
242
+ }
243
+
244
+ if (null === $ allConstraint ) {
245
+ $ metadata ->addPropertyConstraint ($ property , new All (['constraints ' => $ constraints ]));
246
+ } else {
247
+ $ allConstraint ->constraints = array_merge ($ allConstraint ->constraints , $ constraints );
248
+ }
249
+ }
250
+
251
+ /**
252
+ * BC layer for PropertyTypeExtractorInterface::getTypes().
253
+ * Can be removed as soon as PropertyTypeExtractorInterface::getTypes() is removed (8.0).
254
+ */
255
+ private function handleAllConstraintLegacy (string $ property , ?All $ allConstraint , PropertyInfoType $ propertyInfoType , ClassMetadata $ metadata ): void
143
256
{
144
257
$ containsTypeConstraint = false ;
145
258
$ containsNotNullConstraint = false ;
@@ -159,7 +272,7 @@ private function handleAllConstraint(string $property, ?All $allConstraint, Prop
159
272
}
160
273
161
274
if (!$ containsTypeConstraint ) {
162
- $ constraints [] = $ this ->getTypeConstraint ($ propertyInfoType ->getBuiltinType (), $ propertyInfoType );
275
+ $ constraints [] = $ this ->getTypeConstraintLegacy ($ propertyInfoType ->getBuiltinType (), $ propertyInfoType );
163
276
}
164
277
165
278
if (null === $ allConstraint ) {
0 commit comments