Skip to content

Commit e09fed3

Browse files
committed
feat: add more draft-06 support
1 parent e1a6b90 commit e09fed3

13 files changed

+243
-25
lines changed

src/JsonSchema/Constraints/Drafts/Draft06/AdditionalItemsConstraint.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ public function check(&$value, $schema = null, ?JsonPointer $path = null, $i = n
3333
return;
3434
}
3535

36+
3637
$additionalItems = array_diff_key($value, $schema->items);
3738

3839
foreach ($additionalItems as $key => $_) {
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace JsonSchema\Constraints\Drafts\Draft06;
6+
7+
use JsonSchema\ConstraintError;
8+
use JsonSchema\Constraints\ConstraintInterface;
9+
use JsonSchema\Entity\ErrorBagProxy;
10+
use JsonSchema\Entity\JsonPointer;
11+
use JsonSchema\Exception\ValidationException;
12+
13+
class AllOfConstraint implements ConstraintInterface
14+
{
15+
use ErrorBagProxy;
16+
17+
/** @var Factory */
18+
private $factory;
19+
20+
public function __construct(?Factory $factory = null)
21+
{
22+
$this->factory = $factory ?: new Factory();
23+
$this->initialiseErrorBag($this->factory);
24+
}
25+
26+
public function check(&$value, $schema = null, ?JsonPointer $path = null, $i = null): void
27+
{
28+
if (!property_exists($schema, 'allOf')) {
29+
return;
30+
}
31+
32+
foreach ($schema->allOf as $allOfSchema) {
33+
$schemaConstraint = $this->factory->createInstanceFor('schema');
34+
$schemaConstraint->check($value, $allOfSchema, $path, $i);
35+
36+
if ($schemaConstraint->isValid()) {
37+
continue;
38+
}
39+
$this->addError(ConstraintError::ALL_OF(), $path);
40+
$this->addErrors($schemaConstraint->getErrors());
41+
}
42+
}
43+
}

src/JsonSchema/Constraints/Drafts/Draft06/ContainsConstraint.php

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,11 @@ public function check(&$value, $schema = null, ?JsonPointer $path = null, $i = n
2828
}
2929

3030
$properties = [];
31-
if (is_array($value)) {
32-
$properties = $value;
33-
}
34-
if (is_object($value)) {
35-
$properties = get_object_vars($value);
31+
if (!is_array($value)) {
32+
return;
3633
}
3734

38-
foreach ($properties as $propertyName => $propertyValue) {
35+
foreach ($value as $propertyName => $propertyValue) {
3936
$schemaConstraint = $this->factory->createInstanceFor('schema');
4037

4138
$schemaConstraint->check($propertyValue, $schema->contains, $path, $i);

src/JsonSchema/Constraints/Drafts/Draft06/DependenciesConstraint.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,16 @@ public function check(&$value, $schema = null, ?JsonPointer $path = null, $i = n
3030
}
3131

3232
foreach ($schema->dependencies as $dependant => $dependencies) {
33+
if (!property_exists($value, $dependant)) {
34+
continue;
35+
}
36+
if ($dependencies === true) {
37+
continue;
38+
}
39+
if ($dependencies === false) {
40+
$this->addError(ConstraintError::FALSE(), $path, ['dependant' => $dependant]);
41+
continue;
42+
}
3343
foreach ($dependencies as $dependency) {
3444
if (property_exists($value, $dependant) && !property_exists($value, $dependency)) {
3545
$this->addError(ConstraintError::DEPENDENCIES(), $path, ['dependant' => $dependant, 'dependency' => $dependency]);

src/JsonSchema/Constraints/Drafts/Draft06/Draft06Constraint.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,15 @@ public function check(&$value, $schema = null, ?JsonPointer $path = null, $i = n
2727
// Apply defaults
2828
$this->checkForKeyword('required', $value, $schema, $path, $i);
2929
$this->checkForKeyword('contains', $value, $schema, $path, $i);
30+
$this->checkForKeyword('properties', $value, $schema, $path, $i);
3031
$this->checkForKeyword('propertyNames', $value, $schema, $path, $i);
3132
$this->checkForKeyword('patternProperties', $value, $schema, $path, $i);
3233
$this->checkForKeyword('type', $value, $schema, $path, $i);
33-
// Not
34+
$this->checkForKeyword('not', $value, $schema, $path, $i);
3435
$this->checkForKeyword('dependencies', $value, $schema, $path, $i);
35-
// allof
36+
$this->checkForKeyword('allOf', $value, $schema, $path, $i);
3637
$this->checkForKeyword('anyOf', $value, $schema, $path, $i);
37-
// oneof
38+
$this->checkForKeyword('oneOf', $value, $schema, $path, $i);
3839

3940
$this->checkForKeyword('additionalProperties', $value, $schema, $path, $i);
4041
$this->checkForKeyword('items', $value, $schema, $path, $i);

src/JsonSchema/Constraints/Drafts/Draft06/Factory.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,13 @@ class Factory extends \JsonSchema\Constraints\Factory
3232
'required' => RequiredConstraint::class,
3333
'format' => FormatConstraint::class,
3434
'anyOf' => AnyOfConstraint::class,
35+
'allOf' => AllOfConstraint::class,
36+
'oneOf' => OneOfConstraint::class,
37+
'not' => NotConstraint::class,
3538
'contains' => ContainsConstraint::class,
3639
'propertyNames' => PropertiesNamesConstraint::class,
3740
'patternProperties' => PatternPropertiesConstraint::class,
41+
'properties' => PropertiesConstraint::class,
3842
'items' => ItemsConstraint::class,
3943
];
4044
}

src/JsonSchema/Constraints/Drafts/Draft06/ItemsConstraint.php

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,23 +31,26 @@ public function check(&$value, $schema = null, ?JsonPointer $path = null, $i = n
3131
return;
3232
}
3333

34-
$properties = [];
35-
if (is_object($value)) {
36-
$properties = get_object_vars($value);
37-
}
38-
if (is_array($value)) {
39-
$properties = $value;
34+
if (!is_array($value)) {
35+
return;
4036
}
41-
if (is_object($schema->items)) {
42-
foreach ($properties as $propertyName => $propertyValue) {
43-
$schemaConstraint = $this->factory->createInstanceFor('schema');
44-
$schemaConstraint->check($propertyValue, $schema->items, $path, $i);
45-
if ($schemaConstraint->isValid()) {
37+
38+
foreach ($value as $propertyName => $propertyValue) {
39+
$itemSchema = $schema->items;
40+
if (is_array($itemSchema)) {
41+
if (!array_key_exists($propertyName, $itemSchema)) {
4642
continue;
4743
}
4844

49-
$this->addErrors($schemaConstraint->getErrors());
45+
$itemSchema = $itemSchema[$propertyName];
5046
}
47+
$schemaConstraint = $this->factory->createInstanceFor('schema');
48+
$schemaConstraint->check($propertyValue, $itemSchema, $path, $i);
49+
if ($schemaConstraint->isValid()) {
50+
continue;
51+
}
52+
53+
$this->addErrors($schemaConstraint->getErrors());
5154
}
5255
}
5356
}

src/JsonSchema/Constraints/Drafts/Draft06/MultipleOfConstraint.php

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,22 @@ public function check(&$value, $schema = null, ?JsonPointer $path = null, $i = n
2929
return;
3030
}
3131

32-
if (fmod($value, $schema->multipleOf) === 0.0) {
32+
if (fmod($value, $schema->multipleOf) === 0.0 || $this->isMultipleOf((string) $value, (string) $schema->multipleOf)) {
3333
return;
3434
}
3535

3636
$this->addError(ConstraintError::MULTIPLE_OF(), $path, ['multipleOf' => $schema->multipleOf, 'found' => $value]);
3737

3838

3939
}
40+
41+
private function isMultipleOf(string $value, string $multipleOf): bool
42+
{
43+
if (bccomp($multipleOf, '0', 20) === 0) {
44+
return false;
45+
}
46+
47+
$div = bcdiv($value, $multipleOf, 20);
48+
return bccomp(bcmod($div, '1', 20), '0', 20) === 0;
49+
}
4050
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace JsonSchema\Constraints\Drafts\Draft06;
6+
7+
use JsonSchema\ConstraintError;
8+
use JsonSchema\Constraints\ConstraintInterface;
9+
use JsonSchema\Constraints\Factory;
10+
use JsonSchema\Entity\ErrorBagProxy;
11+
use JsonSchema\Entity\JsonPointer;
12+
use JsonSchema\Rfc3339;
13+
use JsonSchema\Tool\Validator\RelativeReferenceValidator;
14+
use JsonSchema\Tool\Validator\UriValidator;
15+
16+
class NotConstraint implements ConstraintInterface
17+
{
18+
use ErrorBagProxy;
19+
20+
/** @var \JsonSchema\Constraints\Drafts\Draft06\Factory */
21+
private $factory;
22+
public function __construct(?Factory $factory = null)
23+
{
24+
$this->factory = $factory ?: new Factory();
25+
$this->initialiseErrorBag($this->factory);
26+
}
27+
28+
public function check(&$value, $schema = null, ?JsonPointer $path = null, $i = null): void
29+
{
30+
if (!property_exists($schema, 'not')) {
31+
return;
32+
}
33+
34+
$schemaConstraint = $this->factory->createInstanceFor('schema');
35+
$schemaConstraint->check($value, $schema->not, $path, $i);
36+
37+
if (! $schemaConstraint->isValid()) {
38+
return;
39+
}
40+
41+
$this->addError(ConstraintError::NOT(), $path);
42+
}
43+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace JsonSchema\Constraints\Drafts\Draft06;
6+
7+
use JsonSchema\ConstraintError;
8+
use JsonSchema\Constraints\ConstraintInterface;
9+
use JsonSchema\Entity\ErrorBagProxy;
10+
use JsonSchema\Entity\JsonPointer;
11+
use JsonSchema\Exception\ValidationException;
12+
13+
class OneOfConstraint implements ConstraintInterface
14+
{
15+
use ErrorBagProxy;
16+
17+
/** @var Factory */
18+
private $factory;
19+
20+
public function __construct(?Factory $factory = null)
21+
{
22+
$this->factory = $factory ?: new Factory();
23+
$this->initialiseErrorBag($this->factory);
24+
}
25+
26+
public function check(&$value, $schema = null, ?JsonPointer $path = null, $i = null): void
27+
{
28+
if (!property_exists($schema, 'oneOf')) {
29+
return;
30+
}
31+
32+
$matchedSchema = 0;
33+
foreach ($schema->oneOf as $oneOfSchema) {
34+
$schemaConstraint = $this->factory->createInstanceFor('schema');
35+
$schemaConstraint->check($value, $oneOfSchema, $path, $i);
36+
37+
if ($schemaConstraint->isValid()) {
38+
$matchedSchema++;
39+
continue;
40+
}
41+
42+
$this->addErrors($schemaConstraint->getErrors());
43+
}
44+
45+
if ($matchedSchema !== 1) {
46+
$this->addError(ConstraintError::ONE_OF(), $path);
47+
} else {
48+
$this->errorBag()->reset();
49+
}
50+
}
51+
}

src/JsonSchema/Constraints/Drafts/Draft06/PatternPropertiesConstraint.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,16 +28,19 @@ public function check(&$value, $schema = null, ?JsonPointer $path = null, $i = n
2828
return;
2929
}
3030

31-
$properties = get_object_vars($value);
31+
if (!is_object($value)) {
32+
return;
33+
}
3234

35+
$properties = get_object_vars($value);
3336

3437
foreach ($properties as $propertyName => $propertyValue) {
3538
foreach ($schema->patternProperties as $patternPropertyRegex => $patternPropertySchema) {
3639
if (preg_match('/' . str_replace('/', '\/', $patternPropertyRegex) . '/', $propertyName)) {
3740
$schemaConstraint = $this->factory->createInstanceFor('schema');
3841
$schemaConstraint->check($propertyValue, $patternPropertySchema, $path, $i);
3942
if ($schemaConstraint->isValid()) {
40-
continue 2;
43+
continue;
4144
}
4245

4346
$this->addErrors($schemaConstraint->getErrors());
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace JsonSchema\Constraints\Drafts\Draft06;
6+
7+
use JsonSchema\ConstraintError;
8+
use JsonSchema\Constraints\ConstraintInterface;
9+
use JsonSchema\Constraints\Factory;
10+
use JsonSchema\Entity\ErrorBagProxy;
11+
use JsonSchema\Entity\JsonPointer;
12+
use JsonSchema\Rfc3339;
13+
use JsonSchema\Tool\Validator\RelativeReferenceValidator;
14+
use JsonSchema\Tool\Validator\UriValidator;
15+
16+
class PropertiesConstraint implements ConstraintInterface
17+
{
18+
use ErrorBagProxy;
19+
20+
/** @var \JsonSchema\Constraints\Drafts\Draft06\Factory */
21+
private $factory;
22+
public function __construct(?Factory $factory = null)
23+
{
24+
$this->factory = $factory ?: new Factory();
25+
$this->initialiseErrorBag($this->factory);
26+
}
27+
28+
public function check(&$value, $schema = null, ?JsonPointer $path = null, $i = null): void
29+
{
30+
if (!property_exists($schema, 'properties')) {
31+
return;
32+
}
33+
34+
if (!is_object($value)) {
35+
return;
36+
}
37+
38+
foreach ($schema->properties as $propertyName => $propertySchema) {
39+
$schemaConstraint = $this->factory->createInstanceFor('schema');
40+
if (! property_exists($value, $propertyName)) {
41+
continue;
42+
}
43+
44+
$schemaConstraint->check($value->{$propertyName}, $propertySchema, $path, $i);
45+
if ($schemaConstraint->isValid()) {
46+
continue;
47+
}
48+
49+
$this->addErrors($schemaConstraint->getErrors());
50+
}
51+
}
52+
}

src/JsonSchema/Constraints/Drafts/Draft06/RequiredConstraint.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public function check(&$value, $schema = null, ?JsonPointer $path = null, $i = n
3030
}
3131

3232
foreach ($schema->required as $required) {
33-
if (isset($value->{$required})) {
33+
if (property_exists($value, $required)) {
3434
continue;
3535
}
3636

0 commit comments

Comments
 (0)