Failed Conditions
Push — master ( b83973...c49005 )
by Bernhard
23:36 queued 21:25
created

src/JsonValidator.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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\Retrievers\UriRetrieverInterface;
18
use JsonSchema\Uri\UriResolver;
19
use JsonSchema\Uri\UriRetriever;
20
use JsonSchema\UriResolverInterface;
21
use JsonSchema\Validator;
22
use Webmozart\PathUtil\Path;
23
24
/**
25
 * Validates decoded JSON values against a JSON schema.
26
 *
27
 * This class is a wrapper for {@link Validator} that adds exceptions and
28
 * validation of schema files. A few edge cases that are not handled by
29
 * {@link Validator} are handled by this class.
30
 *
31
 * @since  1.0
32
 *
33
 * @author Bernhard Schussek <[email protected]>
34
 */
35
class JsonValidator
36
{
37
    /**
38
     * The schema used for validating schemas.
39
     *
40
     * @var object|null
41
     */
42
    private $metaSchema;
43
44
    /**
45
     * Validator instance used for validation.
46
     *
47
     * @var Validator
48
     */
49
    private $validator;
50
51
    /**
52
     * URI retriever for fetching JSON schemas.
53
     *
54
     * @var UriRetrieverInterface
55
     */
56
    private $uriRetriever;
57
58
    /**
59
     * URI resolver for JSON schemas.
60
     *
61
     * @var UriResolverInterface
62
     */
63
    private $uriResolver;
64
65
    /**
66
     * JsonValidator constructor.
67
     *
68
     * @param Validator|null            $validator    JsonSchema\Validator
69
     *                                                instance to use
70
     * @param UriRetriever|null         $uriRetriever The retriever for fetching
71
     *                                                JSON schemas
72
     * @param UriResolverInterface|null $uriResolver  The resolver for URIs
73
     */
74 111
    public function __construct(Validator $validator = null, UriRetriever $uriRetriever = null, UriResolverInterface $uriResolver = null)
75
    {
76 111
        $this->validator = $validator ?: new Validator();
77 111
        $this->uriRetriever = $uriRetriever ?: new UriRetriever();
0 ignored issues
show
Documentation Bug introduced by
It seems like $uriRetriever ?: new \Js...hema\Uri\UriRetriever() of type object<JsonSchema\Uri\UriRetriever> is incompatible with the declared type object<JsonSchema\Uri\Re...\UriRetrieverInterface> of property $uriRetriever.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
78 111
        $this->uriResolver = $uriResolver ?: new UriResolver();
79 111
    }
80
81
    /**
82
     * Validates JSON data against a schema.
83
     *
84
     * The schema may be passed as file path or as object returned from
85
     * `json_decode($schemaFile)`.
86
     *
87
     * @param mixed              $data   The decoded JSON data
88
     * @param string|object|null $schema The schema file or object. If `null`,
89
     *                                   the validator will look for a `$schema`
90
     *                                   property
91
     *
92
     * @return string[] The errors found during validation. Returns an empty
93
     *                  array if no errors were found
94
     *
95
     * @throws InvalidSchemaException If the schema is invalid
96
     */
97 31
    public function validate($data, $schema = null)
98
    {
99 31
        if (null === $schema && isset($data->{'$schema'})) {
100 1
            $schema = $data->{'$schema'};
101
        }
102
103 31
        if (is_string($schema)) {
104 18
            $schema = $this->loadSchema($schema);
105 29
        } elseif (is_object($schema)) {
106 26
            $this->assertSchemaValid($schema);
107
        } else {
108 3
            throw new InvalidSchemaException(sprintf(
109
                'The schema must be given as string, object or in the "$schema" '.
110 3
                'property of the JSON data. Got: %s',
111 3
                is_object($schema) ? get_class($schema) : gettype($schema)
112
            ));
113
        }
114
115 26
        $this->validator->reset();
116
117
        try {
118 26
            $this->validator->check($data, $schema);
119 1
        } catch (InvalidArgumentException $e) {
120 1
            throw new InvalidSchemaException(sprintf(
121 1
                'The schema is invalid: %s',
122 1
                $e->getMessage()
123 1
            ), 0, $e);
124
        }
125
126 26
        $errors = array();
127
128 26
        if (!$this->validator->isValid()) {
129 15
            $errors = (array) $this->validator->getErrors();
130
131 15
            foreach ($errors as $key => $error) {
132 15
                $prefix = $error['property'] ? $error['property'].': ' : '';
133 15
                $errors[$key] = $prefix.$error['message'];
134
            }
135
        }
136
137 26
        return $errors;
138
    }
139
140 26
    private function assertSchemaValid($schema)
141
    {
142 26
        if (null === $this->metaSchema) {
143
            // The meta schema is obviously not validated. If we
144
            // validate it against itself, we have an endless recursion
145 26
            $this->metaSchema = json_decode(file_get_contents(__DIR__.'/../res/meta-schema.json'));
146
        }
147
148 26
        if ($schema === $this->metaSchema) {
149 26
            return;
150
        }
151
152 26
        $errors = $this->validate($schema, $this->metaSchema);
153
154 26
        if (count($errors) > 0) {
155 3
            throw new InvalidSchemaException(sprintf(
156 3
                "The schema is invalid:\n%s",
157 3
                implode("\n", $errors)
158
            ));
159
        }
160 23
    }
161
162 18
    private function loadSchema($file)
163
    {
164
        // Retrieve schema and cache in UriRetriever
165 18
        $file = Path::canonicalize($file);
166
167
        // Add file:// scheme if necessary
168 18
        if (false === strpos($file, '://')) {
169 16
            $file = 'file://'.$file;
170
        }
171
172
        // Resolve references to other schemas
173 18
        $resolver = new RefResolver($this->uriRetriever, $this->uriResolver);
174
175
        try {
176 18
            $schema = $resolver->resolve($file);
177 2
        } catch (ResourceNotFoundException $e) {
178 2
            throw new InvalidSchemaException(sprintf(
179 2
                'The schema %s does not exist.',
180
                $file
181 2
            ), 0, $e);
182
        }
183
184
        try {
185 16
            $this->assertSchemaValid($schema);
186 2
        } catch (InvalidSchemaException $e) {
187 2
            throw new InvalidSchemaException(sprintf(
188 2
                'An error occurred while loading the schema %s: %s',
189
                $file,
190 2
                $e->getMessage()
191 2
            ), 0, $e);
192
        }
193
194 14
        return $schema;
195
    }
196
}
197