Skip to content

Commit 5648eb8

Browse files
committed
Updated to 2020-12
1 parent b3fcb36 commit 5648eb8

14 files changed

+215
-100
lines changed

phpunit.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
<file>./tests/OfficialDraft6Test.php</file>
2525
<file>./tests/OfficialDraft7Test.php</file>
2626
<file>./tests/OfficialDraft201909Test.php</file>
27-
<!-- <file>./tests/OfficialDraft202012Test.php</file>-->
27+
<file>./tests/OfficialDraft202012Test.php</file>
2828
</testsuite>
2929
</testsuites>
3030
</phpunit>

src/Parsers/Drafts/Draft201909.php

+3-1
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,9 @@ public function supportsAnchorId(): bool
8888
*/
8989
protected function getRefKeywordParser(): KeywordParser
9090
{
91-
return new RefKeywordParser('$ref', '$recursiveRef', '$recursiveAnchor', false);
91+
return new RefKeywordParser('$ref', [
92+
['ref' => '$recursiveRef', 'anchor' => '$recursiveAnchor', 'fragment' => false],
93+
]);
9294
}
9395

9496
/**

src/Parsers/Drafts/Draft202012.php

+4-1
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,10 @@ public function supportsAnchorId(): bool
8989
*/
9090
protected function getRefKeywordParser(): KeywordParser
9191
{
92-
return new RefKeywordParser('$ref', '$dynamicRef', '$dynamicAnchor', true);
92+
return new RefKeywordParser('$ref', [
93+
['ref' => '$dynamicRef', 'anchor' => '$dynamicAnchor', 'fragment' => true],
94+
['ref' => '$recursiveRef', 'anchor' => '$recursiveAnchor', 'fragment' => false],
95+
]);
9396
}
9497

