Completed
Pull Request — master (#27)
by Chris
09:19
created

JsonValidator::validate()   C

Complexity

Conditions 11
Paths 18

Size

Total Lines 44
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 21
CRAP Score 11.8604

Importance

Changes 4
Bugs 0 Features 1
Metric Value
c 4
b 0
f 1
dl 0
loc 44
ccs 21
cts 26
cp 0.8077
rs 5.2653
cc 11
eloc 29
nc 18
nop 2
crap 11.8604

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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\Uri\UriRetriever;
17
use JsonSchema\Validator;
18
use Webmozart\PathUtil\Path;
19
20
/**
21
 * Validates decoded JSON values against a JSON schema.
22
 *
23
 * This class is a wrapper for {@link Validator} that adds exceptions and
24
 * validation of schema files. A few edge cases that are not handled by
25
 * {@link Validator} are handled by this class.
26
 *
27
 * @since  1.0
28
 *
29
 * @author Bernhard Schussek <[email protected]>
30
 */
31
class JsonValidator
32
{
33
    /**
34
     * The schema used for validating schemas.
35
     *
36
     * @var object|null
37
     */
38
    private $metaSchema;
39
40
    /**
41
     * Validator instance used for validation.
42
     *
43
     * @var Validator
44
     */
45
    private $validator;
46
47
    /**
48
     * JsonValidator constructor.
49
     *
50
     * @param Validator|null            $validator    JsonSchema\Validator
51
     *                                                instance to use
52
     * @param UriRetriever|null         $uriRetriever The retriever for fetching
53
     *                                                JSON schemas
54
     */
55 113
    public function __construct(Validator $validator = null, UriRetriever $uriRetriever = null)
56
    {
57 113
        $this->validator = $validator ?: new Validator();
58 113
        if($uriRetriever) {
59 3
            $this->validator->setUriRetriever($uriRetriever);
60
        }
61 113
    }
62
63
    /**
64
     * Validates JSON data against a schema.
65
     *
66
     * The schema may be passed as file path or as object returned from
67
     * `json_decode($schemaFile)`.
68
     *
69
     * @param mixed              $data   The decoded JSON data
70
     * @param string|object|null $schema The schema file or object. If `null`,
71
     *                                   the validator will look for a `$schema`
72
     *                                   property
73
     *
74
     * @return string[] The errors found during validation. Returns an empty
75
     *                  array if no errors were found
76
     *
77
     * @throws InvalidSchemaException If the schema is invalid
78
     */
79 29
    public function validate($data, $schema = null)
80
    {
81 29
        if (null === $schema && isset($data->{'$schema'})) {
82 1
            $schema = $data->{'$schema'};
83
        }
84
85 29
        if (is_string($schema)) {
86 16
            $schema = $this->loadSchema($schema);
87 29
        } elseif (is_object($schema)) {
88 26
            $this->assertSchemaValid($schema);
89
        } else {
90 3
            throw new InvalidSchemaException(sprintf(
91
                'The schema must be given as string, object or in the "$schema" '.
92 3
                'property of the JSON data. Got: %s',
93 3
                is_object($schema) ? get_class($schema) : gettype($schema)
94
            ));
95
        }
96
97 26
        $this->validator->reset();
98
99
        try {
100 26
            $this->validator->check($data, $schema);
101 2
        } catch (ResourceNotFoundException $e) {
102 2
            throw new InvalidSchemaException($e->getMessage(), $e->getCode(), $e);
103
        } catch (InvalidArgumentException $e) {
104
            throw new InvalidSchemaException(sprintf(
105
                'The schema is invalid: %s',
106
                $e->getMessage()
107
            ), 0, $e);
108
        }
109
110 26
        $errors = array();
111
112 26
        if (!$this->validator->isValid()) {
113 14
            $errors = (array) $this->validator->getErrors();
114
115 14
            foreach ($errors as $key => $error) {
116 14
                $prefix = $error['property'] ? $error['property'].': ' : '';
117 14
                $errors[$key] = $prefix.$error['message'];
118
            }
119
        }
120
121 26
        return $errors;
122
    }
123
124 26
    private function assertSchemaValid($schema)
125
    {
126 26
        if (null === $this->metaSchema) {
127
            // The meta schema is obviously not validated. If we
128
            // validate it against itself, we have an endless recursion
129 26
            $this->metaSchema = json_decode(file_get_contents(__DIR__.'/../res/meta-schema.json'));
130
131 26
            $this->validator->getSchemaStorage()->addSchema('http://json-schema.org/draft-04/schema', $this->metaSchema);
132
        }
133
134 26
        if ($schema === $this->metaSchema) {
135 26
            return;
136
        }
137
138 26
        $errors = $this->validate($schema, $this->metaSchema);
139
140 26
        if (count($errors) > 0) {
141 2
            throw new InvalidSchemaException(sprintf(
142 2
                "The schema is invalid:\n%s",
143 2
                implode("\n", $errors)
144
            ));
145
        }
146
147 24
        if(!isset($schema->{'$ref'})) {
148 8
            $this->validator->getSchemaStorage()->addSchema($schema->id, $schema);
149
        }
150 24
    }
151
152 16
    private function loadSchema($file)
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
153
    {
154
        // Retrieve schema and cache in UriRetriever
155 16
        $file = Path::canonicalize($file);
156
157
        // Add file:// scheme if necessary
158 16
        if (false === strpos($file, '://')) {
159 14
            $file = 'file://'.$file;
160
        }
161
162 16
        $schema = (object) array('$ref' => $file);
163
164
        try {
165 16
            $this->assertSchemaValid($schema);
166
        } catch (InvalidSchemaException $e) {
167
            throw new InvalidSchemaException(sprintf(
168
                'An error occurred while loading the schema %s: %s',
169
                $file,
170
                $e->getMessage()
171
            ), 0, $e);
172
        }
173
174 16
        return $schema;
175
    }
176
}
177