Skip to content

Commit d88a213

Browse files
committed
Allow pattern properties when additional properties is set to false
setAdditionalProperty must not check if the provided property key matches an internal property
1 parent 1ab115e commit d88a213

File tree

7 files changed

+100
-12
lines changed

7 files changed

+100
-12
lines changed

docs/source/generator/postProcessor.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,7 @@ What can you do inside your custom post processor?
243243
* Implement the **GetterHookInterface** to add code to your getter methods
244244
* Implement the **SetterBeforeValidationHookInterface** to add code to the beginning of your setter methods
245245
* Implement the **SetterAfterValidationHookInterface** to add code to the end of your setter methods
246+
* Implement the **SerializationHookInterface** to add code to the end of your serialization process
246247

247248
.. warning::
248249

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
declare(strict_types = 1);
4+
5+
namespace PHPModelGenerator\Model\Validator;
6+
7+
use PHPModelGenerator\Exception\Object\AdditionalPropertiesException;
8+
use PHPModelGenerator\Model\Property\PropertyInterface;
9+
use PHPModelGenerator\Utils\RenderHelper;
10+
11+
/**
12+
* Class NoAdditionalPropertiesValidator
13+
*
14+
* @package PHPModelGenerator\Model\Validator
15+
*/
16+
class NoAdditionalPropertiesValidator extends PropertyTemplateValidator
17+
{
18+
/**
19+
* PropertyDependencyValidator constructor.
20+
*
21+
* @param PropertyInterface $property
22+
* @param array $json
23+
*/
24+
public function __construct(PropertyInterface $property, array $json)
25+
{
26+
parent::__construct(
27+
$property,
28+
DIRECTORY_SEPARATOR . 'Validator' . DIRECTORY_SEPARATOR . 'NoAdditionalProperties.phptpl',
29+
[
30+
'properties' => RenderHelper::varExportArray(array_keys($json['properties'] ?? [])),
31+
'pattern' => addcslashes(join('|', array_keys($json['patternProperties'] ?? [])), "'/"),
32+
],
33+
AdditionalPropertiesException::class,
34+
['&$additionalProperties']
35+
);
36+
}
37+
}

src/PropertyProcessor/Property/BaseProcessor.php

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
use PHPMicroTemplate\Exception\FileSystemException;
88
use PHPMicroTemplate\Exception\SyntaxErrorException;
99
use PHPMicroTemplate\Exception\UndefinedSymbolException;
10-
use PHPModelGenerator\Exception\Object\AdditionalPropertiesException;
1110
use PHPModelGenerator\Exception\Object\MaxPropertiesException;
1211
use PHPModelGenerator\Exception\Object\MinPropertiesException;
1312
use PHPModelGenerator\Exception\SchemaException;
@@ -20,6 +19,7 @@
2019
use PHPModelGenerator\Model\Validator\AbstractComposedPropertyValidator;
2120
use PHPModelGenerator\Model\Validator\AdditionalPropertiesValidator;
2221
use PHPModelGenerator\Model\Validator\ComposedPropertyValidator;
22+
use PHPModelGenerator\Model\Validator\NoAdditionalPropertiesValidator;
2323
use PHPModelGenerator\Model\Validator\PatternPropertiesValidator;
2424
use PHPModelGenerator\Model\Validator\PropertyNamesValidator;
2525
use PHPModelGenerator\Model\Validator\PropertyTemplateValidator;
@@ -29,7 +29,6 @@
2929
use PHPModelGenerator\PropertyProcessor\PropertyMetaDataCollection;
3030
use PHPModelGenerator\PropertyProcessor\PropertyFactory;
3131
use PHPModelGenerator\PropertyProcessor\PropertyProcessorFactory;
32-
use PHPModelGenerator\Utils\RenderHelper;
3332

