• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

wol-soft / php-json-schema-model-generator / 23929639588

03 Apr 2026 01:24AM UTC coverage: 98.248% (-0.4%) from 98.654%
23929639588

Pull #125

github

Enno Woortmann
docs
Pull Request #125: attributes

1496 of 1526 new or added lines in 66 files covered. (98.03%)

4 existing lines in 3 files now uncovered.

4374 of 4452 relevant lines covered (98.25%)

620.23 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

99.33
/src/PropertyProcessor/PropertyFactory.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace PHPModelGenerator\PropertyProcessor;
6

7
use PHPModelGenerator\Attributes\Deprecated;
8
use PHPModelGenerator\Attributes\JsonPointer;
9
use PHPModelGenerator\Attributes\JsonSchema as JsonSchemaAttribute;
10
use PHPModelGenerator\Attributes\ReadOnlyProperty;
11
use PHPModelGenerator\Attributes\Required;
12
use PHPModelGenerator\Attributes\SchemaName;
13
use PHPModelGenerator\Attributes\WriteOnlyProperty;
14
use Exception;
15
use PHPModelGenerator\Draft\Draft;
16
use PHPModelGenerator\Draft\DraftFactoryInterface;
17
use PHPModelGenerator\Draft\Modifier\ObjectType\ObjectModifier;
18
use PHPModelGenerator\Draft\Modifier\TypeCheckModifier;
19
use PHPModelGenerator\Exception\SchemaException;
20
use PHPModelGenerator\Model\Attributes\PhpAttribute;
21
use PHPModelGenerator\Model\Property\BaseProperty;
22
use PHPModelGenerator\Model\Property\Property;
23
use PHPModelGenerator\Model\Property\PropertyInterface;
24
use PHPModelGenerator\Model\Property\PropertyType;
25
use PHPModelGenerator\Model\Schema;
26
use PHPModelGenerator\Model\SchemaDefinition\JsonSchema;
27
use PHPModelGenerator\Model\Validator\MultiTypeCheckValidator;
28
use PHPModelGenerator\Model\Validator\RequiredPropertyValidator;
29
use PHPModelGenerator\Model\Validator\TypeCheckInterface;
30
use PHPModelGenerator\PropertyProcessor\Decorator\Property\PropertyTransferDecorator;
31
use PHPModelGenerator\PropertyProcessor\Decorator\SchemaNamespaceTransferDecorator;
32
use PHPModelGenerator\PropertyProcessor\Decorator\TypeHint\TypeHintDecorator;
33
use PHPModelGenerator\SchemaProcessor\SchemaProcessor;
34
use PHPModelGenerator\Utils\TypeConverter;
35

36
/**
37
 * Class PropertyFactory
38
 *
39
 * @package PHPModelGenerator\PropertyProcessor
40
 */
