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

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

11 May 2026 10:16PM UTC coverage: 98.554% (+0.003%) from 98.551%
25700642152

Pull #126

github

Enno Woortmann
Add content validator registry for contentMediaType/contentEncoding (Draft 7 Phase 5)

Introduces an opt-in registry on GeneratorConfiguration that lets users register
ContentValidatorInterface implementations keyed by media type and/or encoding.
At generation time the most specific matching validator is embedded into the
generated model; at runtime it is called against the raw string before the
MediaString wrapper is applied. Validator failures throw a ContentException whose
getPrevious() carries the original error for programmatic inspection.

addContentValidator() accepts null (wildcard), string, or string[] for each
dimension; arrays expand via Cartesian product so a single call can cover
multiple media types or encodings at once.
Pull Request #126: Json schema draft7

244 of 252 new or added lines in 13 files covered. (96.83%)

4 existing lines in 4 files now uncovered.

4770 of 4840 relevant lines covered (98.55%)

643.67 hits per line

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

99.12
/src/SchemaProcessor/PostProcessor/AdditionalPropertiesAccessorPostProcessor.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace PHPModelGenerator\SchemaProcessor\PostProcessor;
6

7
use PHPModelGenerator\Exception\Object\MinPropertiesException;
8
use PHPModelGenerator\Exception\Object\RegularPropertyAsAdditionalPropertyException;
9
use PHPModelGenerator\Exception\SchemaException;
10
use PHPModelGenerator\Model\GeneratorConfiguration;
11
use PHPModelGenerator\Model\Property\Property;
12
use PHPModelGenerator\Model\Property\PropertyInterface;
13
use PHPModelGenerator\Model\Property\PropertyType;
14
use PHPModelGenerator\Model\Schema;
15
use PHPModelGenerator\Model\Validator\AdditionalPropertiesValidator;
16
use PHPModelGenerator\Model\Validator\PropertyValidator;
17
use PHPModelGenerator\PropertyProcessor\Decorator\TypeHint\ArrayTypeHintDecorator;
18
use PHPModelGenerator\PropertyProcessor\Decorator\TypeHint\TypeHintDecorator;
19
use PHPModelGenerator\SchemaProcessor\Hook\SchemaHookResolver;
20
use PHPModelGenerator\SchemaProcessor\PostProcessor\Internal\AdditionalPropertiesPostProcessor;
21
use PHPModelGenerator\SchemaProcessor\PostProcessor\Internal\SerializationPostProcessor;
22
use PHPModelGenerator\Utils\RenderHelper;
23

24
/**
25
 * Class AdditionalPropertiesAccessorPostProcessor
26
 *
27
 * @package PHPModelGenerator\SchemaProcessor\PostProcessor
28
 */