9598
/**

src/Parsers/Keywords/RefKeywordParser.php

+28-25
Original file line numberDiff line numberDiff line change
@@ -27,20 +27,11 @@ class RefKeywordParser extends KeywordParser
2727
{
2828
use VariablesTrait;
2929

30-
protected ?string $recursiveRef = null;
31-
protected ?string $recursiveAnchor = null;
32-
protected bool $dynamic = false;
33-
34-
public function __construct(
35-
string $keyword,
36-
?string $recursiveRef = null,
37-
?string $recursiveAnchor = null,
38-
bool $dynamic = false
39-
) {
30+
protected ?array $variations = null;
31+
32+
public function __construct(string $keyword, ?array $variations = null) {
4033
parent::__construct($keyword);
41-
$this->recursiveRef = $recursiveRef;
42-
$this->recursiveAnchor = $recursiveAnchor;
43-
$this->dynamic = $dynamic;
34+
$this->variations = $variations;
4435
}
4536

4637
/**
@@ -56,24 +47,36 @@ public function type(): string
5647
*/
5748
public function parse(SchemaInfo $info, SchemaParser $parser, object $shared): ?Keyword
5849
{
50+
$ref = null;
5951
$recursive = false;
6052
$schema = $info->data();
53+
$variation = null;
6154

6255
if ($this->keywordExists($schema)) {
6356
$ref = $this->keywordValue($schema);
6457
if (!is_string($ref) || $ref === '') {
6558
throw $this->keywordException('{keyword} must be a non-empty string', $info);
6659
}
67-
} elseif ($this->recursiveRef && $this->keywordExists($schema, $this->recursiveRef)) {
68-
$ref = $this->keywordValue($schema, $this->recursiveRef);
69-
if ($this->dynamic) {
70-
if (!preg_match('/^#[a-z][a-z0-9\\-.:_]*/i', $ref)) {
71-
$this->keywordException("{keyword} value is malformed", $info, $this->recursiveRef);
60+
} elseif ($this->variations) {
61+
foreach ($this->variations as $v) {
62+
if (!$this->keywordExists($schema, $v['ref'])) {
63+
continue;
64+
}
65+
$ref = $this->keywordValue($schema, $v['ref']);
66+
if ($v['fragment']) {
67+
if (!preg_match('/^#[a-z][a-z0-9\\-.:_]*/i', $ref)) {
68+
$this->keywordException("{keyword} value is malformed", $info, $v['ref']);
69+
}
70+
} elseif ($ref !== '#') {
71+
$this->keywordException("{keyword} supports only '#' as value", $info, $v['ref']);
7272
}
73-
} elseif ($ref !== '#') {
74-
$this->keywordException("{keyword} supports only '#' as value", $info, $this->recursiveRef);
73+
$variation = $v;
74+
$recursive = true;
75+
break;
76+
}
77+
if (!$recursive) {
78+
return null;
7579
}
76-
$recursive = true;
7780
} else {
7881
return null;
7982
}
@@ -110,12 +113,12 @@ public function parse(SchemaInfo $info, SchemaParser $parser, object $shared): ?
110113

111114
if ($recursive) {
112115
$ref = $info->idBaseRoot()->resolveRef($ref);
113-
if ($this->dynamic) {
114-
return new RecursiveRefKeyword($ref, $mapper, $globals, $slots,
115-
$this->recursiveRef, $this->recursiveAnchor, $ref->fragment());
116+
if ($variation['fragment']) {
117+
return new RecursiveRefKeyword($ref->resolveRef('#'), $mapper, $globals, $slots,
118+
$variation['ref'], $variation['anchor'], $ref->fragment());
116119
}
117120
return new RecursiveRefKeyword($ref, $mapper, $globals, $slots,
118-
$this->recursiveRef, $this->recursiveAnchor, true);
121+
$variation['ref'], $variation['anchor'], true);
119122
}
120123

121124
if ($ref === '#') {

src/Parsers/SchemaParser.php

+2-3
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,9 @@
3535

3636
class SchemaParser
3737
{
38-
/** @var string */
39-
protected const DRAFT_REGEX = '~^https?://json-schema\.org/draft-(\d[0-9-]*\d)/schema#?$~i';
38+
protected const DRAFT_REGEX = '~^https?://json-schema\.org/draft(?:/|-)(\d[0-9-]*\d)/schema#?$~i';
4039
protected const ANCHOR_REGEX = '/^[a-z][a-z0-9\\-.:_]*/i';
41-
protected const DEFAULT_DRAFT = '2019-09';
40+
protected const DEFAULT_DRAFT = '2020-12';
4241

4342
/** @var array */
4443
protected const DEFAULT_OPTIONS = [

src/ValidationResult.php

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?php
2+
/* ============================================================================
3+
* Copyright 2021 Zindex Software
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
* ============================================================================ */
17+
18+
namespace Opis\JsonSchema;
19+
20+
use Opis\JsonSchema\Errors\ValidationError;
21+
22+
class ValidationResult
23+
{
24+
protected ?ValidationError $error;
25+
26+
public function __construct(?ValidationError $error)
27+
{
28+
$this->error = $error;
29+
}
30+
31+
public function error(): ?ValidationError
32+
{
33+
return $this->error;
34+
}
35+
36+
public function isValid(): bool
37+
{
38+
return $this->error === null;
39+
}
40+
41+
public function hasError(): bool
42+
{
43+
return $this->error !== null;
44+
}
45+
46+
public function __toString(): string
47+
{
48+
if ($this->error) {
49+
return $this->error->message();
50+
}
51+
return '';
52+
}
53+
}

src/Validator.php

+35-2
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,7 @@
2424

2525
class Validator
2626
{
27-
2827
protected SchemaLoader $loader;
29-
3028
protected int $maxErrors = 1;
3129

3230
/**
@@ -39,6 +37,41 @@ public function __construct(?SchemaLoader $loader = null, int $max_errors = 1)
3937
$this->maxErrors = $max_errors;
4038
}
4139

40+
/**
41+
* @param $data
42+
* @param bool|string|Uri|Schema|object $schema
43+
* @param array|null $globals
44+
* @param array|null $slots
45+
* @return ValidationResult
46+
*/
47+
public function validate($data, $schema, ?array $globals = null, ?array $slots = null): ValidationResult
48+
{
49+
if (is_string($schema)) {
50+
if ($uri = Uri::parse($schema, true)) {
51+
$schema = $uri;
52+
} else {
53+
$schema = json_decode($schema, false);
54+
}
55+
}
56+
57+
$error = null;
58+
if (is_bool($schema)) {
59+
$error = $this->dataValidation($data, $schema, $globals, $slots);
60+
} elseif (is_object($schema)) {
61+
if ($schema instanceof Uri) {
62+
$error = $this->uriValidation($data, $schema, $globals, $slots);
63+
} elseif ($schema instanceof Schema) {
64+
$error = $this->schemaValidation($data, $schema, $globals, $slots);
65+
} else {
66+
$error = $this->dataValidation($data, $schema, $globals, $slots);
67+
}
68+
} else {
69+
throw new RuntimeException("Invalid schema provided");
70+
}
71+
72+
return new ValidationResult($error);
73+
}
74+
4275
/**
4376
* @param $data
4477
* @param Uri|string $uri

tests/AbstractOfficialDraftTest.php

+16-16
Original file line numberDiff line numberDiff line change
@@ -79,22 +79,6 @@ protected function doValidation($schema, $description, $data, $valid, $comment =
7979
}
8080
}
8181

82-
/**
83-
* @dataProvider basicDataProvider
84-
*/
85-
public function testBasic(...$args)
86-
{
87-
$this->doValidation(...$args);
88-
}
89-
90-
/**
91-
* @dataProvider optionalDataProvider
92-
*/
93-
public function testOptional(...$args)
94-
{
95-
$this->doValidation(...$args);
96-
}
97-
9882
public function testSelf()
9983
{
10084
$validator = self::$validator;
@@ -116,6 +100,22 @@ public function testSelf()
116100
$this->assertNull($validator->schemaValidation($data, $schema));
117101
}
118102

103+
/**
104+
* @dataProvider basicDataProvider
105+
*/
106+
public function testBasic(...$args)
107+
{
108+
$this->doValidation(...$args);
109+
}
110+
111+
/**
112+
* @dataProvider optionalDataProvider
113+
*/
114+
public function testOptional(...$args)
115+
{
116+
$this->doValidation(...$args);
117+
}
118+
119119
protected function getData($dir)
120120
{
121121
$dir = __DIR__ . '/official/tests/draft' . $this->getDraft() . '/' . $dir;

tests/official/drafts/draft/2020-12/meta/applicator

+2-3
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,8 @@
2626
},
2727
"dependentSchemas": {
2828
"type": "object",
29-
"additionalProperties": {
30-
"$dynamicRef": "#meta"
31-
}
29+
"additionalProperties": { "$dynamicRef": "#meta" },
30+
"default": {}
3231
},
3332
"propertyNames": { "$dynamicRef": "#meta" },
3433
"if": { "$dynamicRef": "#meta" },

tests/official/drafts/draft/2020-12/meta/content

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010

1111
"type": ["object", "boolean"],
1212
"properties": {
13-
"contentMediaType": { "type": "string" },
1413
"contentEncoding": { "type": "string" },
14+
"contentMediaType": { "type": "string" },
1515
"contentSchema": { "$dynamicRef": "#meta" }
1616
}
1717
}

tests/official/drafts/draft/2020-12/meta/core

+22-28
Original file line numberDiff line numberDiff line change
@@ -10,37 +10,18 @@
1010
"type": ["object", "boolean"],
1111
"properties": {
1212
"$id": {
13-
"type": "string",
14-
"format": "uri-reference",
13+
"$ref": "#/$defs/uriReferenceString",
1514
"$comment": "Non-empty fragments not allowed.",
1615
"pattern": "^[^#]*#?$"
1716
},
18-
"$schema": {
19-
"type": "string",
20-
"format": "uri"
21-
},
22-
"$anchor": {
23-
"type": "string",
24-
"pattern": "^[A-Za-z_][-A-Za-z0-9._]*$"
25-
},
26-
"$ref": {
27-
"type": "string",
28-
"format": "uri-reference"
29-
},
30-
"$dynamicRef": {
31-
"type": "string",
32-
"format": "uri-reference"
33-
},
34-
"$dynamicAnchor": {
35-
"type": "string",
36-
"pattern": "^[A-Za-z_][-A-Za-z0-9._]*$"
37-
},
17+
"$schema": { "$ref": "#/$defs/uriString" },
18+
"$ref": { "$ref": "#/$defs/uriReferenceString" },
19+
"$anchor": { "$ref": "#/$defs/anchorString" },
20+
"$dynamicRef": { "$ref": "#/$defs/uriReferenceString" },
21+
"$dynamicAnchor": { "$ref": "#/$defs/anchorString" },
3822
"$vocabulary": {
3923
"type": "object",
40-
"propertyNames": {
41-
"type": "string",
42-
"format": "uri"
43-
},
24+
"propertyNames": { "$ref": "#/$defs/uriString" },
4425
"additionalProperties": {
4526
"type": "boolean"
4627
}
@@ -50,8 +31,21 @@
5031
},
5132
"$defs": {
5233
"type": "object",
53-
"additionalProperties": { "$dynamicRef": "#meta" },
54-
"default": {}
34+
"additionalProperties": { "$dynamicRef": "#meta" }
35+
}
36+
},
37+
"$defs": {
38+
"anchorString": {
39+
"type": "string",
40+
"pattern": "^[A-Za-z_][-A-Za-z0-9._]*$"
41+
},
42+
"uriString": {
43+
"type": "string",
44+
"format": "uri"
45+
},
46+
"uriReferenceString": {
47+
"type": "string",
48+
"format": "uri-reference"
5549
}
5650
}
5751
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"$schema": "https://json-schema.org/draft/2020-12/schema",
3+
"$id": "https://json-schema.org/draft/2020-12/meta/format-assertion",
4+
"$vocabulary": {
5+
"https://json-schema.org/draft/2020-12/vocab/format-assertion": true
6+
},
7+
"$dynamicAnchor": "meta",
8+
9+
"title": "Format vocabulary meta-schema for assertion results",
10+
"type": ["object", "boolean"],
11+
"properties": {
12+
"format": { "type": "string" }
13+
}
14+
}

0 commit comments

Comments
 (0)