3433
/**
3534
* Class BaseObjectProcessor
@@ -148,14 +147,9 @@ protected function addAdditionalPropertiesValidator(JsonSchema $propertySchema):
148147
}
149148

150149
$this->schema->addBaseValidator(
151-
new PropertyValidator(
150+
new NoAdditionalPropertiesValidator(
152151
new Property($this->schema->getClassName(), null, $propertySchema),
153-
sprintf(
154-
'$additionalProperties = array_diff(array_keys($modelData), %s)',
155-
RenderHelper::varExportArray(array_keys($json['properties'] ?? []))
156-
),
157-
AdditionalPropertiesException::class,
158-
['&$additionalProperties']
152+
$json
159153
)
160154
);
161155
}

src/SchemaProcessor/PostProcessor/AdditionalPropertiesAccessorPostProcessor.php

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,9 +103,17 @@ private function addSetAdditionalPropertyMethod(
103103
?PropertyInterface $validationProperty
104104
): void {
105105
$objectProperties = RenderHelper::varExportArray(
106-
array_map(function (PropertyInterface $property): string {
107-
return $property->getName();
108-
}, $schema->getProperties())
106+
array_map(
107+
function (PropertyInterface $property): string {
108+
return $property->getName();
109+
},
110+
array_filter(
111+
$schema->getProperties(),
112+
function (PropertyInterface $property): bool {
113+
return !$property->isInternal();
114+
}
115+
)
116+
)
109117
);
110118

111119
$schema->addUsedClass(RegularPropertyAsAdditionalPropertyException::class);
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
$additionalProperties = (function () use ($modelData): array {
2+
$additionalProperties = array_diff(array_keys($modelData), {{ properties }});
3+
4+
{% if pattern %}
5+
// filter out all pattern properties
6+
$additionalProperties = array_filter(
7+
$additionalProperties,
8+
function (string $property): bool {
9+
return preg_match('/{{ pattern }}/', $property) !== 1;
10+
}
11+
);
12+
{% endif %}
13+
14+
return $additionalProperties;
15+
})()

tests/PostProcessor/PatternPropertiesAccessorPostProcessorTest.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use DateTime;
88
use Exception;
99
use PHPModelGenerator\Exception\ErrorRegistryException;
10+
use PHPModelGenerator\Exception\Object\AdditionalPropertiesException;
1011
use PHPModelGenerator\Exception\Object\UnknownPatternPropertyException;
1112
use PHPModelGenerator\Exception\SchemaException;
1213
use PHPModelGenerator\Exception\ValidationException;
@@ -459,4 +460,27 @@ public function testPatternPropertiesWithFilter(): void
459460
$this->assertSame('array', $returnType->getName());
460461
$this->assertFalse($returnType->allowsNull());
461462
}
463+
464+
public function testPatternPropertiesCanBeAddedWhenAdditionalPropertiesAreDenied(): void
465+
{
466+
$this->addPostProcessors(new PatternPropertiesAccessorPostProcessor(), new PopulatePostProcessor());
467+
468+
$className = $this->generateClassFromFile(
469+
'PatternPropertiesWithAdditionalPropertiesDenied.json',
470+
(new GeneratorConfiguration())->setSerialization(true)->setImmutable(false)->setCollectErrors(false)
471+
);
472+
473+
$object = new $className(['a0' => 'Hello', 'a1' => 'World']);
474+
$this->assertEqualsCanonicalizing(['a0' => 'Hello', 'a1' => 'World'], $object->toArray());
475+
476+
$object->populate(['a0' => 'Goodbye', 'a2' => 'cya']);
477+
$this->assertEqualsCanonicalizing(['a0' => 'Goodbye', 'a1' => 'World', 'a2' => 'cya'], $object->toArray());
478+
479+
$this->expectException(AdditionalPropertiesException::class);
480+
$this->expectExceptionMessageMatches(
481+
'/Provided JSON for .* contains not allowed additional properties \[b1\]/'
482+
);
483+
484+
$object->populate(['a0' => 'Hello', 'b1' => 'not allowed']);
485+
}
462486
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"type": "object",
3+
"patternProperties": {
4+
"^a": {
5+
"type": "string"
6+
}
7+
},
8+
"additionalProperties": false
9+
}

0 commit comments

Comments
 (0)