41
class PropertyFactory
42
{
43
    /** @var Draft[] Keyed by draft class name */
44
    private array $draftCache = [];
45

46
    /**
47
     * Create a property, applying all applicable Draft modifiers.
48
     *
49
     * @throws SchemaException
50
     */
51
    public function create(
2,182✔
52
        SchemaProcessor $schemaProcessor,
53
        Schema $schema,
54
        string $propertyName,
55
        JsonSchema $propertySchema,
56
        bool $required = false,
57
    ): PropertyInterface {
58
        $json = $propertySchema->getJson();
2,182✔
59

60
        // $ref: replace the property entirely via the definition dictionary.
61
        // This is a schema-identity primitive — it cannot be a Draft modifier because
62
        // ModifierInterface::modify returns void and cannot replace the property object.
63
        if (isset($json['$ref'])) {
2,182✔
64
            if (isset($json['type']) && $json['type'] === 'base') {
545✔
65
                return $this->processBaseReference(
41✔
66
                    $schemaProcessor,
41✔
67
                    $schema,
41✔
68
                    $propertyName,
41✔
69
                    $propertySchema,
41✔
70
                    $required,
41✔
71
                );
41✔
72
            }
73

74
            return $this->processReference($schemaProcessor, $schema, $propertyName, $propertySchema, $required);
507✔
75
        }
76

77
        $resolvedType = $json['type'] ?? 'any';
2,182✔
78

79
        if (is_array($resolvedType)) {
2,182✔
80
            return $this->createMultiTypeProperty(
72✔
81
                $schemaProcessor,
72✔
82
                $schema,
72✔
83
                $propertyName,
72✔
84
                $propertySchema,
72✔
85
                $resolvedType,
72✔
86
                $required,
72✔
87
            );
72✔
88
        }
89

90
        $this->checkType($resolvedType, $schema);
2,182✔
91

92
        return match ($resolvedType) {
2,182✔
93
            'object' => $this->createObjectProperty(
953✔
94
                $schemaProcessor,
953✔
95
                $schema,
953✔
96
                $propertyName,
953✔
97
                $propertySchema,
953✔
98
                $required,
953✔
99
            ),
953✔
100
            'base'   => $this->createBaseProperty($schemaProcessor, $schema, $propertyName, $propertySchema),
2,181✔
101
            default  => $this->createTypedProperty(
2,167✔
102
                $schemaProcessor,
2,167✔
103
                $schema,
2,167✔
104
                $propertyName,
2,167✔
105
                $propertySchema,
2,167✔
106
                $resolvedType,
2,167✔
107
                $required,
2,167✔
108
            ),
2,167✔
109
        };
2,182✔
110
    }
111

112
    /**
113
     * Handle a nested object property: generate the nested class, wire the outer property,
114
     * then apply universal modifiers (filter, enum, default, const) on the outer property.
115
     *
116
     * @throws SchemaException
117
     */
118
    private function createObjectProperty(
960✔
119
        SchemaProcessor $schemaProcessor,
120
        Schema $schema,
121
        string $propertyName,
122
        JsonSchema $propertySchema,
123
        bool $required,
124
    ): PropertyInterface {
125
        $json     = $propertySchema->getJson();
960✔
126
        $property = $this->buildProperty($schemaProcessor, $propertyName, null, $propertySchema, $required);
960✔
127

128
        $className = $schemaProcessor->getGeneratorConfiguration()->getClassNameGenerator()->getClassName(
960✔
129
            $propertyName,
960✔
130
            $propertySchema,
960✔
131
            false,
960✔
132
            $schemaProcessor->getCurrentClassName(),
960✔
133
        );
960✔
134

135
        // Strip property-level keywords before passing the schema to processSchema: these keywords
136
        // target the outer property and are handled by the universal modifiers below.
137
        $nestedJson = $json;
960✔
138
        unset($nestedJson['filter'], $nestedJson['enum'], $nestedJson['default']);
960✔
139

140
        $nestedSchema = $schemaProcessor->processSchema(
960✔
141
            $propertySchema->withJson($nestedJson),
960✔
142
            $schemaProcessor->getCurrentClassPath(),
960✔
143
            $className,
960✔
144
            $schema->getSchemaDictionary(),
960✔
145
        );
960✔
146

147
        if ($nestedSchema !== null) {
960✔
148
            $property->setNestedSchema($nestedSchema);
960✔
149
            $this->wireObjectProperty($schemaProcessor, $schema, $property, $propertySchema);
960✔
150
        }
151

152
        // Universal modifiers (filter, enum, default, const) run on the outer property.
153
        $this->applyModifiers($schemaProcessor, $schema, $property, $propertySchema, anyOnly: true);
960✔
154

155
        return $property;
958✔
156
    }
157

158
    /**
159
     * Handle a root-level schema (type=base): set up definitions, run all Draft modifiers,
160
     * then transfer any composed properties to the schema.
161
     *
162
     * @throws SchemaException
163
     */
164
    private function createBaseProperty(
2,181✔
165
        SchemaProcessor $schemaProcessor,
166
        Schema $schema,
167
        string $propertyName,
168
        JsonSchema $propertySchema,
169
    ): PropertyInterface {
170
        $schema->getSchemaDictionary()->setUpDefinitionDictionary($schemaProcessor, $schema);
2,181✔
171
        $property = new BaseProperty($propertyName, new PropertyType('object'), $propertySchema);
2,181✔
172

173
        $objectJson         = $propertySchema->getJson();
2,181✔
174
        $objectJson['type'] = 'object';
2,181✔
175
        $this->applyModifiers($schemaProcessor, $schema, $property, $propertySchema->withJson($objectJson));
2,181✔
176

177
        $schemaProcessor->transferComposedPropertiesToSchema($property, $schema);
2,122✔
178

179
        return $property;
2,121✔
180
    }
181

182
    /**
183
     * Handle scalar, array, and untyped properties: construct directly and run all Draft modifiers.
184
     *
185
     * @throws SchemaException
186
     */
187
    private function createTypedProperty(
2,112✔
188
        SchemaProcessor $schemaProcessor,
189
        Schema $schema,
190
        string $propertyName,
191
        JsonSchema $propertySchema,
192
        string $type,
193
        bool $required,
194
    ): PropertyInterface {
195
        $phpType  = $type !== 'any' ? TypeConverter::jsonSchemaToPhp($type) : null;
2,112✔
196
        $property = $this->buildProperty(
2,112✔
197
            $schemaProcessor,
2,112✔
198
            $propertyName,
2,112✔
199
            $phpType !== null ? new PropertyType($phpType) : null,
2,112✔
200
            $propertySchema,
2,112✔
201
            $required,
2,112✔
202
        );
2,112✔
203

204
        $this->applyModifiers($schemaProcessor, $schema, $property, $propertySchema);
2,111✔
205

206
        return $property;
2,069✔
207
    }
208

209
    /**
210
     * Construct a Property with the common required/readOnly setup.
211
     */
212
    private function buildProperty(
2,155✔
213
        SchemaProcessor $schemaProcessor,
214
        string $propertyName,
215
        ?PropertyType $type,
216
        JsonSchema $propertySchema,
217
        bool $required,
218
    ): Property {
219
        $json = $propertySchema->getJson();
2,155✔
220

221
        $property = (new Property($propertyName, $type, $propertySchema, $json['description'] ?? ''))
2,155✔
222
            ->setRequired($required)
2,155✔
223
            ->setReadOnly(
2,155✔
224
                (isset($json['readOnly']) && $json['readOnly'] === true) ||
2,155✔
225
                $schemaProcessor->getGeneratorConfiguration()->isImmutable(),
2,155✔
226
            );
2,155✔
227

228
        if ($required && !str_starts_with($propertyName, 'item of array ')) {
2,154✔
229
            $property->addValidator(new RequiredPropertyValidator($property), 1);
1,070✔
230
        }
231

232
        $configuration = $schemaProcessor->getGeneratorConfiguration();
2,154✔
233

234
        $property
2,154✔
235
            ->addAttribute(
2,154✔
236
                new PhpAttribute(JsonPointer::class, [$propertySchema->getPointer()]),
2,154✔
237
                $configuration,
2,154✔
238
                PhpAttribute::JSON_POINTER,
2,154✔
239
            )
2,154✔
240
            ->addAttribute(
2,154✔
241
                new PhpAttribute(SchemaName::class, [$propertyName]),
2,154✔
242
                $configuration,
2,154✔
243
                PhpAttribute::SCHEMA_NAME,
2,154✔
244
            )
2,154✔
245
            ->addAttribute(
2,154✔
246
                new PhpAttribute(
2,154✔
247
                    JsonSchemaAttribute::class,
2,154✔
248
                    [empty($propertySchema->getJson()) ? '{}' : json_encode($propertySchema->getJson())],
2,154✔
249
                ),
2,154✔
250
                $configuration,
2,154✔
251
                PhpAttribute::JSON_SCHEMA,
2,154✔
252
            );
2,154✔
253

254
        if ($required) {
2,154✔
255
            $property->addAttribute(new PhpAttribute(Required::class), $configuration, PhpAttribute::REQUIRED);
1,080✔
256
        }
257

258
        if (isset($json['readOnly']) && $json['readOnly'] === true) {
2,154✔
259
            $property->addAttribute(
3✔
260
                new PhpAttribute(ReadOnlyProperty::class),
3✔
261
                $configuration,
3✔
262
                PhpAttribute::READ_WRITE_ONLY,
3✔
263
            );
3✔
264
        }
265

266
        if (isset($json['writeOnly']) && $json['writeOnly'] === true) {
2,154✔
267
            $property->addAttribute(
2✔
268
                new PhpAttribute(WriteOnlyProperty::class),
2✔
269
                $configuration,
2✔
270
                PhpAttribute::READ_WRITE_ONLY,
2✔
271
            );
2✔
272
        }
273

274
        if (isset($json['deprecated']) && $json['deprecated'] === true) {
2,154✔
275
            $property->addAttribute(new PhpAttribute(Deprecated::class), $configuration, PhpAttribute::DEPRECATED);
2✔
276
        }
277

278
        return $property;
2,154✔
279
    }
280

281
    /**
282
     * Resolve a $ref reference by looking it up in the definition dictionary.
283
     *
284
     * @throws SchemaException
285
     */
286
    private function processReference(
545✔
287
        SchemaProcessor $schemaProcessor,
288
        Schema $schema,
289
        string $propertyName,
290
        JsonSchema $propertySchema,
291
        bool $required,
292
    ): PropertyInterface {
293
        $path       = [];
545✔
294
        $reference  = $propertySchema->getJson()['$ref'];
545✔
295
        $dictionary = $schema->getSchemaDictionary();
545✔
296

297
        try {
298
            $definition = $dictionary->getDefinition($reference, $schemaProcessor, $path);
545✔
299

300
            if ($definition) {
538✔
301
                $definitionSchema = $definition->getSchema();
538✔
302

303
                if (
304
                    $schema->getClassPath() !== $definitionSchema->getClassPath() ||
538✔
305
                    $schema->getClassName() !== $definitionSchema->getClassName() ||
537✔
306
                    (
307
                        $schema->getClassName() === 'ExternalSchema' &&
538✔
308
                        $definitionSchema->getClassName() === 'ExternalSchema'
538✔
309
                    )
310
                ) {
311
                    $schema->addNamespaceTransferDecorator(
259✔
312
                        new SchemaNamespaceTransferDecorator($definitionSchema),
259✔
313
                    );
259✔
314

315
                    if ($definitionSchema->getClassName() !== 'ExternalSchema') {
259✔
316
                        $schema->addUsedClass(join('\\', array_filter([
72✔
317
                            $schemaProcessor->getGeneratorConfiguration()->getNamespacePrefix(),
72✔
318
                            $definitionSchema->getClassPath(),
72✔
319
                            $definitionSchema->getClassName(),
72✔
320
                        ])));
72✔
321
                    }
322
                }
323

324
                return $definition->resolveReference(
538✔
325
                    $propertyName,
538✔
326
                    implode('/', $path),
538✔
327
                    $required,
538✔
328
                    $propertySchema->getJson()['_dependencies'] ?? null,
538✔
329
                );
538✔
330
            }
331
        } catch (Exception $exception) {
7✔
332
            throw new SchemaException(
7✔
333
                "Unresolved Reference $reference in file {$propertySchema->getFile()}",
7✔
334
                0,
7✔
335
                $exception,
7✔
336
            );
7✔
337
        }
338

NEW
339
        throw new SchemaException("Unresolved Reference $reference in file {$propertySchema->getFile()}");
×
340
    }
341

342
    /**
343
     * Resolve a $ref on a base-level schema: set up definitions, delegate to processReference,
344
     * then copy the referenced schema's properties to the parent schema.
345
     *
346
     * @throws SchemaException
347
     */
348
    private function processBaseReference(
41✔
349
        SchemaProcessor $schemaProcessor,
350
        Schema $schema,
351
        string $propertyName,
352
        JsonSchema $propertySchema,
353
        bool $required,
354
    ): PropertyInterface {
355
        $schema->getSchemaDictionary()->setUpDefinitionDictionary($schemaProcessor, $schema);
41✔
356

357
        $property = $this->processReference($schemaProcessor, $schema, $propertyName, $propertySchema, $required);
41✔
358

359
        if (!$property->getNestedSchema()) {
41✔
360
            throw new SchemaException(
1✔
361
                sprintf(
1✔
362
                    'A referenced schema on base level must provide an object definition for property %s in file %s',
1✔
363
                    $propertyName,
1✔
364
                    $propertySchema->getFile(),
1✔
365
                )
1✔
366
            );
1✔
367
        }
368

369
        foreach ($property->getNestedSchema()->getProperties() as $propertiesOfReferencedObject) {
40✔
370
            $schema->addProperty($propertiesOfReferencedObject);
40✔
371
        }
372

373
        return $property;
40✔
374
    }
375

376
    /**
377
     * Handle "type": [...] properties by processing each type through its Draft modifiers,
378
     * merging validators and decorators onto a single property, then consolidating type checks.
379
     *
380
     * @param string[] $types
381
     *
382
     * @throws SchemaException
383
     */
384
    private function createMultiTypeProperty(
72✔
385
        SchemaProcessor $schemaProcessor,
386
        Schema $schema,
387
        string $propertyName,
388
        JsonSchema $propertySchema,
389
        array $types,
390
        bool $required,
391
    ): PropertyInterface {
392
        $json     = $propertySchema->getJson();
72✔
393
        $property = $this->buildProperty($schemaProcessor, $propertyName, null, $propertySchema, $required);
72✔
394

395
        $collectedTypes   = [];
72✔
396
        $typeHints        = [];
72✔
397
        $resolvedSubCount = 0;
72✔
398
        $totalSubCount    = count($types);
72✔
399

400
        // Strip the default from sub-schemas so that default handling runs only once via the
401
        // universal DefaultValueModifier below, which already handles the multi-type case.
402
        $subJson = $json;
72✔
403
        unset($subJson['default']);
72✔
404

405
        foreach ($types as $type) {
72✔
406
            $this->checkType($type, $schema);
72✔
407

408
            $subJson['type'] = $type;
72✔
409
            $subSchema       = $propertySchema->withJson($subJson);
72✔
410

411
            // For type=object, delegate to the same object path (processSchema + wireObjectProperty).
412
            $subProperty = $type === 'object'
72✔
413
                ? $this->createObjectProperty($schemaProcessor, $schema, $propertyName, $subSchema, $required)
7✔
414
                : $this->createSubTypeProperty(
72✔
415
                    $schemaProcessor,
72✔
416
                    $schema,
72✔
417
                    $propertyName,
72✔
418
                    $subSchema,
72✔
419
                    $type,
72✔
420
                    $required,
72✔
421
                );
72✔
422

423
            $subProperty->onResolve(function () use (
72✔
424
                $property,
72✔
425
                $subProperty,
72✔
426
                $schemaProcessor,
72✔
427
                $schema,
72✔
428
                $propertySchema,
72✔
429
                $totalSubCount,
72✔
430
                &$collectedTypes,
72✔
431
                &$typeHints,
72✔
432
                &$resolvedSubCount,
72✔
433
            ): void {
72✔
434
                foreach ($subProperty->getValidators() as $validatorContainer) {
72✔
435
                    $validator = $validatorContainer->getValidator();
72✔
436

437
                    if ($validator instanceof TypeCheckInterface) {
72✔
438
                        array_push($collectedTypes, ...$validator->getTypes());
72✔
439
                        continue;
72✔
440
                    }
441

442
                    $property->addValidator($validator, $validatorContainer->getPriority());
43✔
443
                }
444

445
                if ($subProperty->getDecorators()) {
72✔
446
                    $property->addDecorator(new PropertyTransferDecorator($subProperty));
33✔
447
                }
448

449
                $typeHints[] = $subProperty->getTypeHint();
72✔
450

451
                if (++$resolvedSubCount < $totalSubCount || empty($collectedTypes)) {
72✔
452
                    return;
72✔
453
                }
454

455
                $this->finalizeMultiTypeProperty(
72✔
456
                    $property,
72✔
457
                    array_unique($collectedTypes),
72✔
458
                    $typeHints,
72✔
459
                    $schemaProcessor,
72✔
460
                    $schema,
72✔
461
                    $propertySchema,
72✔
462
                );
72✔
463
            });
72✔
464
        }
465

466
        return $property;
68✔
467
    }
468

469
    /**
470
     * Build a non-object sub-property for a multi-type array, applying only type-specific
471
     * modifiers (no universal 'any' modifiers — those run once on the parent after finalization).
472
     *
473
     * @throws SchemaException
474
     */
475
    private function createSubTypeProperty(
72✔
476
        SchemaProcessor $schemaProcessor,
477
        Schema $schema,
478
        string $propertyName,
479
        JsonSchema $propertySchema,
480
        string $type,
481
        bool $required,
482
    ): Property {
483
        $subProperty = $this->buildProperty(
72✔
484
            $schemaProcessor,
72✔
485
            $propertyName,
72✔
486
            new PropertyType(TypeConverter::jsonSchemaToPhp($type)),
72✔
487
            $propertySchema,
72✔
488
            $required,
72✔
489
        );
72✔
490

491
        $this->applyModifiers($schemaProcessor, $schema, $subProperty, $propertySchema, anyOnly: false, typeOnly: true);
72✔
492

493
        return $subProperty;
72✔
494
    }
495

496
    /**
497
     * Called once all sub-properties of a multi-type property have resolved.
498
     * Adds the consolidated MultiTypeCheckValidator, sets the union PropertyType,
499
     * attaches the type-hint decorator, and runs universal modifiers.
500
     *
501
     * @param string[] $collectedTypes
502
     * @param string[] $typeHints
503
     *
504
     * @throws SchemaException
505
     */
506
    private function finalizeMultiTypeProperty(
72✔
507
        PropertyInterface $property,
508
        array $collectedTypes,
509
        array $typeHints,
510
        SchemaProcessor $schemaProcessor,
511
        Schema $schema,
512
        JsonSchema $propertySchema,
513
    ): void {
514
        $hasNull      = in_array('null', $collectedTypes, true);
72✔
515
        $nonNullTypes = array_values(array_filter(
72✔
516
            $collectedTypes,
72✔
517
            static fn(string $type): bool => $type !== 'null',
72✔
518
        ));
72✔
519

520
        $allowImplicitNull = $schemaProcessor->getGeneratorConfiguration()->isImplicitNullAllowed()
72✔
521
            && !$property->isRequired();
72✔
522

523
        $property->addValidator(
72✔
524
            new MultiTypeCheckValidator($collectedTypes, $property, $allowImplicitNull),
72✔
525
            2,
72✔
526
        );
72✔
527

528
        if ($nonNullTypes) {
72✔
529
            $property->setType(
72✔
530
                new PropertyType($nonNullTypes, $hasNull ? true : null),
72✔
531
                new PropertyType($nonNullTypes, $hasNull ? true : null),
72✔
532
            );
72✔
533
        }
534

535
        $property->addTypeHintDecorator(new TypeHintDecorator($typeHints));
72✔
536

537
        $this->applyModifiers($schemaProcessor, $schema, $property, $propertySchema, true);
72✔
538
    }
539

540
    /**
541
     * Wire the outer property for a nested object: add the type-check validator and instantiation
542
     * linkage. Schema-targeting modifiers are intentionally NOT run here because processSchema
543
     * already applied them to the nested schema.
544
     *
545
     * @throws SchemaException
546
     */
547
    private function wireObjectProperty(
960✔
548
        SchemaProcessor $schemaProcessor,
549
        Schema $schema,
550
        PropertyInterface $property,
551
        JsonSchema $propertySchema,
552
    ): void {
553
        (new TypeCheckModifier(TypeConverter::jsonSchemaToPhp('object')))->modify(
960✔
554
            $schemaProcessor,
960✔
555
            $schema,
960✔
556
            $property,
960✔
557
            $propertySchema,
960✔
558
        );
960✔
559

560
        (new ObjectModifier())->modify($schemaProcessor, $schema, $property, $propertySchema);
960✔
561
    }
562

563
    /**
564
     * Run Draft modifiers for the given property.
565
     *
566
     * By default all covered types (type-specific + 'any') run. Pass $anyOnly=true to run
567
     * only the 'any' entry (used for object outer-property universal keywords), or $typeOnly=true
568
     * to run only type-specific entries (used for multi-type sub-properties).
569
     *
570
     * @throws SchemaException
571
     */
572
    private function applyModifiers(
2,182✔
573
        SchemaProcessor $schemaProcessor,
574
        Schema $schema,
575
        PropertyInterface $property,
576
        JsonSchema $propertySchema,
577
        bool $anyOnly = false,
578
        bool $typeOnly = false,
579
    ): void {
580
        $type       = $propertySchema->getJson()['type'] ?? 'any';
2,182✔
581
        $builtDraft = $this->resolveBuiltDraft($schemaProcessor, $propertySchema);
2,182✔
582

583
        // For untyped properties ('any'), only run the 'any' entry — getCoveredTypes('any')
584
        // returns all types, which would incorrectly apply type-specific modifiers.
585
        $coveredTypes = $type === 'any'
2,182✔
586
            ? array_filter($builtDraft->getCoveredTypes('any'), static fn($t) => $t->getType() === 'any')
557✔
587
            : $builtDraft->getCoveredTypes($type);
2,182✔
588

589
        foreach ($coveredTypes as $coveredType) {
2,182✔
590
            $isAnyEntry = $coveredType->getType() === 'any';
2,182✔
591

592
            if ($anyOnly && !$isAnyEntry) {
2,182✔
593
                continue;
1,018✔
594
            }
595

596
            if ($typeOnly && $isAnyEntry) {
2,182✔
597
                continue;
72✔
598
            }
599

600
            foreach ($coveredType->getModifiers() as $modifier) {
2,182✔
601
                $modifier->modify($schemaProcessor, $schema, $property, $propertySchema);
2,182✔
602
            }
603
        }
604
    }
605

606
    /**
607
     * @throws SchemaException
608
     */
609
    private function checkType(mixed $type, Schema $schema): void
2,182✔
610
    {
611
        if (is_string($type)) {
2,182✔
612
            return;
2,182✔
613
        }
614

615
        throw new SchemaException(
1✔
616
            sprintf(
1✔
617
                'Invalid property type %s in file %s',
1✔
618
                $type,
1✔
619
                $schema->getJsonSchema()->getFile(),
1✔
620
            )
1✔
621
        );
1✔
622
    }
623

624
    private function resolveBuiltDraft(SchemaProcessor $schemaProcessor, JsonSchema $propertySchema): Draft
2,182✔
625
    {
626
        $configDraft = $schemaProcessor->getGeneratorConfiguration()->getDraft();
2,182✔
627

628
        $draft = $configDraft instanceof DraftFactoryInterface
2,182✔
629
            ? $configDraft->getDraftForSchema($propertySchema)
2,182✔
NEW
630
            : $configDraft;
×
631

632
        return $this->draftCache[$draft::class] ??= $draft->getDefinition()->build();
2,182✔
633
    }
634
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc