@@ -184,6 +184,12 @@ private function applyNullability(array $schema, bool $isNullable): array
184
184
$ currentType = $ schema ['type ' ];
185
185
$ schema ['type ' ] = \is_array ($ currentType ) ? array_merge ($ currentType , ['null ' ]) : [$ currentType , 'null ' ];
186
186
187
+ if (isset ($ schema ['enum ' ])) {
188
+ $ schema ['enum ' ][] = null ;
189
+
190
+ return $ schema ;
191
+ }
192
+
187
193
return $ schema ;
188
194
}
189
195
@@ -199,18 +205,23 @@ private function getJsonSchemaFromType(Type $type, ?bool $readableLink = null):
199
205
{
200
206
$ isNullable = $ type ->isNullable ();
201
207
202
- while ($ type instanceof WrappingTypeInterface) {
203
- $ type = $ type ->getWrappedType ();
204
- }
205
-
206
208
if ($ type instanceof UnionType) {
207
209
$ subTypes = array_filter ($ type ->getTypes (), fn ($ t ) => !($ t instanceof BuiltinType && $ t ->isIdentifiedBy (TypeIdentifier::NULL )));
210
+
211
+ foreach ($ subTypes as $ t ) {
212
+ $ s = $ this ->getJsonSchemaFromType ($ t , $ readableLink );
213
+ // We can not find what type this is, let it be computed at runtime by the SchemaFactory
214
+ if (($ s ['type ' ] ?? null ) === Schema::UNKNOWN_TYPE ) {
215
+ return $ s ;
216
+ }
217
+ }
218
+
208
219
$ schemas = array_map (fn ($ t ) => $ this ->getJsonSchemaFromType ($ t , $ readableLink ), $ subTypes );
209
220
210
221
if (0 === \count ($ schemas )) {
211
222
$ schema = [];
212
223
} elseif (1 === \count ($ schemas )) {
213
- $ schema = $ schemas[ 0 ] ;
224
+ $ schema = current ( $ schemas) ;
214
225
} else {
215
226
$ schema = ['anyOf ' => $ schemas ];
216
227
}
@@ -235,20 +246,20 @@ private function getJsonSchemaFromType(Type $type, ?bool $readableLink = null):
235
246
}
236
247
237
248
if ($ type instanceof CollectionType) {
238
- $ keyType = $ type ->getCollectionKeyType ();
239
249
$ valueType = $ type ->getCollectionValueType ();
240
- $ schema = [];
250
+ $ valueSchema = $ this ->getJsonSchemaFromType ($ valueType , $ readableLink );
251
+ $ keyType = $ type ->getCollectionKeyType ();
241
252
242
253
// Associative array (string keys)
243
- if ($ keyType ->isSatisfiedBy (fn (Type $ t ) => $ t instanceof BuiltinType && $ t ->isIdentifiedBy (TypeIdentifier::STRING ))) {
254
+ if ($ keyType ->isSatisfiedBy (fn (Type $ t ) => $ t instanceof BuiltinType && $ t ->isIdentifiedBy (TypeIdentifier::INT ))) {
244
255
$ schema = [
245
- 'type ' => 'object ' ,
246
- 'additionalProperties ' => $ this -> getJsonSchemaFromType ( $ valueType , $ readableLink ) ,
256
+ 'type ' => 'array ' ,
257
+ 'items ' => $ valueSchema ,
247
258
];
248
259
} else { // List (int keys)
249
260
$ schema = [
250
- 'type ' => 'array ' ,
251
- 'items ' => $ this -> getJsonSchemaFromType ( $ valueType , $ readableLink ) ,
261
+ 'type ' => 'object ' ,
262
+ 'additionalProperties ' => $ valueSchema ,
252
263
];
253
264
}
254
265
@@ -262,10 +273,6 @@ private function getJsonSchemaFromType(Type $type, ?bool $readableLink = null):
262
273
}
263
274
264
275
if ($ type instanceof BuiltinType) {
265
- if ($ type ->isIdentifiedBy (TypeIdentifier::NULL )) {
266
- return ['type ' => 'null ' ];
267
- }
268
-
269
276
$ schema = match ($ type ->getTypeIdentifier ()) {
270
277
TypeIdentifier::INT => ['type ' => 'integer ' ],
271
278
TypeIdentifier::FLOAT => ['type ' => 'number ' ],
@@ -284,7 +291,7 @@ private function getJsonSchemaFromType(Type $type, ?bool $readableLink = null):
284
291
return $ this ->applyNullability ($ schema , $ isNullable );
285
292
}
286
293
287
- return $ this -> applyNullability ( ['type ' => Schema::UNKNOWN_TYPE ], $ isNullable ) ;
294
+ return ['type ' => Schema::UNKNOWN_TYPE ];
288
295
}
289
296
290
297
/**
@@ -316,34 +323,27 @@ private function getClassSchemaDefinition(?string $className, ?bool $readableLin
316
323
return ['type ' => 'string ' , 'format ' => 'binary ' ];
317
324
}
318
325
319
- if (is_a ($ className , \BackedEnum::class, true )) {
326
+ $ isResourceClass = $ this ->isResourceClass ($ className );
327
+ if (!$ isResourceClass && is_a ($ className , \BackedEnum::class, true )) {
320
328
$ enumCases = array_map (static fn (\BackedEnum $ enum ): string |int => $ enum ->value , $ className ::cases ());
321
329
$ type = \is_string ($ enumCases [0 ] ?? '' ) ? 'string ' : 'integer ' ;
322
330
323
331
return ['type ' => $ type , 'enum ' => $ enumCases ];
324
332
}
325
333
326
- $ isResource = $ this ->isResourceClass ($ className );
327
-
328
334
// If it's a resource and links are not readable, represent as IRI string.
329
- if ($ isResource && true !== $ readableLink ) {
335
+ if ($ isResourceClass && true !== $ readableLink ) {
330
336
return [
331
337
'type ' => 'string ' ,
332
338
'format ' => 'iri-reference ' ,
333
339
'example ' => 'https://example.com/ ' , // Add a generic example
334
340
];
335
341
}
336
342
337
- // If it's a known resource represent it as UNKNOWN_TYPE this gets resolved at runtime by the SchemaFactory
338
- if ($ isResource ) {
339
- return ['type ' => Schema::UNKNOWN_TYPE ];
340
- }
341
-
342
- // For non-resource objects that aren't handled specifically, default to object.
343
- return ['type ' => 'object ' ];
343
+ return ['type ' => Schema::UNKNOWN_TYPE ];
344
344
}
345
345
346
- private function getLegacyTypeSchema (ApiProperty $ propertyMetadata , array $ propertySchema , string $ resourceClass , string $ property , bool $ link ): array
346
+ private function getLegacyTypeSchema (ApiProperty $ propertyMetadata , array $ propertySchema , string $ resourceClass , string $ property , ? bool $ link ): array
347
347
{
348
348
$ types = $ propertyMetadata ->getBuiltinTypes () ?? [];
349
349
0 commit comments