Skip to content

Commit 9b6ebfe

Browse files
eraydbighappyface
authored andcommitted
Refactor to simplify the public API and make adding features easier (#351)
This commit makes the following changes: * Split the Constraint class to make Validator independent from it * Add Validator::validate() as the main entry point * Turn Validator::coerce() and Validator::check() into aliases * Add Factory::setConfig(), getConfig(), addConfig() & removeConfig() * Make type-coercion a checkMode option, don't pass $coerce everywhere * Add some extra tests
1 parent 325a0f8 commit 9b6ebfe

25 files changed

+348
-272
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,4 @@ install:
2828
- travis_retry composer install --no-interaction --prefer-dist
2929

3030
script:
31-
- if [[ "$WITH_COVERAGE" == "true" ]]; then composer coverage; else composer test; fi
31+
- if [[ "$WITH_COVERAGE" == "true" ]]; then ./vendor/bin/phpunit --coverage-text; else composer test; fi
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
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\Constraints;
11+
12+
use JsonSchema\Entity\JsonPointer;
13+
14+
/**
15+
* A more basic constraint definition - used for the public
16+
* interface to avoid exposing library internals.
17+
*/
18+
class BaseConstraint
19+
{
20+
/**
21+
* @var array Errors
22+
*/
23+
protected $errors = array();
24+
25+
/**
26+
* @var Factory
27+
*/
28+
protected $factory;
29+
30+
/**
31+
* @param Factory $factory
32+
*/
33+
public function __construct(Factory $factory = null)
34+
{
35+
$this->factory = $factory ? : new Factory();
36+
}
37+
38+
/**
39+
* {@inheritDoc}
40+
*/
41+
public function addError(JsonPointer $path = null, $message, $constraint = '', array $more = null)
42+
{
43+
$error = array(
44+
'property' => $this->convertJsonPointerIntoPropertyPath($path ?: new JsonPointer('')),
45+
'pointer' => ltrim(strval($path ?: new JsonPointer('')), '#'),
46+
'message' => $message,
47+
'constraint' => $constraint,
48+
);
49+
50+
if (is_array($more) && count($more) > 0)
51+
{
52+
$error += $more;
53+
}
54+
55+
$this->errors[] = $error;
56+
}
57+
58+
/**
59+
* {@inheritDoc}
60+
*/
61+
public function addErrors(array $errors)
62+
{
63+
if ($errors) {
64+
$this->errors = array_merge($this->errors, $errors);
65+
}
66+
}
67+
68+
/**
69+
* {@inheritDoc}
70+
*/
71+
public function getErrors()
72+
{
73+
return $this->errors;
74+
}
75+
76+
/**
77+
* {@inheritDoc}
78+
*/
79+
public function isValid()
80+
{
81+
return !$this->getErrors();
82+
}
83+
84+
/**
85+
* Clears any reported errors. Should be used between
86+
* multiple validation checks.
87+
*/
88+
public function reset()
89+
{
90+
$this->errors = array();
91+
}
92+
}

src/JsonSchema/Constraints/CollectionConstraint.php

Lines changed: 22 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -23,20 +23,7 @@ class CollectionConstraint extends Constraint
2323
/**
2424
* {@inheritDoc}
2525
*/
26-
public function check($value, $schema = null, JsonPointer $path = null, $i = null)
27-
{
28-
$this->_check($value, $schema, $path, $i);
29-
}
30-
31-
/**
32-
* {@inheritDoc}
33-
*/
34-
public function coerce(&$value, $schema = null, JsonPointer $path = null, $i = null)
35-
{
36-
$this->_check($value, $schema, $path, $i, true);
37-
}
38-
39-
protected function _check(&$value, $schema = null, JsonPointer $path = null, $i = null, $coerce = false)
26+
public function check(&$value, $schema = null, JsonPointer $path = null, $i = null)
4027
{
4128
// Verify minItems
4229
if (isset($schema->minItems) && count($value) < $schema->minItems) {
@@ -61,7 +48,7 @@ protected function _check(&$value, $schema = null, JsonPointer $path = null, $i
6148

6249
// Verify items
6350
if (isset($schema->items)) {
64-
$this->validateItems($value, $schema, $path, $i, $coerce);
51+
$this->validateItems($value, $schema, $path, $i);
6552
}
6653
}
6754

@@ -72,9 +59,8 @@ protected function _check(&$value, $schema = null, JsonPointer $path = null, $i
7259
* @param \stdClass $schema
7360
* @param JsonPointer|null $path
7461
* @param string $i
75-
* @param boolean $coerce
7662
*/
77-
protected function validateItems(&$value, $schema = null, JsonPointer $path = null, $i = null, $coerce = false)
63+
protected function validateItems(&$value, $schema = null, JsonPointer $path = null, $i = null)
7864
{
7965
if (is_object($schema->items)) {
8066
// just one type definition for the whole array
@@ -90,26 +76,24 @@ protected function validateItems(&$value, $schema = null, JsonPointer $path = nu
9076
// performance optimization
9177
$type = $schema->items->type;
9278
$typeValidator = $this->factory->createInstanceFor('type');
93-
$validator = $this->factory->createInstanceFor($type === 'integer' ? 'number' : $type);
79+
$validator = $this->factory->createInstanceFor($type === 'integer' ? 'number' : $type);
9480

95-
foreach ($value as $k => $v) {
96-
$k_path = $this->incrementPath($path, $k);
97-
if($coerce) {
98-
$typeValidator->coerce($v, $schema->items, $k_path, $i);
99-
} else {
100-
$typeValidator->check($v, $schema->items, $k_path, $i);
101-
}
81+
foreach ($value as $k => &$v) {
82+
$k_path = $this->incrementPath($path, $k);
83+
$typeValidator->check($v, $schema->items, $k_path, $i);
10284

103-
$validator->check($v, $schema->items, $k_path, $i);
85+
$validator->check($v, $schema->items, $k_path, $i);
10486
}
87+
unset($v); // remove dangling reference to prevent any future bugs
88+
// caused by accidentally using $v elsewhere
10589
$this->addErrors($typeValidator->getErrors());
10690
$this->addErrors($validator->getErrors());
10791
} else {
108-
foreach ($value as $k => $v) {
92+
foreach ($value as $k => &$v) {
10993
$initErrors = $this->getErrors();
11094

11195
// First check if its defined in "items"
112-
$this->checkUndefined($v, $schema->items, $path, $k, $coerce);
96+
$this->checkUndefined($v, $schema->items, $path, $k);
11397

11498
// Recheck with "additionalItems" if the first test fails
11599
if (count($initErrors) < count($this->getErrors()) && (isset($schema->additionalItems) && $schema->additionalItems !== false)) {
@@ -124,32 +108,37 @@ protected function validateItems(&$value, $schema = null, JsonPointer $path = nu
124108
$this->errors = $initErrors;
125109
}
126110
}
111+
unset($v); // remove dangling reference to prevent any future bugs
112+
// caused by accidentally using $v elsewhere
127113
}
128114
} else {
129115
// Defined item type definitions
130-
foreach ($value as $k => $v) {
116+
foreach ($value as $k => &$v) {
131117
if (array_key_exists($k, $schema->items)) {
132-
$this->checkUndefined($v, $schema->items[$k], $path, $k, $coerce);
118+
$this->checkUndefined($v, $schema->items[$k], $path, $k);
133119
} else {
134120
// Additional items
135121
if (property_exists($schema, 'additionalItems')) {
136122
if ($schema->additionalItems !== false) {
137-
$this->checkUndefined($v, $schema->additionalItems, $path, $k, $coerce);
123+
$this->checkUndefined($v, $schema->additionalItems, $path, $k);
138124
} else {
139125
$this->addError(
140126
$path, 'The item ' . $i . '[' . $k . '] is not defined and the definition does not allow additional items', 'additionalItems', array('additionalItems' => $schema->additionalItems,));
141127
}
142128
} else {
143129
// Should be valid against an empty schema
144-
$this->checkUndefined($v, new \stdClass(), $path, $k, $coerce);
130+
$this->checkUndefined($v, new \stdClass(), $path, $k);
145131
}
146132
}
147133
}
134+
unset($v); // remove dangling reference to prevent any future bugs
135+
// caused by accidentally using $v elsewhere
148136

149137
// Treat when we have more schema definitions than values, not for empty arrays
150138
if (count($value) > 0) {
151139
for ($k = count($value); $k < count($schema->items); $k++) {
152-
$this->checkUndefined($this->factory->createInstanceFor('undefined'), $schema->items[$k], $path, $k, $coerce);
140+
$undefinedInstance = $this->factory->createInstanceFor('undefined');
141+
$this->checkUndefined($undefinedInstance, $schema->items[$k], $path, $k);
153142
}
154143
}
155144
}

src/JsonSchema/Constraints/Constraint.php

Lines changed: 14 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -20,81 +20,15 @@
2020
* @author Robert Schönthal <[email protected]>
2121
* @author Bruno Prieto Reis <[email protected]>
2222
*/
23-
abstract class Constraint implements ConstraintInterface
23+
abstract class Constraint extends BaseConstraint implements ConstraintInterface
2424
{
25-
protected $errors = array();
2625
protected $inlineSchemaProperty = '$schema';
2726

28-
const CHECK_MODE_NORMAL = 0x00000001;
29-
const CHECK_MODE_TYPE_CAST = 0x00000002;
30-
31-
/**
32-
* @var Factory
33-
*/
34-
protected $factory;
35-
36-
/**
37-
* @param Factory $factory
38-
*/
39-
public function __construct(Factory $factory = null)
40-
{
41-
$this->factory = $factory ? : new Factory();
42-
}
43-
44-
/**
45-
* {@inheritDoc}
46-
*/
47-
public function addError(JsonPointer $path = null, $message, $constraint='', array $more=null)
48-
{
49-
$error = array(
50-
'property' => $this->convertJsonPointerIntoPropertyPath($path ?: new JsonPointer('')),
51-
'pointer' => ltrim(strval($path ?: new JsonPointer('')), '#'),
52-
'message' => $message,
53-
'constraint' => $constraint,
54-
);
55-
56-
if (is_array($more) && count($more) > 0)
57-
{
58-
$error += $more;
59-
}
60-
61-
$this->errors[] = $error;
62-
}
63-
64-
/**
65-
* {@inheritDoc}
66-
*/
67-
public function addErrors(array $errors)
68-
{
69-
if ($errors) {
70-
$this->errors = array_merge($this->errors, $errors);
71-
}
72-
}
73-
74-
/**
75-
* {@inheritDoc}
76-
*/
77-
public function getErrors()
78-
{
79-
return $this->errors;
80-
}
81-
82-
/**
83-
* {@inheritDoc}
84-
*/
85-
public function isValid()
86-
{
87-
return !$this->getErrors();
88-
}
89-
90-
/**
91-
* Clears any reported errors. Should be used between
92-
* multiple validation checks.
93-
*/
94-
public function reset()
95-
{
96-
$this->errors = array();
97-
}
27+
const CHECK_MODE_NONE = 0x00000000;
28+
const CHECK_MODE_NORMAL = 0x00000001;
29+
const CHECK_MODE_TYPE_CAST = 0x00000002;
30+
const CHECK_MODE_COERCE_TYPES = 0x00000004;
31+
const CHECK_MODE_APPLY_DEFAULTS = 0x00000008;
9832

9933
/**
10034
* Bubble down the path
@@ -123,16 +57,11 @@ protected function incrementPath(JsonPointer $path = null, $i)
12357
* @param mixed $schema
12458
* @param JsonPointer|null $path
12559
* @param mixed $i
126-
* @param boolean $coerce
12760
*/
128-
protected function checkArray(&$value, $schema = null, JsonPointer $path = null, $i = null, $coerce = false)
61+
protected function checkArray(&$value, $schema = null, JsonPointer $path = null, $i = null)
12962
{
13063
$validator = $this->factory->createInstanceFor('collection');
131-
if($coerce) {
132-
$validator->coerce($value, $schema, $path, $i);
133-
} else {
134-
$validator->check($value, $schema, $path, $i);
135-
}
64+
$validator->check($value, $schema, $path, $i);
13665

13766
$this->addErrors($validator->getErrors());
13867
}
@@ -145,16 +74,11 @@ protected function checkArray(&$value, $schema = null, JsonPointer $path = null,
14574
* @param JsonPointer|null $path
14675
* @param mixed $i
14776
* @param mixed $patternProperties
148-
* @param boolean $coerce
14977
*/
150-
protected function checkObject(&$value, $schema = null, JsonPointer $path = null, $i = null, $patternProperties = null, $coerce = false)
78+
protected function checkObject(&$value, $schema = null, JsonPointer $path = null, $i = null, $patternProperties = null)
15179
{
15280
$validator = $this->factory->createInstanceFor('object');
153-
if($coerce){
154-
$validator->coerce($value, $schema, $path, $i, $patternProperties);
155-
} else {
156-
$validator->check($value, $schema, $path, $i, $patternProperties);
157-
}
81+
$validator->check($value, $schema, $path, $i, $patternProperties);
15882

15983
$this->addErrors($validator->getErrors());
16084
}
@@ -166,16 +90,11 @@ protected function checkObject(&$value, $schema = null, JsonPointer $path = null
16690
* @param mixed $schema
16791
* @param JsonPointer|null $path
16892
* @param mixed $i
169-
* @param boolean $coerce
17093
*/
171-
protected function checkType(&$value, $schema = null, JsonPointer $path = null, $i = null, $coerce = false)
94+
protected function checkType(&$value, $schema = null, JsonPointer $path = null, $i = null)
17295
{
17396
$validator = $this->factory->createInstanceFor('type');
174-
if($coerce) {
175-
$validator->coerce($value, $schema, $path, $i);
176-
} else {
177-
$validator->check($value, $schema, $path, $i);
178-
}
97+
$validator->check($value, $schema, $path, $i);
17998

18099
$this->addErrors($validator->getErrors());
181100
}
@@ -187,17 +106,12 @@ protected function checkType(&$value, $schema = null, JsonPointer $path = null,
187106
* @param mixed $schema
188107
* @param JsonPointer|null $path
189108
* @param mixed $i
190-
* @param boolean $coerce
191109
*/
192-
protected function checkUndefined(&$value, $schema = null, JsonPointer $path = null, $i = null, $coerce = false)
110+
protected function checkUndefined(&$value, $schema = null, JsonPointer $path = null, $i = null)
193111
{
194112
$validator = $this->factory->createInstanceFor('undefined');
195113

196-
if($coerce){
197-
$validator->coerce($value, $this->factory->getSchemaStorage()->resolveRefSchema($schema), $path, $i);
198-
} else {
199-
$validator->check($value, $this->factory->getSchemaStorage()->resolveRefSchema($schema), $path, $i);
200-
}
114+
$validator->check($value, $this->factory->getSchemaStorage()->resolveRefSchema($schema), $path, $i);
201115

202116
$this->addErrors($validator->getErrors());
203117
}

0 commit comments

Comments
 (0)