Skip to content
This repository was archived by the owner on Jul 15, 2021. It is now read-only.

Commit 9511c40

Browse files
author
JSchneider
committed
CUR-1567: add PR jsonrainbow#581
1 parent c41b93a commit 9511c40

File tree

3 files changed

+266
-1
lines changed

3 files changed

+266
-1
lines changed

src/JsonSchema/ConstraintError.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@ class ConstraintError extends Enum
99
const ADDITIONAL_ITEMS = 'additionalItems';
1010
const ADDITIONAL_PROPERTIES = 'additionalProp';
1111
const ALL_OF = 'allOf';
12+
const ALWAYS_FAILS = 'alwaysFails';
1213
const ANY_OF = 'anyOf';
14+
const CONDITIONAL_IF = 'if';
15+
const CONDITIONAL_THEN = 'then';
16+
const CONDITIONAL_ELSE = 'else';
1317
const DEPENDENCIES = 'dependencies';
1418
const DISALLOW = 'disallow';
1519
const DIVISIBLE_BY = 'divisibleBy';
@@ -59,12 +63,16 @@ public function getMessage()
5963
self::ADDITIONAL_ITEMS => 'The item %s[%s] is not defined and the definition does not allow additional items',
6064
self::ADDITIONAL_PROPERTIES => 'The property %s is not defined and the definition does not allow additional properties',
6165
self::ALL_OF => 'Failed to match all schemas',
66+
self::ALWAYS_FAILS => 'Schema always fails validation',
6267
self::ANY_OF => 'Failed to match at least one schema',
6368
self::DEPENDENCIES => '%s depends on %s, which is missing',
6469
self::DISALLOW => 'Disallowed value was matched',
6570
self::DIVISIBLE_BY => 'Is not divisible by %d',
6671
self::ENUM => 'Does not have a value in the enumeration %s',
6772
self::CONSTANT => 'Does not have a value equal to %s',
73+
self::CONDITIONAL_IF => 'The keyword "if" must be a boolean or an object',
74+
self::CONDITIONAL_THEN => 'The keyword "then" must be a boolean or an object',
75+
self::CONDITIONAL_ELSE => 'The keyword "else" must be a boolean or an object',
6876
self::EXCLUSIVE_MINIMUM => 'Must have a minimum value greater than %d',
6977
self::EXCLUSIVE_MAXIMUM => 'Must have a maximum value less than %d',
7078
self::FORMAT_COLOR => 'Invalid color',

src/JsonSchema/Constraints/UndefinedConstraint.php

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use JsonSchema\Entity\JsonPointer;
1515
use JsonSchema\Exception\ValidationException;
1616
use JsonSchema\Uri\UriResolver;
17+
use JsonSchema\Validator;
1718

