Failed Conditions
Pull Request — master (#20)
by Chris
05:31
created

src/JsonValidator.php (3 issues)

Labels
Severity

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\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 and resolving JSON schemas.
51
     *
52
     * @var RefResolver
53
     */
54
    private $refResolver;
55
56
    /**
57
     * JsonValidator constructor.
58
     *
59
     * @param Validator|null $validator JsonSchema\Validator instance to use.
60
     */
61 111
    public function __construct(Validator $validator = null, UriRetriever $uriRetriever = null, UriResolver $uriResolver = null)
62
    {
63 111
        $this->validator = $validator ?: new Validator();
64 111
        if(!interface_exists('JsonSchema\UriRetrieverInterface')) {
65
            // Remove when justinrainbow/json-schema dependency is ^2.0
66
            $this->refResolver = new RefResolver($uriRetriever);
0 ignored issues
show
The call to RefResolver::__construct() misses a required argument $uriResolver.

This check looks for function calls that miss required arguments.

Loading history...
It seems like $uriRetriever defined by parameter $uriRetriever on line 61 can be null; however, JsonSchema\RefResolver::__construct() does not accept null, maybe add an additional type check?

It seems like you allow that null is being passed for a parameter, however the function which is called does not seem to accept null.

We recommend to add an additional type check (or disallow null for the parameter):

function notNullable(stdClass $x) { }

// Unsafe
function withoutCheck(stdClass $x = null) {
    notNullable($x);
}

// Safe - Alternative 1: Adding Additional Type-Check
function withCheck(stdClass $x = null) {
    if ($x instanceof stdClass) {
        notNullable($x);
    }
}

// Safe - Alternative 2: Changing Parameter
function withNonNullableParam(stdClass $x) {
    notNullable($x);
}
Loading history...
67
        } else {
68 111
            $this->refResolver = new RefResolver($uriRetriever ?: new UriRetriever(), $uriResolver ?: new UriResolver());
69
        }
70 111
    }
71
72
    /**
73
     * Validates JSON data against a schema.
74
     *
75
     * The schema may be passed as file path or as object returned from
76
     * `json_decode($schemaFile)`.
77
     *
78
     * @param mixed              $data   The decoded JSON data.
79
     * @param string|object|null $schema The schema file or object. If `null`,
80
     *                                   the validator will look for a `$schema`
81
     *                                   property.
82
     *
83
     * @return string[] The errors found during validation. Returns an empty
84
     *                  array if no errors were found.
85
     *
86
     * @throws InvalidSchemaException If the schema is invalid.
87
     */
88 31
    public function validate($data, $schema = null)
89
    {
90 31
        if (null === $schema && isset($data->{'$schema'})) {
91 1
            $schema = $data->{'$schema'};
92
        }
93
94 31
        if (is_string($schema)) {
95 18
            $schema = $this->loadSchema($schema);
96 29
        } elseif (is_object($schema)) {
97 26
            $this->assertSchemaValid($schema);
98
        } else {
99 3
            throw new InvalidSchemaException(sprintf(
100
                'The schema must be given as string, object or in the "$schema" '.
101 3
                'property of the JSON data. Got: %s',
102 3
                is_object($schema) ? get_class($schema) : gettype($schema)
103
            ));
104
        }
105
106 26
        $this->validator->reset();
107
108
        try {
109 26
            $this->validator->check($data, $schema);
110 1
        } catch (InvalidArgumentException $e) {
111 1
            throw new InvalidSchemaException(sprintf(
112 1
                'The schema is invalid: %s',
113 1
                $e->getMessage()
114 1
            ), 0, $e);
115
        }
116
117 26
        $errors = array();
118
119 26
        if (!$this->validator->isValid()) {
120 15
            $errors = (array) $this->validator->getErrors();
121
122 15
            foreach ($errors as $key => $error) {
123 15
                $prefix = $error['property'] ? $error['property'].': ' : '';
124 15
                $errors[$key] = $prefix.$error['message'];
125
            }
126
        }
127
128 26
        return $errors;
129
    }
130
131 26
    private function assertSchemaValid($schema)
132
    {
133 26
        if (null === $this->metaSchema) {
134
            // The meta schema is obviously not validated. If we
135
            // validate it against itself, we have an endless recursion
136 26
            $this->metaSchema = json_decode(file_get_contents(__DIR__.'/../res/meta-schema.json'));
137
        }
138
139 26
        if ($schema === $this->metaSchema) {
140 26
            return;
141
        }
142
143 26
        $errors = $this->validate($schema, $this->metaSchema);
144
145 26
        if (count($errors) > 0) {
146 3
            throw new InvalidSchemaException(sprintf(
147 3
                "The schema is invalid:\n%s",
148 3
                implode("\n", $errors)
149
            ));
150
        }
151 23
    }
152
153 18
    private function loadSchema($file)
154
    {
155
        // Retrieve schema and cache in UriRetriever
156 18
        $file = Path::canonicalize($file);
157
158
        // Add file:// scheme if necessary
159 18
        if (false === strpos($file, '://')) {
160 16
            $file = 'file://'.$file;
161
        }
162
163
        try {
164 18
            if(!interface_exists('JsonSchema\UriRetrieverInterface')) {
165
                // Remove when justinrainbow/json-schema dependency is ^2.0
166
                $schema = $this->refResolver->getUriRetriever()->retrieve($file);
0 ignored issues
show
The method getUriRetriever() does not seem to exist on object<JsonSchema\RefResolver>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
167
                $this->refResolver->resolve($schema, $file);
168
            } else {
169 18
                $schema = $this->refResolver->resolve($file);
170
            }
171 2
        } catch (ResourceNotFoundException $e) {
172 2
            throw new InvalidSchemaException(sprintf(
173 2
                'The schema %s does not exist.',
174
                $file
175 2
            ), 0, $e);
176
        }
177
178
        try {
179 16
            $this->assertSchemaValid($schema);
180 2
        } catch (InvalidSchemaException $e) {
181 2
            throw new InvalidSchemaException(sprintf(
182 2
                'An error occurred while loading the schema %s: %s',
183
                $file,
184 2
                $e->getMessage()
185 2
            ), 0, $e);
186
        }
187
188 14
        return $schema;
189
    }
190
}
191