@@ -44,16 +44,24 @@ public function check(&$value = null, $schema = null, JsonPointer $path = null,
44
44
{
45
45
$ type = isset ($ schema ->type ) ? $ schema ->type : null ;
46
46
$ isValid = false ;
47
+ $ coerce = $ this ->factory ->getConfig (self ::CHECK_MODE_COERCE_TYPES );
48
+ $ earlyCoerce = $ this ->factory ->getConfig (self ::CHECK_MODE_EARLY_COERCE );
47
49
$ wording = array ();
48
50
49
51
if (is_array ($ type )) {
50
- $ this ->validateTypesArray ($ value , $ type , $ wording , $ isValid , $ path );
52
+ $ this ->validateTypesArray ($ value , $ type , $ wording , $ isValid , $ path , $ coerce && $ earlyCoerce );
53
+ if (!$ isValid && $ coerce && !$ earlyCoerce ) {
54
+ $ this ->validateTypesArray ($ value , $ type , $ wording , $ isValid , $ path , true );
55
+ }
51
56
} elseif (is_object ($ type )) {
52
57
$ this ->checkUndefined ($ value , $ type , $ path );
53
58
54
59
return ;
55
60
} else {
56
- $ isValid = $ this ->validateType ($ value , $ type );
61
+ $ isValid = $ this ->validateType ($ value , $ type , $ coerce && $ earlyCoerce );
62
+ if (!$ isValid && $ coerce && !$ earlyCoerce ) {
63
+ $ isValid = $ this ->validateType ($ value , $ type , true );
64
+ }
57
65
}
58
66
59
67
if ($ isValid === false ) {
@@ -62,8 +70,8 @@ public function check(&$value = null, $schema = null, JsonPointer $path = null,
62
70
$ wording [] = self ::$ wording [$ type ];
63
71
}
64
72
$ this ->addError (ConstraintError::TYPE (), $ path , array (
65
- 'expected ' => gettype ($ value ),
66
- 'found ' => $ this ->implodeWith ($ wording , ', ' , 'or ' )
73
+ 'found ' => gettype ($ value ),
74
+ 'expected ' => $ this ->implodeWith ($ wording , ', ' , 'or ' )
67
75
));
68
76
}
69
77
}
@@ -79,9 +87,14 @@ public function check(&$value = null, $schema = null, JsonPointer $path = null,
79
87
* @param bool $isValid The current validation value
80
88
* @param $path
81
89
*/
82
- protected function validateTypesArray (&$ value , array $ type , &$ validTypesWording , &$ isValid , $ path )
90
+ protected function validateTypesArray (&$ value , array $ type , &$ validTypesWording , &$ isValid , $ path, $ coerce = false )
83
91
{
84
92
foreach ($ type as $ tp ) {
93
+ // already valid, so no need to waste cycles looping over everything
94
+ if ($ isValid ) {
95
+ return ;
96
+ }
97
+
85
98
// $tp can be an object, if it's a schema instead of a simple type, validate it
86
99
// with a new type constraint
87
100
if (is_object ($ tp )) {
@@ -98,7 +111,7 @@ protected function validateTypesArray(&$value, array $type, &$validTypesWording,
98
111
$ this ->validateTypeNameWording ($ tp );
99
112
$ validTypesWording [] = self ::$ wording [$ tp ];
100
113
if (!$ isValid ) {
101
- $ isValid = $ this ->validateType ($ value , $ tp );
114
+ $ isValid = $ this ->validateType ($ value , $ tp, $ coerce );
102
115
}
103
116
}
104
117
}
@@ -157,7 +170,7 @@ protected function validateTypeNameWording($type)
157
170
*
158
171
* @return bool
159
172
*/
160
- protected function validateType (&$ value , $ type )
173
+ protected function validateType (&$ value , $ type, $ coerce = false )
161
174
{
162
175
//mostly the case for inline schema
163
176
if (!$ type ) {
@@ -173,11 +186,13 @@ protected function validateType(&$value, $type)
173
186
}
174
187
175
188
if ('array ' === $ type ) {
189
+ if ($ coerce ) {
190
+ $ value = $ this ->toArray ($ value );
191
+ }
192
+
176
193
return $ this ->getTypeCheck ()->isArray ($ value );
177
194
}
178
195
179
- $ coerce = $ this ->factory ->getConfig (Constraint::CHECK_MODE_COERCE_TYPES );
180
-
181
196
if ('integer ' === $ type ) {
182
197
if ($ coerce ) {
183
198
$ value = $ this ->toInteger ($ value );
@@ -203,10 +218,18 @@ protected function validateType(&$value, $type)
203
218
}
204
219
205
220
if ('string ' === $ type ) {
221
+ if ($ coerce ) {
222
+ $ value = $ this ->toString ($ value );
223
+ }
224
+
206
225
return is_string ($ value );
207
226
}
208
227
209
228
if ('null ' === $ type ) {
229
+ if ($ coerce ) {
230
+ $ value = $ this ->toNull ($ value );
231
+ }
232
+
210
233
return is_null ($ value );
211
234
}
212
235
@@ -222,19 +245,21 @@ protected function validateType(&$value, $type)
222
245
*/
223
246
protected function toBoolean ($ value )
224
247
{
225
- if ($ value === 'true ' ) {
248
+ if ($ value === 1 || $ value === 'true ' ) {
226
249
return true ;
227
250
}
228
-
229
- if ($ value === 'false ' ) {
251
+ if (is_null ($ value ) || $ value === 0 || $ value === 'false ' ) {
230
252
return false ;
231
253
}
254
+ if ($ this ->getTypeCheck ()->isArray ($ value ) && count ($ value ) === 1 ) {
255
+ return $ this ->toBoolean (reset ($ value ));
256
+ }
232
257
233
258
return $ value ;
234
259
}
235
260
236
261
/**
237
- * Converts a numeric string to a number. For example, "4" becomes 4.
262
+ * Converts a value to a number. For example, "4.5 " becomes 4.5 .
238
263
*
239
264
* @param mixed $value the value to convert to a number
240
265
*
@@ -245,14 +270,89 @@ protected function toNumber($value)
245
270
if (is_numeric ($ value )) {
246
271
return $ value + 0 ; // cast to number
247
272
}
273
+ if (is_bool ($ value ) || is_null ($ value )) {
274
+ return (int ) $ value ;
275
+ }
276
+ if ($ this ->getTypeCheck ()->isArray ($ value ) && count ($ value ) === 1 ) {
277
+ return $ this ->toNumber (reset ($ value ));
278
+ }
248
279
249
280
return $ value ;
250
281
}
251
282
283
+ /**
284
+ * Converts a value to an integer. For example, "4" becomes 4.
285
+ *
286
+ * @param mixed $value
287
+ *
288
+ * @return int|mixed
289
+ */
252
290
protected function toInteger ($ value )
253
291
{
254
- if (is_numeric ($ value ) && (int ) $ value == $ value ) {
255
- return (int ) $ value ; // cast to number
292
+ $ numberValue = $ this ->toNumber ($ value );
293
+ if (is_numeric ($ numberValue ) && (int ) $ numberValue == $ numberValue ) {
294
+ return (int ) $ numberValue ; // cast to number
295
+ }
296
+
297
+ return $ value ;
298
+ }
299
+
300
+ /**
301
+ * Converts a value to an array containing that value. For example, [4] becomes 4.
302
+ *
303
+ * @param mixed $value
304
+ *
305
+ * @return array|mixed
306
+ */
307
+ protected function toArray ($ value )
308
+ {
309
+ if (is_scalar ($ value ) || is_null ($ value )) {
310
+ return array ($ value );
311
+ }
312
+
313
+ return $ value ;
314
+ }
315
+
316
+ /**
317
+ * Convert a value to a string representation of that value. For example, null becomes "".
318
+ *
319
+ * @param mixed $value
320
+ *
321
+ * @return string|mixed
322
+ */
323
+ protected function toString ($ value )
324
+ {
325
+ if (is_numeric ($ value )) {
326
+ return "$ value " ;
327
+ }
328
+ if ($ value === true ) {
329
+ return 'true ' ;
330
+ }
331
+ if ($ value === false ) {
332
+ return 'false ' ;
333
+ }
334
+ if (is_null ($ value )) {
335
+ return '' ;
336
+ }
337
+ if ($ this ->getTypeCheck ()->isArray ($ value ) && count ($ value ) === 1 ) {
338
+ return $ this ->toString (reset ($ value ));
339
+ }
340
+ }
341
+
342
+ /**
343
+ * Convert a value to a null. For example, 0 becomes null.
344
+ *
345
+ * @param mixed $value
346
+ *
347
+ * @return null|mixed
348
+ */
349
+ protected function toNull ($ value )
350
+ {
351
+ if ($ value === 0 || $ value === false || $ value === '' ) {
352
+ return null ;
353
+ }
354
+ if ($ this ->getTypeCheck ()->isArray ($ value ) && count ($ value ) === 1 ) {
355
+ return $ this ->toNull (reset ($ value ));
256
356
}
257
357
258
358
return $ value ;
0 commit comments