Skip to content

Commit c9e1276

Browse files
committed
Add remaining coercion cases from ajv matrix
1 parent 79b5881 commit c9e1276

File tree

3 files changed

+181
-27
lines changed

3 files changed

+181
-27
lines changed

src/JsonSchema/Constraints/TypeConstraint.php

Lines changed: 74 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,8 @@ public function check(&$value = null, $schema = null, JsonPointer $path = null,
6969
$wording[] = self::$wording[$type];
7070
}
7171
$this->addError(ConstraintError::TYPE(), $path, array(
72-
'expected' => gettype($value),
73-
'found' => $this->implodeWith($wording, ', ', 'or')
72+
'found' => gettype($value),
73+
'expected' => $this->implodeWith($wording, ', ', 'or')
7474
));
7575
}
7676
}
@@ -185,6 +185,10 @@ protected function validateType(&$value, $type, $coerce = false)
185185
}
186186

187187
if ('array' === $type) {
188+
if ($coerce) {
189+
$value = $this->toArray($value);
190+
}
191+
188192
return $this->getTypeCheck()->isArray($value);
189193
}
190194

@@ -213,6 +217,10 @@ protected function validateType(&$value, $type, $coerce = false)
213217
}
214218

215219
if ('string' === $type) {
220+
if ($coerce) {
221+
$value = $this->toString($value);
222+
}
223+
216224
return is_string($value);
217225
}
218226

