Completed
Pull Request — master (#18)
by Mathieu
03:26
created

JsonValidator   A

Complexity

Total Complexity 20

Size/Duplication

Total Lines 138
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 90.38%

Importance

Changes 9
Bugs 2 Features 3
Metric Value
wmc 20
c 9
b 2
f 3
lcom 1
cbo 5
dl 0
loc 138
ccs 47
cts 52
cp 0.9038
rs 10

4 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 7 3
D validate() 0 42 10
A assertSchemaValid() 0 21 4
A loadSchema() 0 20 3
1
<?php
2
3
/*
4
 * This file is part of the Webmozart JSON package.
5
 *
6
 * (c) Bernhard Schussek <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Webmozart\Json;
13
14
use JsonSchema\Exception\InvalidArgumentException;
15
use JsonSchema\Exception\ResourceNotFoundException;
16
use JsonSchema\RefResolver;
17
use JsonSchema\Uri\UriResolver;
18
use JsonSchema\Uri\UriRetriever;
19
use JsonSchema\Validator;
20
use Webmozart\PathUtil\Path;
21
22
/**
23
 * Validates decoded JSON values against a JSON schema.
24
 *
25
 * This class is a wrapper for {@link Validator} that adds exceptions and
26
 * validation of schema files. A few edge cases that are not handled by
27
 * {@link Validator} are handled by this class.
28
 *
29
 * @since  1.0
30
 *
31
 * @author Bernhard Schussek <[email protected]>
32
 */
33
class JsonValidator
34
{
35
    /**
36
     * The schema used for validating schemas.
37
     *
38
     * @var object|null
39
     */
40
    private $metaSchema;
41
42
    /**
43
     * Validator instance used for validation.
44
     *
45
     * @var Validator
46
     */
47
    private $validator;
48
49
    /**
50
     * for fetching & resolving JSON schemas.
51
     *
52
     * @var RefResolver
53
     */
54
    private $resolver;
55
56
    /**
57
     * JsonValidator constructor.
58
     *
59
     * @param Validator|null $validator JsonSchema\Validator instance to use.
60
     */
61 111
    public function __construct(
62
        Validator $validator = null,
63
        RefResolver $resolver = null
64
    ) {
65 111
        $this->validator = $validator ?: new Validator();
66 111
        $this->resolver = $resolver ?: new RefResolver(new UriRetriever(), new UriResolver());
67 111
    }
68
69
    /**
70
     * Validates JSON data against a schema.
71
     *
72
     * The schema may be passed as file path or as object returned from
73
     * `json_decode($schemaFile)`.
74
     *
75
     * @param mixed              $data   The decoded JSON data.
76
     * @param string|object|null $schema The schema file or object. If `null`,
77
     *                                   the validator will look for a `$schema`
78
     *                                   property.
79
     *
80
     * @return string[] The errors found during validation. Returns an empty
81
     *                  array if no errors were found.
82
     *
83
     * @throws InvalidSchemaException If the schema is invalid.
84
     */
85 28
    public function validate($data, $schema = null)
86
    {
87 28
        if (null === $schema && isset($data->{'$schema'})) {
88
            $schema = $data->{'$schema'};
89
        }
90
91 28
        if (is_string($schema)) {
92 15
            $schema = $this->loadSchema($schema);
93 14
        } elseif (is_object($schema)) {
94 11
            $this->assertSchemaValid($schema);
95
        } else {
96 3
            throw new InvalidSchemaException(sprintf(
97
                'The schema must be given as string, object or in the "$schema" '.
98 3
                'property of the JSON data. Got: %s',
99 3
                is_object($schema) ? get_class($schema) : gettype($schema)
100
            ));
101
        }
102
103 11
        $this->validator->reset();
104
105
        try {
106 11
            $this->validator->check($data, $schema);
107 1
        } catch (InvalidArgumentException $e) {
108 1
            throw new InvalidSchemaException(sprintf(
109 1
                'The schema is invalid: %s',
110 1
                $e->getMessage()
111 1
            ), 0, $e);
112
        }
113
114 11
        $errors = array();
115
116 11
        if (!$this->validator->isValid()) {
117 6
            $errors = (array) $this->validator->getErrors();
118
119 6
            foreach ($errors as $key => $error) {
120 6
                $prefix = $error['property'] ? $error['property'].': ' : '';
121 6
                $errors[$key] = $prefix.$error['message'];
122
            }
123
        }
124
125 11
        return $errors;
126
    }
127
128 11
    private function assertSchemaValid($schema)
129
    {
130 11
        if (null === $this->metaSchema) {
131
            // The meta schema is obviously not validated. If we
132
            // validate it against itself, we have an endless recursion
133 11
            $this->metaSchema = json_decode(file_get_contents(__DIR__.'/../res/meta-schema.json'));
134
        }
135
136 11
        if ($schema === $this->metaSchema) {
137 11
            return;
138
        }
139
140 11
        $errors = $this->validate($schema, $this->metaSchema);
141
142 11
        if (count($errors) > 0) {
143 1
            throw new InvalidSchemaException(sprintf(
144 1
                "The schema is invalid:\n%s",
145 1
                implode("\n", $errors)
146
            ));
147
        }
148 10
    }
149
150 15
    private function loadSchema($file)
151
    {
152
        try {
153 15
            $schema = $this->resolver->resolve($file);
154 1
            $this->assertSchemaValid($schema);
155 14
        } catch (ResourceNotFoundException $e) {
156 13
            throw new InvalidSchemaException(sprintf(
157 13
                'The schema %s does not exist.',
158
                $file
159 13
            ), 0, $e);
160 1
        } catch (InvalidSchemaException $e) {
161
            throw new InvalidSchemaException(sprintf(
162
                'An error occurred while loading the schema %s: %s',
163
                $file,
164
                $e->getMessage()
165
            ), 0, $e);
166
        }
167
168 1
        return $schema;
169
    }
170
}
171