Skip to content

Commit b1e994d

Browse files
committed
basic pattern properties implementation
1 parent 4d138b5 commit b1e994d

File tree

8 files changed

+248
-4
lines changed

8 files changed

+248
-4
lines changed

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
}
1212
],
1313
"require": {
14-
"wol-soft/php-json-schema-model-generator-production": "^0.16.0",
14+
"wol-soft/php-json-schema-model-generator-production": "dev-PatternProperties",
1515
"wol-soft/php-micro-template": "^1.3.2",
1616

1717
"php": ">=7.2",

src/Model/GeneratorConfiguration.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,10 @@ public function __construct()
7676
public function addFilter(FilterInterface ...$additionalFilter): self
7777
{
7878
foreach ($additionalFilter as $filter) {
79-
$this->validateFilterCallback($filter->getFilter(), "Invalid filter callback for filter {$filter->getToken()}");
79+
$this->validateFilterCallback(
80+
$filter->getFilter(),
81+
"Invalid filter callback for filter {$filter->getToken()}"
82+
);
8083

8184
if ($filter instanceof TransformingFilterInterface) {
8285
$this->validateFilterCallback(
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
<?php
2+
3+
declare(strict_types = 1);
4+
5+
namespace PHPModelGenerator\Model\Validator;
6+
7+
use PHPModelGenerator\Exception\Object\InvalidPatternPropertiesException;
8+
use PHPModelGenerator\Exception\SchemaException;
9+
use PHPModelGenerator\Model\Property\Property;
10+
use PHPModelGenerator\Model\Property\PropertyInterface;
11+
use PHPModelGenerator\Model\Schema;
12+
use PHPModelGenerator\Model\SchemaDefinition\JsonSchema;
13+
use PHPModelGenerator\PropertyProcessor\PropertyMetaDataCollection;
14+
use PHPModelGenerator\PropertyProcessor\PropertyFactory;
15+
use PHPModelGenerator\PropertyProcessor\PropertyProcessorFactory;
16+
use PHPModelGenerator\SchemaProcessor\SchemaProcessor;
17+
use PHPModelGenerator\Utils\RenderHelper;
18+
19+
/**
20+
* Class PatternPropertiesValidator
21+
*
22+
* @package PHPModelGenerator\Model\Validator
23+
*/
24+
class PatternPropertiesValidator extends PropertyTemplateValidator
25+
{
26+
/** @var PropertyInterface */
27+
private $validationProperty;
28+
29+
/**
30+
* PatternPropertiesValidator constructor.
31+
*
32+
* @param SchemaProcessor $schemaProcessor
33+
* @param Schema $schema
34+
* @param string $pattern
35+
* @param JsonSchema $propertyStructure
36+
*
37+
* @throws SchemaException
38+
*/
39+
public function __construct(
40+
SchemaProcessor $schemaProcessor,
41+
Schema $schema,
42+
string $pattern,
43+
JsonSchema $propertyStructure
44+
) {
45+
$propertyFactory = new PropertyFactory(new PropertyProcessorFactory());
46+
47+
$this->validationProperty = $propertyFactory->create(
48+
new PropertyMetaDataCollection(['pattern property']),
49+
$schemaProcessor,
50+
$schema,
51+
'pattern property',
52+
$propertyStructure
53+
);
54+
55+
parent::__construct(
56+
new Property($schema->getClassName(), null, $propertyStructure),
57+
DIRECTORY_SEPARATOR . 'Validator' . DIRECTORY_SEPARATOR . 'PatternProperties.phptpl',
58+
[
59+
'pattern' => "/$pattern/",
60+
'validationProperty' => $this->validationProperty,
61+
'generatorConfiguration' => $schemaProcessor->getGeneratorConfiguration(),
62+
'viewHelper' => new RenderHelper($schemaProcessor->getGeneratorConfiguration()),
63+
],
64+
InvalidPatternPropertiesException::class,
65+
[$pattern, '&$invalidProperties']
66+
);
67+
}
68+
69+
/**
70+
* @inheritDoc
71+
*/
72+
public function getCheck(): string
73+
{
74+
$this->removeRequiredPropertyValidator($this->validationProperty);
75+
76+
return parent::getCheck();
77+
}
78+
79+
/**
80+
* Initialize all variables which are required to execute a property names validator
81+
*
82+
* @return string
83+
*/
84+
public function getValidatorSetUp(): string
85+
{
86+
return '
87+
$properties = $value;
88+
$invalidProperties = [];
89+
';
90+
}
91+
}

src/PropertyProcessor/Property/BaseProcessor.php

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
use PHPModelGenerator\Model\Validator\AbstractComposedPropertyValidator;
2121
use PHPModelGenerator\Model\Validator\AdditionalPropertiesValidator;
2222
use PHPModelGenerator\Model\Validator\ComposedPropertyValidator;
23+
use PHPModelGenerator\Model\Validator\PatternPropertiesValidator;
2324
use PHPModelGenerator\Model\Validator\PropertyNamesValidator;
2425
use PHPModelGenerator\Model\Validator\PropertyTemplateValidator;
2526
use PHPModelGenerator\Model\Validator\PropertyValidator;
@@ -72,7 +73,9 @@ public function process(string $propertyName, JsonSchema $propertySchema): Prope
7273
$this->generateValidators($property, $propertySchema);
7374

7475
$this->addPropertyNamesValidator($propertySchema);
76+
$this->addPatternPropertiesValidator($propertySchema);
7577
$this->addAdditionalPropertiesValidator($propertySchema);
78+
7679
$this->addMinPropertiesValidator($propertyName, $propertySchema);
7780
$this->addMaxPropertiesValidator($propertyName, $propertySchema);
7881

@@ -108,7 +111,7 @@ protected function addPropertyNamesValidator(JsonSchema $propertySchema): void
108111
}
109112

110113
/**
111-
* Add an object validator to disallow properties which are not defined in the schema
114+
* Add an object validator to specify constraints for properties which are not defined in the schema
112115
*
113116
* @param JsonSchema $propertySchema
114117
*
@@ -156,6 +159,37 @@ protected function addAdditionalPropertiesValidator(JsonSchema $propertySchema):
156159
);
157160
}
158161

162+
/**
163+
* @param JsonSchema $propertySchema
164+
*
165+
* @throws SchemaException
166+
*/
167+
protected function addPatternPropertiesValidator(JsonSchema $propertySchema): void
168+
{
169+
$json = $propertySchema->getJson();
170+
171+
if (!isset($json['patternProperties'])) {
172+
return;
173+
}
174+
175+
foreach ($json['patternProperties'] as $pattern => $schema) {
176+
if (preg_match("/$pattern/", '') === false) {
177+
throw new SchemaException(
178+
"Invalid pattern $pattern for pattern property in file {$propertySchema->getFile()}"
179+
);
180+
}
181+
182+
$this->schema->addBaseValidator(
183+
new PatternPropertiesValidator(
184+
$this->schemaProcessor,
185+
$this->schema,
186+
$pattern,
187+
$propertySchema->withJson($schema)
188+
)
189+
);
190+
}
191+
}
192+
159193
/**
160194
* Add an object validator to limit the amount of provided properties
161195
*
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
(function () use ($properties, &$invalidProperties) {
2+
{% if generatorConfiguration.collectErrors() %}
3+
$originalErrorRegistry = $this->_errorRegistry;
4+
{% endif %}
5+
6+
foreach ($properties as $propertyKey => $value) {
7+
try {
8+
if (!preg_match("{{ pattern }}", $propertyKey)) {
9+
continue;
10+
}
11+
12+
{% if generatorConfiguration.collectErrors() %}
13+
$this->_errorRegistry = new {{ viewHelper.getSimpleClassName(generatorConfiguration.getErrorRegistryClass()) }}();
14+
{% endif %}
15+
16+
{{ viewHelper.resolvePropertyDecorator(validationProperty) }}
17+
18+
{% foreach validationProperty.getOrderedValidators() as validator %}
19+
{{ validator.getValidatorSetUp() }}
20+
if ({{ validator.getCheck() }}) {
21+
{{ viewHelper.validationError(validator) }}
22+
}
23+
{% endforeach %}
24+
25+
{% if generatorConfiguration.collectErrors() %}
26+
if ($this->_errorRegistry->getErrors()) {
27+
$invalidProperties[$propertyKey] = $this->_errorRegistry->getErrors();
28+
}
29+
{% endif %}
30+
31+
} catch (\Exception $e) {
32+
// collect all errors concerning invalid property names
33+
isset($invalidProperties[$propertyKey])
34+
? $invalidProperties[$propertyKey][] = $e
35+
: $invalidProperties[$propertyKey] = [$e];
36+
}
37+
}
38+
39+
{% if generatorConfiguration.collectErrors() %}
40+
$this->_errorRegistry = $originalErrorRegistry;
41+
{% endif %}
42+
43+
return !empty($invalidProperties);
44+
})()

tests/Basic/PatternPropertiesTest.php

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PHPModelGenerator\Tests\Basic;
6+
7+
use PHPModelGenerator\Model\GeneratorConfiguration;
8+
use PHPModelGenerator\Tests\AbstractPHPModelGeneratorTest;
9+
use stdClass;
10+
11+
class PatternPropertiesTest extends AbstractPHPModelGeneratorTest
12+
{
13+
/**
14+
* @dataProvider invalidTypedPatternPropertyDataProvider
15+
*/
16+
public function testTypedPatternPropertyWithInvalidInputThrowsAnException(
17+
GeneratorConfiguration $configuration,
18+
$propertyValue
19+
): void {
20+
$this->expectValidationError(
21+
$configuration,
22+
'Invalid type for pattern property. Requires string, got ' . gettype($propertyValue)
23+
);
24+
25+
$className = $this->generateClassFromFile('TypedPatternProperty.json', $configuration);
26+
27+
new $className(['S_invalid' => $propertyValue]);
28+
}
29+
30+
public function invalidTypedPatternPropertyDataProvider(): array
31+
{
32+
return $this->combineDataProvider(
33+
$this->validationMethodDataProvider(),
34+
[
35+
'int' => [1],
36+
'float' => [0.92],
37+
'bool' => [true],
38+
'array' => [[]],
39+
'object' => [new stdClass()],
40+
]
41+
);
42+
}
43+
44+
/**
45+
* @dataProvider validTypedPatternPropertyDataProvider
46+
*/
47+
public function testTypedPatternPropertyWithValidInputIsValid(string $propertyValue): void
48+
{
49+
$className = $this->generateClassFromFile('TypedPatternProperty.json');
50+
$object = new $className(['S_valid' => $propertyValue]);
51+
52+
$this->assertSame(['S_valid' => $propertyValue], $object->getRawModelDataInput());
53+
}
54+
55+
public function validTypedPatternPropertyDataProvider(): array
56+
{
57+
return [
58+
'empty string' => [''],
59+
'spaces' => [' '],
60+
'only non numeric chars' => ['abc'],
61+
'mixed string' => ['1234a'],
62+
];
63+
}
64+
}

tests/Objects/StringPropertyTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ public function invalidPropertyTypeDataProvider(): array
100100
'float' => [0.92],
101101
'bool' => [true],
102102
'array' => [[]],
103-
'object' => [new stdClass()]
103+
'object' => [new stdClass()],
104104
]
105105
);
106106
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"type": "object",
3+
"patternProperties": {
4+
"^S_": {
5+
"type": "string"
6+
}
7+
}
8+
}

0 commit comments

Comments
 (0)