Skip to content

Commit 6f877ff

Browse files
committed
Merge pull request #49 from mchiocca/master
Support for Draft v4 multipleOf, not, allOf, anyOf, and oneOf keywords.
2 parents 3ec2db5 + 5fa70ed commit 6f877ff

File tree

4 files changed

+167
-18
lines changed

4 files changed

+167
-18
lines changed

src/JsonSchema/Constraints/Number.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,16 @@ public function check($element, $schema = null, $path = null, $i = null)
5252
$this->addError($path, "must have a maximum value of " . $schema->maximum);
5353
}
5454

55-
// Verify divisibleBy
55+
// Verify divisibleBy - Draft v3
5656
if (isset($schema->divisibleBy) && $this->fmod($element, $schema->divisibleBy) != 0) {
5757
$this->addError($path, "is not divisible by " . $schema->divisibleBy);
5858
}
5959

60+
// Verify multipleOf - Draft v4
61+
if (isset($schema->multipleOf) && $this->fmod($element, $schema->multipleOf) != 0) {
62+
$this->addError($path, "must be a multiple of " . $schema->multipleOf);
63+
}
64+
6065
$this->checkFormat($element, $schema, $path, $i);
6166
}
6267

src/JsonSchema/Constraints/Undefined.php

Lines changed: 88 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010
namespace JsonSchema\Constraints;
1111

12+
use JsonSchema\Uri\UriResolver;
13+
1214
/**
1315
* The Undefined Constraints
1416
*
@@ -26,11 +28,15 @@ public function check($value, $schema = null, $path = null, $i = null)
2628
return;
2729
}
2830

31+
$i = is_null($i) ? "" : $i;
2932
$path = $this->incrementPath($path, $i);
3033

3134
// check special properties
3235
$this->validateCommonProperties($value, $schema, $path);
3336

37+
// check allOf, anyOf, and oneOf properties
38+
$this->validateOfProperties($value, $schema, $path);
39+
3440
// check known types
3541
$this->validateTypes($value, $schema, $path, $i);
3642
}
@@ -85,20 +91,19 @@ public function validateTypes($value, $schema = null, $path = null, $i = null)
8591
* @param string $path
8692
* @param string $i
8793
*/
88-
protected function validateCommonProperties($value, $schema = null, $path = null, $i = null)
94+
protected function validateCommonProperties($value, $schema = null, $path = null, $i = "")
8995
{
9096
// if it extends another schema, it must pass that schema as well
9197
if (isset($schema->extends)) {
9298
if (is_string($schema->extends)) {
9399
$schema->extends = $this->validateUri($schema, $schema->extends);
94100
}
95-
$increment = is_null($i) ? "" : $i;
96101
if (is_array($schema->extends)) {
97102
foreach ($schema->extends as $extends) {
98-
$this->checkUndefined($value, $extends, $path, $increment);
103+
$this->checkUndefined($value, $extends, $path, $i);
99104
}
100105
} else {
101-
$this->checkUndefined($value, $schema->extends, $path, $increment);
106+
$this->checkUndefined($value, $schema->extends, $path, $i);
102107
}
103108
}
104109

@@ -133,7 +138,19 @@ protected function validateCommonProperties($value, $schema = null, $path = null
133138

134139
// if no new errors were raised it must be a disallowed value
135140
if (count($this->getErrors()) == count($initErrors)) {
136-
$this->addError($path, " disallowed value was matched");
141+
$this->addError($path, "disallowed value was matched");
142+
} else {
143+
$this->errors = $initErrors;
144+
}
145+
}
146+
147+
if (isset($schema->not)) {
148+
$initErrors = $this->getErrors();
149+
$this->checkUndefined($value, $schema->not, $path, $i);
150+
151+
// if no new errors were raised then the instance validated against the "not" schema
152+
if (count($this->getErrors()) == count($initErrors)) {
153+
$this->addError($path, "matched a schema which it should not");
137154
} else {
138155
$this->errors = $initErrors;
139156
}
@@ -143,12 +160,12 @@ protected function validateCommonProperties($value, $schema = null, $path = null
143160
if (is_object($value)) {
144161
if (isset($schema->minProperties)) {
145162
if (count(get_object_vars($value)) < $schema->minProperties) {
146-
$this->addError($path, "must contain a minimum of " + $schema->minProperties + " properties");
163+
$this->addError($path, "must contain a minimum of " . $schema->minProperties . " properties");
147164
}
148165
}
149166
if (isset($schema->maxProperties)) {
150167
if (count(get_object_vars($value)) > $schema->maxProperties) {
151-
$this->addError($path, "must contain no more than " + $schema->maxProperties + " properties");
168+
$this->addError($path, "must contain no more than " . $schema->maxProperties . " properties");
152169
}
153170
}
154171
}
@@ -159,14 +176,72 @@ protected function validateCommonProperties($value, $schema = null, $path = null
159176
}
160177
}
161178

179+
/**
180+
* Validate allOf, anyOf, and oneOf properties
181+
*
182+
* @param mixed $value
183+
* @param mixed $schema
184+
* @param string $path
185+
* @param string $i
186+
*/
187+
protected function validateOfProperties($value, $schema, $path, $i = "")
188+
{
189+
if (isset($schema->allOf)) {
190+
$isValid = true;
191+
foreach ($schema->allOf as $allOf) {
192+
$initErrors = $this->getErrors();
193+
$this->checkUndefined($value, $allOf, $path, $i);
194+
$isValid = $isValid && (count($this->getErrors()) == count($initErrors));
195+
}
196+
if (!$isValid) {
197+
$this->addError($path, "failed to match all schemas");
198+
}
199+
}
200+
201+
if (isset($schema->anyOf)) {
202+
$isValid = false;
203+
$startErrors = $this->getErrors();
204+
foreach ($schema->anyOf as $anyOf) {
205+
$initErrors = $this->getErrors();
206+
$this->checkUndefined($value, $anyOf, $path, $i);
207+
if ($isValid = (count($this->getErrors()) == count($initErrors))) {
208+
break;
209+
}
210+
}
211+
if (!$isValid) {
212+
$this->addError($path, "failed to match at least one schema");
213+
} else {
214+
$this->errors = $startErrors;
215+
}
216+
}
217+
218+
if (isset($schema->oneOf)) {
219+
$matchedSchemas = 0;
220+
$startErrors = $this->getErrors();
221+
foreach ($schema->oneOf as $oneOf) {
222+
$initErrors = $this->getErrors();
223+
$this->checkUndefined($value, $oneOf, $path, $i);
224+
if (count($this->getErrors()) == count($initErrors)) {
225+
$matchedSchemas++;
226+
}
227+
}
228+
if ($matchedSchemas !== 1) {
229+
$this->addError($path, "failed to match exactly one schema");
230+
} else {
231+
$this->errors = $startErrors;
232+
}
233+
}
234+
}
235+
162236
/**
163237
* Validate dependencies
164238
*
165239
* @param mixed $value
166240
* @param mixed $dependencies
167241
* @param string $path
242+
* @param string $i
168243
*/
169-
protected function validateDependencies($value, $dependencies, $path)
244+
protected function validateDependencies($value, $dependencies, $path, $i = "")
170245
{
171246
foreach ($dependencies as $key => $dependency) {
172247
if (property_exists($value, $key)) {
@@ -184,22 +259,23 @@ protected function validateDependencies($value, $dependencies, $path)
184259
}
185260
} else if (is_object($dependency)) {
186261
// Schema - e.g. "dependencies": {"bar": {"properties": {"foo": {...}}}}
187-
$this->checkUndefined($value, $dependency, $path, "");
262+
$this->checkUndefined($value, $dependency, $path, $i);
188263
}
189264
}
190265
}
191266
}
192267