29
class AdditionalPropertiesAccessorPostProcessor extends PostProcessor
30
{
31
    /**
32
     * AdditionalPropertiesAccessorPostProcessor constructor.
33
     *
34
     * @param bool $addForModelsWithoutAdditionalPropertiesDefinition By default, the additional properties accessor
35
     * methods will be added only to schemas defining additionalProperties constraints as these models expect additional
36
     * properties. If set to true the accessor methods will be generated for models which don't define
37
     * additionalProperties constraints.
38
     */
39
    public function __construct(private readonly bool $addForModelsWithoutAdditionalPropertiesDefinition = false)
37✔
40
    {}
37✔
41

42
    /**
43
     * Add methods to handle additional properties to the provided schema
44
     *
45
     * @throws SchemaException
46
     */
47
    public function process(Schema $schema, GeneratorConfiguration $generatorConfiguration): void
37✔
48
    {
49
        $json = $schema->getJsonSchema()->getJson();
37✔
50

51
        if (
52
            (!$this->addForModelsWithoutAdditionalPropertiesDefinition && !isset($json['additionalProperties']))
37✔
53
            || (isset($json['additionalProperties']) && $json['additionalProperties'] === false)
35✔
54
            || (!isset($json['additionalProperties']) && $generatorConfiguration->denyAdditionalProperties())
37✔
55
        ) {
56
            return;
13✔
57
        }
58

59
        $validationProperty = null;
32✔
60
        foreach ($schema->getBaseValidators() as $validator) {
32✔
61
            if (is_a($validator, AdditionalPropertiesValidator::class)) {
29✔
62
                $validationProperty = $validator->getValidationProperty();
21✔
63
            }
64
        }
65

66
        // check if basic code must be added
67
        if ($this->addForModelsWithoutAdditionalPropertiesDefinition && !isset($json['additionalProperties'])) {
32✔
68
            (new AdditionalPropertiesPostProcessor())->addAdditionalPropertiesCollectionProperty($schema);
11✔
69
        }
70
        if (
71
            $generatorConfiguration->hasSerializationEnabled() &&
32✔
72
            $this->addForModelsWithoutAdditionalPropertiesDefinition &&
32✔
73
            !isset($json['additionalProperties'])
32✔
74
        ) {
75
            (new SerializationPostProcessor())->addAdditionalPropertiesSerialization($schema, $generatorConfiguration);
4✔
76
        }
77

78
        $this->addGetAdditionalPropertiesMethod($schema, $generatorConfiguration, $validationProperty);
32✔
79
        $this->addGetAdditionalPropertyMethod($schema, $generatorConfiguration, $validationProperty);
32✔
80

81
        if (!$generatorConfiguration->isImmutable()) {
32✔
82
            $this->addSetAdditionalPropertyMethod($schema, $generatorConfiguration, $validationProperty);
19✔
83
            $this->addRemoveAdditionalPropertyMethod($schema, $generatorConfiguration);
19✔
84
        }
85
    }
86

87
    /**
88
     * Adds a method to add or update an additional property
89
     */
90
    private function addSetAdditionalPropertyMethod(
19✔
91
        Schema $schema,
92
        GeneratorConfiguration $generatorConfiguration,
93
        ?PropertyInterface $validationProperty,
94
    ): void {
95
        $objectProperties = RenderHelper::varExportArray(
19✔
96
            array_map(
19✔
97
                static fn(PropertyInterface $property): string => $property->getName(),
19✔
98
                array_filter(
19✔
99
                    $schema->getProperties(),
19✔
100
                    static fn(PropertyInterface $property): bool => !$property->isInternal(),
19✔
101
                )
19✔
102
            ),
19✔
103
        );
19✔
104

105
        $schema->addUsedClass(RegularPropertyAsAdditionalPropertyException::class);
19✔
106
        $schema->addMethod(
19✔
107
            'setAdditionalProperty',
19✔
108
            new RenderedMethod(
19✔
109
                $schema,
19✔
110
                $generatorConfiguration,
19✔
111
                'AdditionalProperties/SetAdditionalProperty.phptpl',
19✔
112
                [
19✔
113
                    'validationProperty' => $validationProperty,
19✔
114
                    'objectProperties' => $objectProperties,
19✔
115
                    'schemaHookResolver' => new SchemaHookResolver($schema),
19✔
116
                ],
19✔
117
            )
19✔
118
        );
19✔
119
    }
120

121
    /**
122
     * Adds a method to remove an additional property from the object via property key
123
     *
124
     * @throws SchemaException
125
     */
126
    private function addRemoveAdditionalPropertyMethod(
19✔
127
        Schema $schema,
128
        GeneratorConfiguration $generatorConfiguration,
129
    ): void {
130
        $minPropertyValidator = null;
19✔
131
        $json = $schema->getJsonSchema()->getJson();
19✔
132
        if (isset($json['minProperties'])) {
19✔
133
            $minPropertyValidator = new PropertyValidator(
8✔
134
                new Property($schema->getClassName(), null, $schema->getJsonSchema()),
8✔
135
                sprintf(
8✔
136
                    '%s < %d',
8✔
137
                    'count($this->rawModelDataInput) - 1',
8✔
138
                    $json['minProperties'],
8✔
139
                ),
8✔
140
                MinPropertiesException::class,
8✔
141
                [$json['minProperties']],
8✔
142
            );
8✔
143
        }
144

145
        $schema->addMethod(
19✔
146
            'removeAdditionalProperty',
19✔
147
            new RenderedMethod(
19✔
148
                $schema,
19✔
149
                $generatorConfiguration,
19✔
150
                'AdditionalProperties/RemoveAdditionalProperty.phptpl',
19✔
151
                ['minPropertyValidator' => $minPropertyValidator],
19✔
152
            )
19✔
153
        );
19✔
154
    }
155

156
    /**
157
     * Adds a method to get a single additional property via property key
158
     */
159
    private function addGetAdditionalPropertyMethod(
32✔
160
        Schema $schema,
161
        GeneratorConfiguration $generatorConfiguration,
162
        ?PropertyInterface $validationProperty,
163
    ): void {
164
        // return type of the additional property must always be nullable as a non existent key can be requested
165
        if ($validationProperty && $validationProperty->getType()) {
32✔
166
            $validationProperty = (clone $validationProperty)->setType(
21✔
167
                $validationProperty->getType(),
21✔
168
                new PropertyType($validationProperty->getType(true)->getNames(), true),
21✔
169
            );
21✔
170
        }
171

172
        $schema->addMethod(
32✔
173
            'getAdditionalProperty',
32✔
174
            new RenderedMethod(
32✔
175
                $schema,
32✔
176
                $generatorConfiguration,
32✔
177
                'AdditionalProperties/GetAdditionalProperty.phptpl',
32✔
178
                [
32✔
179
                    'validationProperty' => $validationProperty
32✔
180
                        // type hint always with null as a non existent property may be requested (casually covered by
32✔
181
                        // the nullable type, except for multi type properties)
32✔
182
                        ? (clone $validationProperty)->addTypeHintDecorator(new TypeHintDecorator(['null']))
21✔
UNCOV
183
                        : null
×
184
                ],
32✔
185
            )
32✔
186
        );
32✔
187
    }
188

189
    private function addGetAdditionalPropertiesMethod(
32✔
190
        Schema $schema,
191
        GeneratorConfiguration $generatorConfiguration,
192
        ?PropertyInterface $validationProperty,
193
    ): void {
194
        $validationProperty = $validationProperty
32✔
195
            // type hint always without null as the getter always returns an array
32✔
196
            ? (clone $validationProperty)
21✔
197
                ->setRequired(true)
21✔
198
                ->addTypeHintDecorator(new ArrayTypeHintDecorator($validationProperty))
21✔
199
            : null;
11✔
200

201
        if ($validationProperty && $validationProperty->getType(true)) {
32✔
202
            $validationProperty->setType(
21✔
203
                $validationProperty->getType(),
21✔
204
                new PropertyType($validationProperty->getType(true)->getNames(), false),
21✔
205
            );
21✔
206
        }
207

208
        $schema->addMethod(
32✔
209
            'getAdditionalProperties',
32✔
210
            new RenderedMethod(
32✔
211
                $schema,
32✔
212
                $generatorConfiguration,
32✔
213
                'AdditionalProperties/GetAdditionalProperties.phptpl',
32✔
214
                [
32✔
215
                    'validationProperty' => $validationProperty,
32✔
216

217
                ],
32✔
218
            )
32✔
219
        );
32✔
220
    }
221
}
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