• 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.38
/src/SchemaProcessor/PostProcessor/Internal/SerializationPostProcessor.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace PHPModelGenerator\SchemaProcessor\PostProcessor\Internal;
6

7
use JsonSerializable;
8
use PHPModelGenerator\Filter\TransformingFilterInterface;
9
use PHPModelGenerator\Interfaces\SerializationInterface;
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\SchemaDefinition\JsonSchema;
16
use PHPModelGenerator\Model\Validator\AdditionalPropertiesValidator;
17
use PHPModelGenerator\Model\Validator\FilterValidator;
18
use PHPModelGenerator\Model\Validator\PatternPropertiesValidator;
19
use PHPModelGenerator\SchemaProcessor\Hook\SchemaHookResolver;
20
use PHPModelGenerator\SchemaProcessor\Hook\SerializationHookInterface;
21
use PHPModelGenerator\SchemaProcessor\PostProcessor\PostProcessor;
22
use PHPModelGenerator\SchemaProcessor\PostProcessor\RenderedMethod;
23
use PHPModelGenerator\Traits\SerializableTrait;
24

25
/**
26
 * Class SerializationPostProcessor
27
 *
28
 * @package PHPModelGenerator\SchemaProcessor\PostProcessor
29
 */
30
class SerializationPostProcessor extends PostProcessor
31
{
32
    /**
33
     * Add serialization support to the provided schema
34
     */
35
    public function process(Schema $schema, GeneratorConfiguration $generatorConfiguration): void
60✔
36
    {
37
        $schema
60✔
38
            ->addTrait(SerializableTrait::class)
60✔
39
            ->addInterface(JsonSerializable::class)
60✔
40
            ->addInterface(SerializationInterface::class);
60✔
41

42
        $this->addSerializeFunctionsForTransformingFilters($schema, $generatorConfiguration);
60✔
43
        $this->addSerializationHookMethod($schema, $generatorConfiguration);
60✔
44
        $this->addSkipNotProvidedPropertiesMap($schema, $generatorConfiguration);
60✔
45
        $this->addWriteOnlyExclusion($schema, $generatorConfiguration);
60✔
46

47
        $this->addPatternPropertiesSerialization($schema, $generatorConfiguration);
60✔
48

49
        $json = $schema->getJsonSchema()->getJson();
60✔
50
        if (isset($json['additionalProperties']) && $json['additionalProperties'] !== false) {
60✔
51
            $this->addAdditionalPropertiesSerialization($schema, $generatorConfiguration);
11✔
52
        }
53
    }
54

55
    /**
56
     * Each transforming filter must provide a method to serialize the value. Add a method to the schema to call the
57
     * serialization for each property with a transforming filter
58
     */
59
    private function addSerializeFunctionsForTransformingFilters(
60✔
60
        Schema $schema,
61
        GeneratorConfiguration $generatorConfiguration,
62
    ): void {
63
        foreach ($schema->getProperties() as $property) {
60✔
64
            foreach ($property->getValidators() as $validator) {
59✔
65
                $validator = $validator->getValidator();
56✔
66

67
                if (
68
                    $validator instanceof FilterValidator &&
56✔
69
                    $validator->getFilter() instanceof TransformingFilterInterface
56✔
70
                ) {
71
                    [$serializerClass, $serializerMethod] = $validator->getFilter()->getSerializer();
24✔
72

73
                    $schema->addMethod(
24✔
74
                        "serialize{$property->getAttribute()}",
24✔
75
                        new RenderedMethod(
24✔
76
                            $schema,
24✔
77
                            $generatorConfiguration,
24✔
78
                            join(
24✔
79
                                DIRECTORY_SEPARATOR,
24✔
80
                                ['Serialization', 'TransformingFilterSerializer.phptpl'],
24✔
81
                            ),
24✔
82
                            [
24✔
83
                                'property' => $property,
24✔
84
                                'serializerClass' => $serializerClass,
24✔
85
                                'serializerMethod' => $serializerMethod,
24✔
86
                                'serializerOptions' => var_export($validator->getFilterOptions(), true),
24✔
87
                            ],
24✔
88
                        )
24✔
89
                    );
24✔
90
                }
91
            }
92
        }
93

94
        foreach ($schema->getBaseValidators() as $validator) {
60✔
95
            if ($validator instanceof PatternPropertiesValidator) {
23✔
96
                foreach ($validator->getValidationProperty()->getValidators() as $patternPropertyValidator) {
6✔
97
                    $filterValidator = $patternPropertyValidator->getValidator();
6✔
98

99
                    if (
100
                        $filterValidator instanceof FilterValidator &&
6✔
101
                        $filterValidator->getFilter() instanceof TransformingFilterInterface
6✔
102
                    ) {
103
                        [$serializerClass, $serializerMethod] = $filterValidator->getFilter()->getSerializer();
2✔
104

105
                        $schema->addMethod(
2✔
106
                            "serialize{$validator->getKey()}",
2✔
107
                            new RenderedMethod(
2✔
108
                                $schema,
2✔
109
                                $generatorConfiguration,
2✔
110
                                join(
2✔
111
                                    DIRECTORY_SEPARATOR,
2✔
112
                                    ['Serialization', 'PatternPropertyTransformingFilterSerializer.phptpl'],
2✔
113
                                ),
2✔
114
                                [
2✔
115
                                    'key' => $validator->getKey(),
2✔
116
                                    'serializerClass' => $serializerClass,
2✔
117
                                    'serializerMethod' => $serializerMethod,
2✔
118
                                    'serializerOptions' => var_export($filterValidator->getFilterOptions(), true),
2✔
119
                                ],
2✔
120
                            )
2✔
121
                        );
2✔
122
                    }
123
                }
124
            }
125
        }
126
    }
127

128
    private function addSerializationHookMethod(Schema $schema, GeneratorConfiguration $generatorConfiguration): void
60✔
129
    {
130
        $schema->addMethod(
60✔
131
            'resolveSerializationHook',
60✔
132
            new RenderedMethod(
60✔
133
                $schema,
60✔
134
                $generatorConfiguration,
60✔
135
                join(DIRECTORY_SEPARATOR, ['Serialization', 'SerializationHook.phptpl']),
60✔
136
                [
60✔
137
                    'schemaHookResolver' => new SchemaHookResolver($schema),
60✔
138
                ],
60✔
139
            )
60✔
140
        );
60✔
141
    }
142

143
    /**
144
     * Adds code to merge serialized pattern properties into the serialization result
145
     */
146
    private function addPatternPropertiesSerialization(
60✔
147
        Schema $schema,
148
        GeneratorConfiguration $generatorConfiguration,
149
    ): void {
150
        if (!isset($schema->getJsonSchema()->getJson()['patternProperties'])) {
60✔
151
            return;
54✔
152
        }
153

154
        $schema->addMethod(
6✔
155
            'serializePatternProperties',
6✔
156
            new RenderedMethod($schema, $generatorConfiguration, 'Serialization/PatternPropertiesSerializer.phptpl'),
6✔
157
        );
6✔
158

159
        $schema->addSchemaHook(
6✔
160
            new class () implements SerializationHookInterface {
6✔
161
                public function getCode(): string
162
                {
163
                    return '$data += $this->serializePatternProperties($depth, $except);';
6✔
164
                }
165
            },
6✔
166
        );
6✔
167
    }
168

169
    /**
170
     * Adds a custom serialization function to the schema to merge all additional properties into the serialization
171
     * result on serializations
172
     */
173
    public function addAdditionalPropertiesSerialization(
15✔
174
        Schema $schema,
175
        GeneratorConfiguration $generatorConfiguration,
176
    ): void {
177
        $validationProperty = null;
15✔
178
        foreach ($schema->getBaseValidators() as $validator) {
15✔
179
            if (is_a($validator, AdditionalPropertiesValidator::class)) {
15✔
180
                $validationProperty = $validator->getValidationProperty();
11✔
181
            }
182
        }
183

184
        $transformingFilterValidator = null;
15✔
185

186
        if ($validationProperty) {
15✔
187
            foreach ($validationProperty->getValidators() as $validator) {
11✔
188
                $validator = $validator->getValidator();
11✔
189

190
                if (
191
                    $validator instanceof FilterValidator &&
11✔
192
                    $validator->getFilter() instanceof TransformingFilterInterface
11✔
193
                ) {
194
                    $transformingFilterValidator = $validator;
3✔
195
                    [$serializerClass, $serializerMethod] = $validator->getFilter()->getSerializer();
3✔
196
                }
197
            }
198
        }
199

200
        $schema->addMethod(
15✔
201
            'serializeAdditionalProperties',
15✔
202
            new RenderedMethod(
15✔
203
                $schema,
15✔
204
                $generatorConfiguration,
15✔
205
                'Serialization/AdditionalPropertiesSerializer.phptpl',
15✔
206
                [
15✔
207
                    'serializerClass' => $serializerClass ?? null,
15✔
208
                    'serializerMethod' => $serializerMethod ?? null,
15✔
209
                    'serializerOptions' => $transformingFilterValidator
15✔
210
                        ? var_export($transformingFilterValidator->getFilterOptions(), true)
3✔
UNCOV
211
                        : [],
×
212
                ],
15✔
213
            )
15✔
214
        );
15✔
215

216
        $schema->addSchemaHook(
15✔
217
            new class () implements SerializationHookInterface {
15✔
218
                public function getCode(): string
219
                {
220
                    return '$data = array_merge($this->serializeAdditionalProperties($depth, $except), $data);';
15✔
221
                }
222
            },
15✔
223
        );
15✔
224
    }
225

226
    private function addWriteOnlyExclusion(
60✔
227
        Schema $schema,
228
        GeneratorConfiguration $generatorConfiguration,
229
    ): void {
230
        $writeOnlyAttributes = array_map(
60✔
231
            static fn(PropertyInterface $property): string => $property->getAttribute(true),
60✔
232
            array_filter(
60✔
233
                $schema->getProperties(),
60✔
234
                static fn(PropertyInterface $property): bool => $property->isWriteOnly(),
60✔
235
            ),
60✔
236
        );
60✔
237

238
        if (!$writeOnlyAttributes) {
60✔
239
            return;
58✔
240
        }
241

242
        $keysExport = var_export(array_values($writeOnlyAttributes), true);
2✔
243

244
        $schema->addSchemaHook(
2✔
245
            new class ($keysExport) implements SerializationHookInterface
2✔
246
            {
2✔
247
                public function __construct(private readonly string $keysExport)
248
                {}
2✔
249

250
                public function getCode(): string
251
                {
252
                    return sprintf(
2✔
253
                        'foreach (%s as $_writeOnlyKey) { unset($data[$_writeOnlyKey]); }',
2✔
254
                        $this->keysExport,
2✔
255
                    );
2✔
256
                }
257
            },
2✔
258
        );
2✔
259
    }
260

261
    private function addSkipNotProvidedPropertiesMap(
60✔
262
        Schema $schema,
263
        GeneratorConfiguration $generatorConfiguration,
264
    ): void {
265
        if ($generatorConfiguration->isImplicitNullAllowed()) {
60✔
266
            return;
45✔
267
        }
268

269
        $skipNotProvidedValues = array_map(
15✔
270
            static fn(PropertyInterface $property): string => $property->getName(),
15✔
271
            array_filter(
15✔
272
                $schema->getProperties(),
15✔
273
                static fn(PropertyInterface $property): bool =>
15✔
274
                    !$property->isRequired() && !$property->getDefaultValue(),
15✔
275
            )
15✔
276
        );
15✔
277

278
        $schema->addProperty(
15✔
279
            (new Property(
15✔
280
                'skipNotProvidedPropertiesMap',
15✔
281
                new PropertyType('array'),
15✔
282
                new JsonSchema(__FILE__, []),
15✔
283
                'Values which might be skipped for serialization if not provided',
15✔
284
            ))
15✔
285
                ->setDefaultValue($skipNotProvidedValues)
15✔
286
                ->setInternal(true),
15✔
287
        );
15✔
288
    }
289
}
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