193268
protected function validateUri($schema, $schemaUri = null)
194269
{
195-
$resolver = new \JsonSchema\Uri\UriResolver();
270+
$resolver = new UriResolver();
196271
$retriever = $this->getUriRetriever();
197272

273+
$jsonSchema = null;
198274
if ($resolver->isValid($schemaUri)) {
199275
$schemaId = property_exists($schema, 'id') ? $schema->id : null;
200276
$jsonSchema = $retriever->retrieve($schemaId, $schemaUri);
201-
202-
return $jsonSchema;
203277
}
278+
279+
return $jsonSchema;
204280
}
205281
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the JsonSchema 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 JsonSchema\Tests\Constraints;
11+
12+
class NotTest extends BaseTestCase
13+
{
14+
public function getInvalidTests()
15+
{
16+
return array(
17+
array(
18+
'{
19+
"x": [1, 2]
20+
}',
21+
'{
22+
"properties": {
23+
"x": {
24+
"not": {
25+
"type": "array",
26+
"items": {"type": "integer"},
27+
"minItems": 2
28+
}
29+
}
30+
}
31+
}'
32+
)
33+
);
34+
}
35+
36+
public function getValidTests()
37+
{
38+
return array(
39+
array(
40+
'{
41+
"x": [1]
42+
}',
43+
'{
44+
"properties": {
45+
"x": {
46+
"not": {
47+
"type": "array",
48+
"items": {"type": "integer"},
49+
"minItems": 2
50+
}
51+
}
52+
}
53+
}'
54+
),
55+
array(
56+
'{
57+
"x": ["foo", 2]
58+
}',
59+
'{
60+
"properties": {
61+
"x": {
62+
"not": {
63+
"type": "array",
64+
"items": {"type": "integer"},
65+
"minItems": 2
66+
}
67+
}
68+
}
69+
}'
70+
)
71+
);
72+
}
73+
}

tests/JsonSchema/Tests/Drafts/Draft4Test.php

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,7 @@ protected function getSkippedTests()
1616
{
1717
return array(
1818
// Not Yet Implemented
19-
'allOf.json',
20-
'anyOf.json',
2119
'definitions.json',
22-
'multipleOf.json',
23-
'not.json',
24-
'oneOf.json',
2520
// Partially Implemented
2621
'ref.json',
2722
'refRemote.json',

0 commit comments

Comments
 (0)