1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/* |
4
|
|
|
* This file is part of the JVal package. |
5
|
|
|
* |
6
|
|
|
* For the full copyright and license information, please view the LICENSE |
7
|
|
|
* file that was distributed with this source code. |
8
|
|
|
*/ |
9
|
|
|
|
10
|
|
|
namespace JVal\Constraint; |
11
|
|
|
|
12
|
|
|
use JVal\Constraint; |
13
|
|
|
use JVal\Context; |
14
|
|
|
use JVal\Exception\Constraint\InvalidRegexException; |
15
|
|
|
use JVal\Exception\Constraint\InvalidTypeException; |
16
|
|
|
use JVal\Types; |
17
|
|
|
use JVal\Utils; |
18
|
|
|
use JVal\Walker; |
19
|
|
|
use stdClass; |
20
|
|
|
|
21
|
|
|
/** |
22
|
|
|
* Constraint for the "properties", "additionalProperties" and |
23
|
|
|
* "patternProperties" keywords. |
24
|
|
|
*/ |
25
|
|
|
class PropertiesConstraint implements Constraint |
26
|
|
|
{ |
27
|
|
|
/** |
28
|
|
|
* {@inheritdoc} |
29
|
|
|
*/ |
30
|
357 |
|
public function keywords() |
31
|
|
|
{ |
32
|
357 |
|
return ['properties', 'additionalProperties', 'patternProperties']; |
33
|
|
|
} |
34
|
|
|
|
35
|
|
|
/** |
36
|
|
|
* {@inheritdoc} |
37
|
|
|
*/ |
38
|
348 |
|
public function supports($type) |
39
|
|
|
{ |
40
|
348 |
|
return $type === Types::TYPE_OBJECT; |
41
|
|
|
} |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* {@inheritdoc} |
45
|
|
|
*/ |
46
|
121 |
|
public function normalize(stdClass $schema, Context $context, Walker $walker) |
47
|
|
|
{ |
48
|
121 |
|
$this->createDefaults($schema); |
49
|
|
|
|
50
|
121 |
|
$context->enterNode($schema->properties, 'properties'); |
51
|
121 |
|
$this->parsePropertiesProperty($schema, $context, $walker); |
52
|
|
|
|
53
|
119 |
|
$context->enterSibling($schema->additionalProperties, 'additionalProperties'); |
54
|
119 |
|
$this->parseAdditionalPropertiesProperty($schema, $context, $walker); |
55
|
|
|
|
56
|
118 |
|
$context->enterSibling($schema->patternProperties, 'patternProperties'); |
57
|
118 |
|
$this->parsePatternPropertiesProperty($schema, $context, $walker); |
58
|
115 |
|
$context->leaveNode(); |
59
|
115 |
|
} |
60
|
|
|
|
61
|
|
|
/** |
62
|
|
|
* {@inheritdoc} |
63
|
|
|
*/ |
64
|
99 |
|
public function apply($instance, stdClass $schema, Context $context, Walker $walker) |
65
|
|
|
{ |
66
|
|
|
// implementation of the algorithms described in 5.4.4.4 and in 8.3 |
67
|
99 |
|
foreach ($instance as $property => $value) { |
68
|
92 |
|
$context->enterNode($instance->{$property}, $property); |
69
|
92 |
|
$schemas = []; |
70
|
|
|
|
71
|
92 |
|
if (isset($schema->properties->{$property})) { |
72
|
65 |
|
$schemas[] = $schema->properties->{$property}; |
73
|
65 |
|
} |
74
|
|
|
|
75
|
92 |
|
foreach ($schema->patternProperties as $regex => $propertySchema) { |
76
|
34 |
|
if (Utils::matchesRegex($property, $regex)) { |
77
|
23 |
|
$schemas[] = $propertySchema; |
78
|
23 |
|
} |
79
|
92 |
|
} |
80
|
|
|
|
81
|
92 |
|
if (!$schemas) { |
|
|
|
|
82
|
36 |
|
if (is_object($schema->additionalProperties)) { |
83
|
27 |
|
$schemas[] = $schema->additionalProperties; |
84
|
36 |
|
} elseif ($schema->additionalProperties === false) { |
85
|
9 |
|
$context->leaveNode(); |
86
|
9 |
|
$context->addViolation('additional property "%s" is not allowed', [$property]); |
87
|
9 |
|
$context->enterNode($instance->{$property}, $property); |
88
|
9 |
|
} |
89
|
36 |
|
} |
90
|
|
|
|
91
|
92 |
|
foreach ($schemas as $childSchema) { |
92
|
90 |
|
$walker->applyConstraints($value, $childSchema, $context); |
93
|
92 |
|
} |
94
|
|
|
|
95
|
92 |
|
$context->leaveNode(); |
96
|
99 |
|
} |
97
|
99 |
|
} |
98
|
|
|
|
99
|
121 |
|
private function createDefaults(stdClass $schema) |
100
|
|
|
{ |
101
|
121 |
|
if (!property_exists($schema, 'properties')) { |
102
|
17 |
|
$schema->properties = new stdClass(); |
103
|
17 |
|
} |
104
|
|
|
|
105
|
121 |
|
if (!property_exists($schema, 'additionalProperties') |
106
|
121 |
|
|| $schema->additionalProperties === true) { |
107
|
33 |
|
$schema->additionalProperties = new stdClass(); |
108
|
33 |
|
} |
109
|
|
|
|
110
|
121 |
|
if (!property_exists($schema, 'patternProperties')) { |
111
|
33 |
|
$schema->patternProperties = new stdClass(); |
112
|
33 |
|
} |
113
|
121 |
|
} |
114
|
|
|
|
115
|
121 |
|
private function parsePropertiesProperty(stdClass $schema, Context $context, Walker $walker) |
116
|
|
|
{ |
117
|
121 |
|
if (!is_object($schema->properties)) { |
118
|
1 |
|
throw new InvalidTypeException($context, Types::TYPE_OBJECT); |
119
|
|
|
} |
120
|
|
|
|
121
|
120 |
View Code Duplication |
foreach ($schema->properties as $property => $value) { |
122
|
90 |
|
$context->enterNode($schema->properties->{$property}, $property); |
123
|
|
|
|
124
|
90 |
|
if (!is_object($value)) { |
125
|
1 |
|
throw new InvalidTypeException($context, Types::TYPE_OBJECT); |
126
|
|
|
} |
127
|
|
|
|
128
|
89 |
|
$walker->parseSchema($value, $context); |
129
|
89 |
|
$context->leaveNode(); |
130
|
119 |
|
} |
131
|
119 |
|
} |
132
|
|
|
|
133
|
119 |
View Code Duplication |
private function parseAdditionalPropertiesProperty(stdClass $schema, Context $context, Walker $walker) |
134
|
|
|
{ |
135
|
119 |
|
if (is_object($schema->additionalProperties)) { |
136
|
94 |
|
$walker->parseSchema($schema->additionalProperties, $context); |
137
|
119 |
|
} elseif (!is_bool($schema->additionalProperties)) { |
138
|
1 |
|
throw new InvalidTypeException($context, [Types::TYPE_OBJECT, Types::TYPE_BOOLEAN]); |
139
|
|
|
} |
140
|
118 |
|
} |
141
|
|
|
|
142
|
118 |
|
private function parsePatternPropertiesProperty(stdClass $schema, Context $context, Walker $walker) |
143
|
|
|
{ |
144
|
118 |
|
if (!is_object($schema->patternProperties)) { |
145
|
1 |
|
throw new InvalidTypeException($context, Types::TYPE_OBJECT); |
146
|
|
|
} |
147
|
|
|
|
148
|
117 |
|
foreach ($schema->patternProperties as $regex => $value) { |
149
|
42 |
|
$context->enterNode($value, $regex); |
150
|
|
|
|
151
|
42 |
|
if (!Utils::isValidRegex($regex)) { |
152
|
1 |
|
throw new InvalidRegexException($context); |
153
|
|
|
} |
154
|
|
|
|
155
|
41 |
|
if (!is_object($value)) { |
156
|
1 |
|
throw new InvalidTypeException($context, Types::TYPE_OBJECT); |
157
|
|
|
} |
158
|
|
|
|
159
|
40 |
|
$walker->parseSchema($value, $context); |
160
|
40 |
|
$context->leaveNode(); |
161
|
115 |
|
} |
162
|
115 |
|
} |
163
|
|
|
} |
164
|
|
|
|
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.