Skip to content

Commit efc9124

Browse files
Implemented TypeCheck to allow for a common interface on how to check arrays and/or objects
1 parent 0c4fd72 commit efc9124

File tree

10 files changed

+194
-44
lines changed

10 files changed

+194
-44
lines changed

src/JsonSchema/Constraints/Constraint.php

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
namespace JsonSchema\Constraints;
1111

1212
use JsonSchema\Uri\UriRetriever;
13+
use JsonSchema\Validator;
1314

1415
/**
1516
* The Base Constraints, all Validators should extend this class
@@ -49,8 +50,7 @@ public function __construct($checkMode = self::CHECK_MODE_NORMAL, UriRetriever $
4950
*/
5051
public function getUriRetriever()
5152
{
52-
if (is_null($this->uriRetriever))
53-
{
53+
if (is_null($this->uriRetriever)) {
5454
$this->setUriRetriever(new UriRetriever);
5555
}
5656

@@ -63,7 +63,7 @@ public function getUriRetriever()
6363
public function getFactory()
6464
{
6565
if (!$this->factory) {
66-
$this->factory = new Factory($this->getUriRetriever());
66+
$this->factory = new Factory($this->getUriRetriever(), $this->checkMode);
6767
}
6868

6969
return $this->factory;
@@ -288,4 +288,14 @@ protected function retrieveUri($uri)
288288
// TODO validate using schema
289289
return $jsonSchema;
290290
}
291+
292+
/**
293+
* Get the type check based on the set check mode.
294+
*
295+
* @return TypeCheck\TypeCheckInterface
296+
*/
297+
protected function getTypeCheck()
298+
{
299+
return $this->getFactory()->getTypeCheck();
300+
}
291301
}

src/JsonSchema/Constraints/EnumConstraint.php

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
*/
99

1010
namespace JsonSchema\Constraints;
11+
use JsonSchema\Validator;
1112

1213
/**
1314
* The EnumConstraint Constraints, validates an element against a given set of possibilities
@@ -26,17 +27,23 @@ public function check($element, $schema = null, $path = null, $i = null)
2627
if ($element instanceof UndefinedConstraint && (!isset($schema->required) || !$schema->required)) {
2728
return;
2829
}
30+
$type = gettype($element);
2931

3032
foreach ($schema->enum as $enum) {
31-
$type = gettype($element);
33+
$enumType = gettype($enum);
34+
if ($this->checkMode === self::CHECK_MODE_TYPE_CAST && $type == "array" && $enumType == "object") {
35+
if ((object)$element == $enum) {
36+
return;
37+
}
38+
}
39+
3240
if ($type === gettype($enum)) {
3341
if ($type == "object") {
34-
if ($element == $enum)
42+
if ($element == $enum) {
3543
return;
36-
} else {
37-
if ($element === $enum)
38-
return;
39-
44+
}
45+
} elseif ($element === $enum) {
46+
return;
4047
}
4148
}
4249
}

src/JsonSchema/Constraints/Factory.php

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,16 @@ class Factory
2323
*/
2424
protected $uriRetriever;
2525

26+
/**
27+
* @var int
28+
*/
29+
private $checkMode;
30+
31+
/**
32+
* @var TypeCheck\TypeCheckInterface[]
33+
*/
34+
private $typeCheck = [];
35+
2636
/**
2737
* @var array $constraintMap
2838
*/
@@ -43,13 +53,14 @@ class Factory
4353
/**
4454
* @param UriRetriever $uriRetriever
4555
*/
46-
public function __construct(UriRetriever $uriRetriever = null)
56+
public function __construct(UriRetriever $uriRetriever = null, $checkMode = Constraint::CHECK_MODE_NORMAL)
4757
{
4858
if (!$uriRetriever) {
4959
$uriRetriever = new UriRetriever();
5060
}
5161

5262
$this->uriRetriever = $uriRetriever;
63+
$this->checkMode = $checkMode;
5364
}
5465

5566
/**
@@ -60,23 +71,36 @@ public function getUriRetriever()
6071
return $this->uriRetriever;
6172
}
6273

74+
public function getTypeCheck()
75+
{
76+
if (!isset($this->typeCheck[$this->checkMode])) {
77+
if ($this->checkMode === Constraint::CHECK_MODE_TYPE_CAST) {
78+
$this->typeCheck[Constraint::CHECK_MODE_TYPE_CAST] = new TypeCheck\LooseTypeCheck();
79+
} else {
80+
$this->typeCheck[$this->checkMode] = new TypeCheck\StrictTypeCheck();
81+
}
82+
}
83+
84+
return $this->typeCheck[$this->checkMode];
85+
}
86+
6387
/**
6488
* @param string $name
6589
* @param string $class
6690
* @return Factory
6791
*/
6892
public function setConstraintClass($name, $class)
6993
{
70-
// Ensure class exists
71-
if (!class_exists($class)) {
72-
throw new InvalidArgumentException('Unknown constraint ' . $name);
73-
}
74-
// Ensure class is appropriate
75-
if (!in_array('JsonSchema\Constraints\ConstraintInterface', class_implements($class))) {
76-
throw new InvalidArgumentException('Invalid class ' . $name);
77-
}
78-
$this->constraintMap[$name] = $class;
79-
return $this;
94+
// Ensure class exists
95+
if (!class_exists($class)) {
96+
throw new InvalidArgumentException('Unknown constraint ' . $name);
97+
}
98+
// Ensure class is appropriate
99+
if (!in_array('JsonSchema\Constraints\ConstraintInterface', class_implements($class))) {
100+
throw new InvalidArgumentException('Invalid class ' . $name);
101+
}
102+
$this->constraintMap[$name] = $class;
103+
return $this;
80104
}
81105

82106
/**
@@ -89,7 +113,7 @@ public function setConstraintClass($name, $class)
89113
public function createInstanceFor($constraintName)
90114
{
91115
if (array_key_exists($constraintName, $this->constraintMap)) {
92-
return new $this->constraintMap[$constraintName](Constraint::CHECK_MODE_NORMAL, $this->uriRetriever, $this);
116+
return new $this->constraintMap[$constraintName]($this->checkMode, $this->uriRetriever, $this);
93117
}
94118
throw new InvalidArgumentException('Unknown constraint ' . $constraintName);
95119
}

src/JsonSchema/Constraints/ObjectConstraint.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class ObjectConstraint extends Constraint
2020
/**
2121
* {@inheritDoc}
2222
*/
23-
function check($element, $definition = null, $path = null, $additionalProp = null, $patternProperties = null)
23+
public function check($element, $definition = null, $path = null, $additionalProp = null, $patternProperties = null)
2424
{
2525
if ($element instanceof UndefinedConstraint) {
2626
return;
@@ -161,13 +161,13 @@ protected function getProperty($element, $property, $fallback = null)
161161
protected function validateMinMaxConstraint($element, $objectDefinition, $path) {
162162
// Verify minimum number of properties
163163
if (isset($objectDefinition->minProperties) && !is_object($objectDefinition->minProperties)) {
164-
if (count(get_object_vars($element)) < $objectDefinition->minProperties) {
164+
if ($this->getTypeCheck()->propertyCount($element) < $objectDefinition->minProperties) {
165165
$this->addError($path, "Must contain a minimum of " . $objectDefinition->minProperties . " properties", 'minProperties', array('minProperties' => $objectDefinition->minProperties,));
166166
}
167167
}
168168
// Verify maximum number of properties
169169
if (isset($objectDefinition->maxProperties) && !is_object($objectDefinition->maxProperties)) {
170-
if (count(get_object_vars($element)) > $objectDefinition->maxProperties) {
170+
if ($this->getTypeCheck()->propertyCount($element) > $objectDefinition->maxProperties) {
171171
$this->addError($path, "Must contain no more than " . $objectDefinition->maxProperties . " properties", 'maxProperties', array('maxProperties' => $objectDefinition->maxProperties,));
172172
}
173173
}

src/JsonSchema/Constraints/SchemaConstraint.php

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,16 @@ public function check($element, $schema = null, $path = null, $i = null)
2727
if ($schema !== null) {
2828
// passed schema
2929
$this->checkUndefined($element, $schema, '', '');
30-
} elseif (property_exists($element, $this->inlineSchemaProperty)) {
30+
} elseif ($this->getTypeCheck()->propertyExists($element, $this->inlineSchemaProperty)) {
31+
$inlineSchema = $this->getTypeCheck()->propertyGet($element, $this->inlineSchemaProperty);
32+
if (is_array($inlineSchema)) {
33+
$inlineSchema = json_decode(json_encode($inlineSchema));
34+
}
35+
3136
// inline schema
32-
$this->checkUndefined($element, $element->{$this->inlineSchemaProperty}, '', '');
37+
$this->checkUndefined($element, $inlineSchema, '', '');
3338
} else {
3439
throw new InvalidArgumentException('no schema found to verify against');
3540
}
3641
}
37-
}
42+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<?php
2+
3+
namespace JsonSchema\Constraints\TypeCheck;
4+
5+
class LooseTypeCheck implements TypeCheckInterface
6+
{
7+
public static function isObject($value)
8+
{
9+
return
10+
is_object($value) ||
11+
(is_array($value) && (count($value) == 0 || self::isAssociativeArray($value)));
12+
}
13+
14+
public static function isArray($value)
15+
{
16+
return
17+
is_array($value) &&
18+
(count($value) == 0 || !self::isAssociativeArray($value));
19+
}
20+
21+
public static function propertyGet($value, $property)
22+
{
23+
if (is_object($value)) {
24+
return $value->{$property};
25+
}
26+
27+
return $value[$property];
28+
}
29+
30+
public static function propertyExists($value, $property)
31+
{
32+
if (is_object($value)) {
33+
return property_exists($value, $property);
34+
}
35+
36+
return array_key_exists($property, $value);
37+
}
38+
39+
public static function propertyCount($value)
40+
{
41+
if (is_object($value)) {
42+
return count(get_object_vars($value));
43+
}
44+
45+
return count($value);
46+
}
47+
48+
/**
49+
* Check if the provided array is associative or not
50+
*
51+
* @param array $arr
52+
*
53+
* @return bool
54+
*/
55+
private static function isAssociativeArray($arr)
56+
{
57+
return (array_keys($arr) !== range(0, count($arr) - 1));
58+
}
59+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
namespace JsonSchema\Constraints\TypeCheck;
4+
5+
class StrictTypeCheck implements TypeCheckInterface
6+
{
7+
public static function isObject($value)
8+
{
9+
return is_object($value);
10+
}
11+
12+
public static function isArray($value)
13+
{
14+
return is_array($value);
15+
}
16+
17+
public static function propertyGet($value, $property)
18+
{
19+
return $value->{$property};
20+
}
21+
22+
public static function propertyExists($value, $property)
23+
{
24+
return property_exists($value, $property);
25+
}
26+
27+
public static function propertyCount($value)
28+
{
29+
return count(get_object_vars($value));
30+
}
31+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
namespace JsonSchema\Constraints\TypeCheck;
4+
5+
interface TypeCheckInterface
6+
{
7+
public static function isObject($value);
8+
9+
public static function isArray($value);
10+
11+
public static function propertyGet($value, $property);
12+
13+
public static function propertyExists($value, $property);
14+
15+
public static function propertyCount($value);
16+
}

src/JsonSchema/Constraints/TypeConstraint.php

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,12 +116,11 @@ protected function validateType($value, $type)
116116
}
117117

118118
if ('object' === $type) {
119-
return is_object($value);
120-
//return ($this::CHECK_MODE_TYPE_CAST == $this->checkMode) ? is_array($value) : is_object($value);
119+
return $this->getTypeCheck()->isObject($value);
121120
}
122121

123122
if ('array' === $type) {
124-
return is_array($value);
123+
return $this->getTypeCheck()->isArray($value);
125124
}
126125

127126
if ('string' === $type) {

0 commit comments

Comments
 (0)