@@ -236,19 +244,21 @@ protected function validateType(&$value, $type, $coerce = false)
236244
*/
237245
protected function toBoolean($value)
238246
{
239-
if ($value === 'true') {
247+
if ($value === 1 || $value === 'true') {
240248
return true;
241249
}
242-
243-
if ($value === 'false') {
250+
if (is_null($value) || $value === 0 || $value === 'false') {
244251
return false;
245252
}
253+
if ($this->getTypeCheck()->isArray($value) && count($value) === 1) {
254+
return $this->toBoolean(reset($value));
255+
}
246256

247257
return $value;
248258
}
249259

250260
/**
251-
* Converts a numeric string to a number. For example, "4" becomes 4.
261+
* Converts a numeric string to a number. For example, "4.5" becomes 4.5.
252262
*
253263
* @param mixed $value the value to convert to a number
254264
*
@@ -259,16 +269,72 @@ protected function toNumber($value)
259269
if (is_numeric($value)) {
260270
return $value + 0; // cast to number
261271
}
272+
if (is_bool($value) || is_null($value)) {
273+
return (int) $value;
274+
}
275+
if ($this->getTypeCheck()->isArray($value) && count($value) === 1) {
276+
return $this->toNumber(reset($value));
277+
}
262278

263279
return $value;
264280
}
265281

282+
/**
283+
* Converts a value to an integer. For example, "4" becomes 4.
284+
*
285+
* @param mixed $value
286+
*
287+
* @return int|mixed
288+
*/
266289
protected function toInteger($value)
267290
{
268-
if (is_numeric($value) && (int) $value == $value) {
269-
return (int) $value; // cast to number
291+
$numberValue = $this->toNumber($value);
292+
if (is_numeric($numberValue) && (int) $numberValue == $numberValue) {
293+
return (int) $numberValue; // cast to number
270294
}
271295

272296
return $value;
273297
}
298+
299+
/**
300+
* Converts a value to an array containing that value. For example, [4] becomes 4.
301+
*
302+
* @param mixed $value
303+
*
304+
* @return array|mixed
305+
*/
306+
protected function toArray($value)
307+
{
308+
if (is_scalar($value) || is_null($value)) {
309+
return array($value);
310+
}
311+
312+
return $value;
313+
}
314+
315+
/**
316+
* Convert a value to a string representation of that value. For example, null becomes "".
317+
*
318+
* @param mixed $value
319+
*
320+
* @return string|mixed
321+
*/
322+
protected function toString($value)
323+
{
324+
if (is_numeric($value)) {
325+
return "$value";
326+
}
327+
if ($value === true) {
328+
return 'true';
329+
}
330+
if ($value === false) {
331+
return 'false';
332+
}
333+
if (is_null($value)) {
334+
return '';
335+
}
336+
if ($this->getTypeCheck()->isArray($value) && count($value) === 1) {
337+
return $this->toString(reset($value));
338+
}
339+
}
274340
}

tests/Constraints/CoerciveTest.php

Lines changed: 103 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -48,31 +48,75 @@ public function testValidCoerceCases($input, $schema, $errors = array())
4848
$validator = new Validator(new Factory($schemaStorage, null, $checkMode));
4949
$value = json_decode($input);
5050

51+
$this->assertTrue(gettype($value->string) == 'string');
52+
$this->assertTrue(gettype($value->numberString) == 'integer');
53+
$this->assertTrue(gettype($value->boolString) == 'boolean');
54+
$this->assertTrue(gettype($value->boolFalseString) == 'boolean');
55+
$this->assertTrue(gettype($value->nullString) == 'NULL');
56+
$this->assertTrue(gettype($value->arrayString) == 'array');
5157
$this->assertTrue(gettype($value->number) == 'string');
58+
$this->assertTrue(gettype($value->boolNumber) == 'boolean');
59+
$this->assertTrue(gettype($value->nullNumber) == 'NULL');
60+
$this->assertTrue(gettype($value->arrayNumber) == 'array');
5261
$this->assertTrue(gettype($value->integer) == 'string');
62+
$this->assertTrue(gettype($value->negativeInteger) == 'string');
5363
$this->assertTrue(gettype($value->boolean) == 'string');
64+
$this->assertTrue(gettype($value->numberBoolean) == 'integer');
65+
$this->assertTrue(gettype($value->nullBoolean) == 'NULL');
66+
$this->assertTrue(gettype($value->arrayBoolean) == 'array');
67+
$this->assertTrue(gettype($value->array) == 'string');
68+
$this->assertTrue(gettype($value->numberArray) == 'integer');
69+
$this->assertTrue(gettype($value->boolArray) == 'boolean');
70+
$this->assertTrue(gettype($value->nullArray) == 'NULL');
5471

5572
$validator->validate($value, $schema, $checkMode);
5673

74+
$this->assertTrue(gettype($value->string) == 'string');
75+
$this->assertTrue(gettype($value->numberString) == 'string');
76+
$this->assertTrue(gettype($value->boolString) == 'string');
77+
$this->assertTrue(gettype($value->boolFalseString) == 'string');
78+
$this->assertTrue(gettype($value->nullString) == 'string');
79+
$this->assertTrue(gettype($value->arrayString) == 'string');
5780
$this->assertTrue(gettype($value->number) == 'double');
81+
$this->assertTrue(gettype($value->boolNumber) == 'integer');
82+
$this->assertTrue(gettype($value->nullNumber) == 'integer');
83+
$this->assertTrue(gettype($value->arrayNumber) == 'double');
5884
$this->assertTrue(gettype($value->integer) == 'integer');
5985
$this->assertTrue(gettype($value->negativeInteger) == 'integer');
6086
$this->assertTrue(gettype($value->boolean) == 'boolean');
61-
62-
$this->assertTrue($value->number === 1.5);
63-
$this->assertTrue($value->integer === 1);
64-
$this->assertTrue($value->negativeInteger === -2);
65-
$this->assertTrue($value->boolean === true);
87+
$this->assertTrue(gettype($value->numberBoolean) == 'boolean');
88+
$this->assertTrue(gettype($value->nullBoolean) == 'boolean');
89+
$this->assertTrue(gettype($value->arrayBoolean) == 'boolean');
90+
$this->assertTrue(gettype($value->array) == 'array');
91+
$this->assertTrue(gettype($value->numberArray) == 'array');
92+
$this->assertTrue(gettype($value->boolArray) == 'array');
93+
$this->assertTrue(gettype($value->nullArray) == 'array');
6694

6795
$this->assertTrue(gettype($value->multitype1) == 'boolean');
6896
$this->assertTrue(gettype($value->multitype2) == 'double');
6997
$this->assertTrue(gettype($value->multitype3) == 'integer');
7098
$this->assertTrue(gettype($value->multitype4) == 'string');
7199

100+
$this->assertTrue($value->string === 'string test');
101+
$this->assertTrue($value->numberString === '45');
102+
$this->assertTrue($value->boolString === 'true');
103+
$this->assertTrue($value->boolFalseString === 'false');
104+
$this->assertTrue($value->nullString === '');
105+
$this->assertTrue($value->arrayString === '45');
72106
$this->assertTrue($value->number === 1.5);
107+
$this->assertTrue($value->boolNumber === 1);
108+
$this->assertTrue($value->nullNumber === 0);
109+
$this->assertTrue($value->arrayNumber === 1.5);
73110
$this->assertTrue($value->integer === 1);
74111
$this->assertTrue($value->negativeInteger === -2);
75112
$this->assertTrue($value->boolean === true);
113+
$this->assertTrue($value->numberBoolean === true);
114+
$this->assertTrue($value->nullBoolean === false);
115+
$this->assertTrue($value->arrayBoolean === true);
116+
$this->assertTrue($value->array === array('string'));
117+
$this->assertTrue($value->numberArray === array(45));
118+
$this->assertTrue($value->boolArray === array(true));
119+
$this->assertTrue($value->nullArray === array(null));
76120

77121
$this->assertTrue($validator->isValid(), print_r($validator->getErrors(), true));
78122
}
@@ -123,12 +167,26 @@ public function getValidCoerceTests()
123167
array(
124168
'{
125169
"string":"string test",
170+
"numberString":45,
171+
"boolString":true,
172+
"boolFalseString":false,
173+
"nullString":null,
174+
"arrayString":[45],
126175
"number":"1.5",
176+
"boolNumber":true,
177+
"nullNumber":null,
178+
"arrayNumber":["1.5"],
127179
"integer":"1",
128180
"negativeInteger":"-2",
129181
"boolean":"true",
182+
"numberBoolean":1,
183+
"nullBoolean":null,
184+
"arrayBoolean":["true"],
130185
"object":{},
131-
"array":[],
186+
"array":"string",
187+
"numberArray":45,
188+
"boolArray":true,
189+
"nullArray":null,
132190
"null":null,
133191
"any": "string",
134192
"allOf": "1",
@@ -149,12 +207,26 @@ public function getValidCoerceTests()
149207
"type":"object",
150208
"properties":{
151209
"string":{"type":"string"},
210+
"numberString":{"type":"string"},
211+
"boolString":{"type":"string"},
212+
"boolFalseString":{"type":"string"},
213+
"nullString":{"type":"string"},
214+
"arrayString":{"type":"string"},
152215
"number":{"type":"number"},
216+
"boolNumber":{"type":"number"},
217+
"nullNumber":{"type":"number"},
218+
"arrayNumber":{"type":"number"},
153219
"integer":{"type":"integer"},
154220
"negativeInteger":{"type":"integer"},
155221
"boolean":{"type":"boolean"},
222+
"numberBoolean":{"type":"boolean"},
223+
"nullBoolean":{"type":"boolean"},
224+
"arrayBoolean":{"type":"boolean"},
156225
"object":{"type":"object"},
157226
"array":{"type":"array"},
227+
"numberArray":{"type":"array"},
228+
"boolArray":{"type":"array"},
229+
"nullArray":{"type":"array"},
158230
"null":{"type":"null"},
159231
"any": {"type":"any"},
160232
"allOf" : {"allOf":[{
@@ -195,9 +267,11 @@ public function getValidCoerceTests()
195267
public function getInvalidCoerceTests()
196268
{
197269
return array(
198-
array(
270+
array( // #0
199271
'{
200-
"string":null
272+
"string":{
273+
"I am":"an object!"
274+
}
201275
}',
202276
'{
203277
"type":"object",
@@ -207,7 +281,7 @@ public function getInvalidCoerceTests()
207281
"additionalProperties":false
208282
}',
209283
),
210-
array(
284+
array( // #1
211285
'{
212286
"number":"five"
213287
}',
@@ -219,7 +293,7 @@ public function getInvalidCoerceTests()
219293
"additionalProperties":false
220294
}',
221295
),
222-
array(
296+
array( // #2
223297
'{
224298
"integer":"5.2"
225299
}',
@@ -231,7 +305,7 @@ public function getInvalidCoerceTests()
231305
"additionalProperties":false
232306
}',
233307
),
234-
array(
308+
array( // #3
235309
'{
236310
"boolean":"0"
237311
}',
@@ -243,7 +317,7 @@ public function getInvalidCoerceTests()
243317
"additionalProperties":false
244318
}',
245319
),
246-
array(
320+
array( // #4
247321
'{
248322
"object":null
249323
}',
@@ -255,9 +329,11 @@ public function getInvalidCoerceTests()
255329
"additionalProperties":false
256330
}',
257331
),
258-
array(
332+
array( // #5
259333
'{
260-
"array":null
334+
"array":{
335+
"string":"string"
336+
}
261337
}',
262338
'{
263339
"type":"object",
@@ -267,7 +343,7 @@ public function getInvalidCoerceTests()
267343
"additionalProperties":false
268344
}',
269345
),
270-
array(
346+
array( // #6
271347
'{
272348
"null":1
273349
}',
@@ -279,6 +355,18 @@ public function getInvalidCoerceTests()
279355
"additionalProperties":false
280356
}',
281357
),
358+
array( // #7
359+
'{
360+
"integer":"5f"
361+
}',
362+
'{
363+
"type":"object",
364+
"properties": {
365+
"integer":{"type":"integer"}
366+
},
367+
"additionalProperties":false
368+
}',
369+
),
282370
);
283371
}
284372
}

tests/Constraints/OfPropertiesTest.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,8 @@ public function getInvalidTests()
7878
'constraint' => array(
7979
'name' => 'type',
8080
'params' => array(
81-
'expected' => 'array',
82-
'found' => 'a string'
81+
'expected' => 'a string',
82+
'found' => 'array'
8383
)
8484
)
8585
),
@@ -90,8 +90,8 @@ public function getInvalidTests()
9090
'constraint' => array(
9191
'name' => 'type',
9292
'params' => array(
93-
'expected' => 'array',
94-
'found' => 'a number'
93+
'expected' => 'a number',
94+
'found' => 'array'
9595
)
9696
)
9797
),

0 commit comments

Comments
 (0)