1819
/**
1920
* The UndefinedConstraint Constraints
@@ -45,7 +46,7 @@ public function check(&$value, $schema = null, JsonPointer $path = null, $i = nu
4546
// check special properties
4647
$this->validateCommonProperties($value, $schema, $path, $i);
4748

48-
// check allOf, anyOf, and oneOf properties
49+
// check allOf, anyOf, oneOf, if, then, and else properties
4950
$this->validateOfProperties($value, $schema, $path, '');
5051

5152
// check known types
@@ -372,6 +373,34 @@ protected function validateOfProperties(&$value, $schema, JsonPointer $path, $i
372373
$this->errors = $startErrors;
373374
}
374375
}
376+
377+
if (isset($schema->if)) {
378+
if (!is_bool($schema->if) && !is_object($schema->if)) {
379+
$this->addError(ConstraintError::CONDITIONAL_IF(), $path);
380+
}
381+
$validator = new Validator();
382+
if ($schema->if !== false && Validator::ERROR_NONE === $validator->validate($value, $schema->if)) {
383+
if (isset($schema->then)) {
384+
if (!is_bool($schema->then) && !is_object($schema->then)) {
385+
$this->addError(ConstraintError::CONDITIONAL_THEN(), $path);
386+
}
387+
if ($schema->then === false) {
388+
$this->addError(ConstraintError::ALWAYS_FAILS(), $path);
389+
} else {
390+
$this->check($value, $schema->then);
391+
}
392+
}
393+
} elseif (isset($schema->else)) {
394+
if (!is_bool($schema->else) && !is_object($schema->else)) {
395+
$this->addError(ConstraintError::CONDITIONAL_ELSE(), $path);
396+
}
397+
if ($schema->else === false) {
398+
$this->addError(ConstraintError::ALWAYS_FAILS(), $path);
399+
} else {
400+
$this->check($value, $schema->else);
401+
}
402+
}
403+
}
375404
}
376405

377406
/**

tests/Constraints/IfThenElseTest.php

Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
<?php
2+
/*
3+
* This file is part of the JsonSchema package.
4+
*
5+
* For the full copyright and license information, please view the LICENSE
6+
* file that was distributed with this source code.
7+
*/
8+
namespace JsonSchema\Tests\Constraints;
9+
10+
class IfThenElseTest extends BaseTestCase
11+
{
12+
protected $validateSchema = true;
13+
14+
public function getInvalidTests()
15+
{
16+
return array(
17+
// If "foo" === "bar", then "bar" must be defined, else Validation Failed.
18+
// But "foo" === "bar" and "bar" is not defined.
19+
array(
20+
'{
21+
"foo":"bar"
22+
}',
23+
'{
24+
"type": "object",
25+
"properties": {
26+
"foo": {"type": "string"},
27+
"bar": {"type": "string"}
28+
},
29+
"if": {
30+
"properties": {"foo": {"enum": ["bar"]}},
31+
"required": ["foo"]
32+
},
33+
"then": {"required": ["bar"]},
34+
"else": false
35+
}'
36+
),
37+
// If "foo" === "bar", then "bar" must be defined, else Validation Failed.
38+
// But "foo" !== "bar".
39+
array(
40+
'{
41+
"foo":"baz"
42+
}',
43+
'{
44+
"type": "object",
45+
"properties": {
46+
"foo": {"type": "string"},
47+
"bar": {"type": "string"}
48+
},
49+
"if": {
50+
"properties": {"foo": {"enum": ["bar"]}},
51+
"required": ["foo"]
52+
},
53+
"then": {"required": ["bar"]},
54+
"else": false
55+
}'
56+
),
57+
// If "foo" === "bar", then "bar" must === "baz", else Validation Failed.
58+
// But "foo" === "bar" and "bar" !== "baz".
59+
array(
60+
'{
61+
"foo":"bar",
62+
"bar":"potato"
63+
}',
64+
'{
65+
"type": "object",
66+
"properties": {
67+
"foo": {"type": "string"},
68+
"bar": {"type": "string"}
69+
},
70+
"if": {
71+
"properties": {"foo": {"enum": ["bar"]}},
72+
"required": ["foo"]
73+
},
74+
"then": {
75+
"properties": {"bar": {"enum": ["baz"]}},
76+
"required": ["bar"]
77+
},
78+
"else": false
79+
}'
80+
),
81+
// Always go to "else".
82+
// But schema is invalid.
83+
array(
84+
'{
85+
"foo":"bar"
86+
}',
87+
'{
88+
"type": "object",
89+
"properties": {
90+
"foo": {"type": "string"},
91+
"bar": {"type": "string"}
92+
},
93+
"if": false,
94+
"then": true,
95+
"else": {
96+
"properties": {"bar": {"enum": ["baz"]}},
97+
"required": ["bar"]
98+
}
99+
}'
100+
),
101+
// Always go to "then".
102+
// But schema is invalid.
103+
array(
104+
'{
105+
"foo":"bar"
106+
}',
107+
'{
108+
"type": "object",
109+
"properties": {
110+
"foo": {"type": "string"},
111+
"bar": {"type": "string"}
112+
},
113+
"if": true,
114+
"then": {
115+
"properties": {"bar": {"enum": ["baz"]}},
116+
"required": ["bar"]
117+
},
118+
"else": true
119+
}'
120+
)
121+
);
122+
}
123+
public function getValidTests()
124+
{
125+
return array(
126+
// Always validate.
127+
array(
128+
'{
129+
"foo":"bar"
130+
}',
131+
'{
132+
"type": "object",
133+
"properties": {
134+
"foo": {"type": "string"},
135+
"bar": {"type": "string"}
136+
},
137+
"if": true,
138+
"then": true,
139+
"else": false
140+
}'
141+
),
142+
// Always validate schema in then.
143+
array(
144+
'{
145+
"foo":"bar"
146+
}',
147+
'{
148+
"type": "object",
149+
"properties": {
150+
"foo": {"type": "string"},
151+
"bar": {"type": "string"}
152+
},
153+
"if": true,
154+
"then": {
155+
"properties": {"foo": {"enum": ["bar"]}},
156+
"required": ["foo"]
157+
},
158+
"else": false
159+
}'
160+
),
161+
// Always validate schema in else.
162+
array(
163+
'{
164+
"foo":"bar"
165+
}',
166+
'{
167+
"type": "object",
168+
"properties": {
169+
"foo": {"type": "string"},
170+
"bar": {"type": "string"}
171+
},
172+
"if": false,
173+
"then": false,
174+
"else": {
175+
"properties": {"foo": {"enum": ["bar"]}},
176+
"required": ["foo"]
177+
}
178+
}'
179+
),
180+
// "If" is evaluated to true, so "then" is to validate.
181+
array(
182+
'{
183+
"foo":"bar",
184+
"bar":"baz"
185+
}',
186+
'{
187+
"type": "object",
188+
"properties": {
189+
"foo": {"type": "string"},
190+
"bar": {"type": "string"}
191+
},
192+
"if": {
193+
"properties": {"foo": {"enum": ["bar"]}},
194+
"required": ["foo"]
195+
},
196+
"then": {
197+
"properties": {"bar": {"enum": ["baz"]}},
198+
"required": ["bar"]
199+
},
200+
"else": false
201+
}'
202+
),
203+
// "If" is evaluated to false, so "else" is to validate.
204+
array(
205+
'{
206+
"foo":"bar",
207+
"bar":"baz"
208+
}',
209+
'{
210+
"type": "object",
211+
"properties": {
212+
"foo": {"type": "string"},
213+
"bar": {"type": "string"}
214+
},
215+
"if": {
216+
"properties": {"foo": {"enum": ["potato"]}},
217+
"required": ["foo"]
218+
},
219+
"then": false,
220+
"else": {
221+
"properties": {"bar": {"enum": ["baz"]}},
222+
"required": ["bar"]
223+
}
224+
}'
225+
),
226+
);
227+
}
228+
}

0 commit comments

Comments
 (0)