Skip to content

Commit 6cfe73f

Browse files
committed
Add CHECK_MODE_EARLY_COERCE
If set, falls back to the old behavior of coercing to the first compatible type, regardless of whether another already-valid type might be available.
1 parent d9602dd commit 6cfe73f

File tree

4 files changed

+28
-9
lines changed

4 files changed

+28
-9
lines changed

README.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -186,12 +186,17 @@ third argument to `Validator::validate()`, or can be provided as the third argum
186186
| `Constraint::CHECK_MODE_NORMAL` | Validate in 'normal' mode - this is the default |
187187
| `Constraint::CHECK_MODE_TYPE_CAST` | Enable fuzzy type checking for associative arrays and objects |
188188
| `Constraint::CHECK_MODE_COERCE_TYPES` | Convert data types to match the schema where possible |
189+
| `Constraint::CHECK_MODE_EARLY_COERCE` | Apply type coercion as soon as possible |
189190
| `Constraint::CHECK_MODE_APPLY_DEFAULTS` | Apply default values from the schema if not set |
190191
| `Constraint::CHECK_MODE_EXCEPTIONS` | Throw an exception immediately if validation fails |
191192
| `Constraint::CHECK_MODE_DISABLE_FORMAT` | Do not validate "format" constraints |
192193

193-
Please note that using `Constraint::CHECK_MODE_COERCE_TYPES` or `Constraint::CHECK_MODE_APPLY_DEFAULTS`
194-
will modify your original data.
194+
Please note that using `CHECK_MODE_COERCE_TYPES` or `CHECK_MODE_APPLY_DEFAULTS` will modify your
195+
original data.
196+
197+
`CHECK_MODE_EARLY_COERCE` has no effect unless used in combination with `CHECK_MODE_COERCE_TYPES`. If
198+
enabled, type coercion will occur as soon as possible, even if the value may already be of another valid
199+
type.
195200

196201
## Running the tests
197202

src/JsonSchema/Constraints/Constraint.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ abstract class Constraint extends BaseConstraint implements ConstraintInterface
3131
const CHECK_MODE_APPLY_DEFAULTS = 0x00000008;
3232
const CHECK_MODE_EXCEPTIONS = 0x00000010;
3333
const CHECK_MODE_DISABLE_FORMAT = 0x00000020;
34+
const CHECK_MODE_EARLY_COERCE = 0x00000040;
3435

3536
/**
3637
* Bubble down the path

src/JsonSchema/Constraints/TypeConstraint.php

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,20 +45,21 @@ public function check(&$value = null, $schema = null, JsonPointer $path = null,
4545
$type = isset($schema->type) ? $schema->type : null;
4646
$isValid = false;
4747
$coerce = $this->factory->getConfig(self::CHECK_MODE_COERCE_TYPES);
48+
$earlyCoerce = $this->factory->getConfig(self::CHECK_MODE_EARLY_COERCE);
4849
$wording = array();
4950

5051
if (is_array($type)) {
51-
$this->validateTypesArray($value, $type, $wording, $isValid, $path, false);
52-
if (!$isValid && $coerce) {
52+
$this->validateTypesArray($value, $type, $wording, $isValid, $path, $coerce && $earlyCoerce);
53+
if (!$isValid && $coerce && !$earlyCoerce) {
5354
$this->validateTypesArray($value, $type, $wording, $isValid, $path, true);
5455
}
5556
} elseif (is_object($type)) {
5657
$this->checkUndefined($value, $type, $path);
5758

5859
return;
5960
} else {
60-
$isValid = $this->validateType($value, $type, false);
61-
if (!$isValid && $coerce) {
61+
$isValid = $this->validateType($value, $type, $coerce && $earlyCoerce);
62+
if (!$isValid && $coerce && !$earlyCoerce) {
6263
$isValid = $this->validateType($value, $type, true);
6364
}
6465
}

tests/Constraints/CoerciveTest.php

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -136,12 +136,22 @@ public function dataCoerceCases()
136136
'array', 'array', array(42, '42'), true
137137
);
138138

139+
// #44 check early coercion
140+
$tests[] = array(
141+
'{"properties":{"propertyOne":{"type":["object", "number", "string"]}}}',
142+
'{"propertyOne":"42"}',
143+
'string', 'integer', 42, true, true
144+
);
145+
139146
return $tests;
140147
}
141148

142149
/** @dataProvider dataCoerceCases **/
143-
public function testCoerceCases($schema, $data, $startType, $endType, $endValue, $valid, $assoc = false)
150+
public function testCoerceCases($schema, $data, $startType, $endType, $endValue, $valid, $early = false, $assoc = false)
144151
{
152+
if ($early) {
153+
$this->factory->addConfig(Constraint::CHECK_MODE_EARLY_COERCE);
154+
}
145155
$validator = new Validator($this->factory);
146156

147157
$schema = json_decode($schema);
@@ -181,11 +191,13 @@ public function testCoerceCases($schema, $data, $startType, $endType, $endValue,
181191
$this->assertFalse($validator->isValid(), 'Validation succeeded, but should have failed');
182192
$this->assertEquals(1, count($validator->getErrors()));
183193
}
194+
195+
$this->factory->removeConfig(Constraint::CHECK_MODE_EARLY_COERCE);
184196
}
185197

186198
/** @dataProvider dataCoerceCases **/
187-
public function testCoerceCasesUsingAssoc($schema, $data, $startType, $endType, $endValue, $valid)
199+
public function testCoerceCasesUsingAssoc($schema, $data, $startType, $endType, $endValue, $valid, $early = false)
188200
{
189-
$this->testCoerceCases($schema, $data, $startType, $endType, $endValue, $valid, true);
201+
$this->testCoerceCases($schema, $data, $startType, $endType, $endValue, $valid, $early, true);
190202
}
191203
}

0 commit comments

Comments
 (0)