Completed
Push — master ( 8826df...56530a )
by Bernhard
02:42
created

JsonValidator::loadSchema()   B

Complexity

Conditions 3
Paths 3

Size

Total Lines 30
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 19
CRAP Score 3

Importance

Changes 2
Bugs 0 Features 1
Metric Value
c 2
b 0
f 1
dl 0
loc 30
ccs 19
cts 19
cp 1
rs 8.8571
cc 3
eloc 19
nc 3
nop 1
crap 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\RefResolver;
16
use JsonSchema\Uri\UriRetriever;
17
use JsonSchema\Validator;
18
19
/**
20
 * Validates decoded JSON values against a JSON schema.
21
 *
22
 * This class is a wrapper for {@link Validator} that adds exceptions and
23
 * validation of schema files. A few edge cases that are not handled by
24
 * {@link Validator} are handled by this class.
25
 *
26
 * @since  1.0
27
 *
28
 * @author Bernhard Schussek <[email protected]>
29
 */
30
class JsonValidator
31
{
32
    /**
33
     * The schema used for validating schemas.
34
     *
35
     * @var object|null
36
     */
37
    private $metaSchema;
38
39
    /**
40
     * Validator instance used for validation.
41
     *
42
     * @var Validator
43
     */
44
    private $validator;
45
46
    /**
47
     * JsonValidator constructor.
48
     *
49
     * @param Validator|null $validator JsonSchema\Validator instance to use.
50
     */
51 105
    public function __construct(Validator $validator = null)
52
    {
53 105
        $this->validator = $validator ?: new Validator();
54 105
    }
55
56
    /**
57
     * Validates JSON data against a schema.
58
     *
59
     * The schema may be passed as file path or as object returned from
60
     * `json_decode($schemaFile)`.
61
     *
62
     * @param mixed         $data   The decoded JSON data.
63
     * @param string|object $schema The schema file or object.
64
     *
65
     * @return string[] The errors found during validation. Returns an empty
66
     *                  array if no errors were found.
67
     *
68
     * @throws InvalidSchemaException If the schema is invalid.
69
     */
70 25
    public function validate($data, $schema)
71
    {
72 25
        if (is_string($schema)) {
73 14
            $schema = $this->loadSchema($schema);
74 10
        } else {
75 23
            $this->assertSchemaValid($schema);
76
        }
77
78 23
        $this->validator->reset();
79
80
        try {
81 23
            $this->validator->check($data, $schema);
82 23
        } catch (InvalidArgumentException $e) {
83 1
            throw new InvalidSchemaException(sprintf(
84 1
                'The schema is invalid: %s',
85 1
                $e->getMessage()
86 1
            ), 0, $e);
87
        }
88
89 23
        $errors = array();
90
91 23
        if (!$this->validator->isValid()) {
92 15
            $errors = (array) $this->validator->getErrors();
93
94 15
            foreach ($errors as $key => $error) {
95 15
                $prefix = $error['property'] ? $error['property'].': ' : '';
96 15
                $errors[$key] = $prefix.$error['message'];
97 15
            }
98 15
        }
99
100 23
        return $errors;
101
    }
102
103 23
    private function assertSchemaValid($schema)
104
    {
105 23
        if (null === $this->metaSchema) {
106
            // The meta schema is obviously not validated. If we
107
            // validate it against itself, we have an endless recursion
108 23
            $this->metaSchema = json_decode(file_get_contents(__DIR__.'/../res/meta-schema.json'));
109 23
        }
110
111 23
        if ($schema === $this->metaSchema) {
112 23
            return;
113
        }
114
115 23
        $errors = $this->validate($schema, $this->metaSchema);
116
117 23
        if (count($errors) > 0) {
118 4
            throw new InvalidSchemaException(sprintf(
119 4
                "The schema is invalid:\n%s",
120 4
                implode("\n", $errors)
121 4
            ));
122
        }
123
124
        // not caught by justinrainbow/json-schema
125 19
        if (!is_object($schema)) {
126
            throw new InvalidSchemaException(sprintf(
127
                'The schema must be an object. Got: %s',
128
                $schema,
129
                gettype($schema)
130
            ));
131
        }
132 19
    }
133
134 14
    private function loadSchema($file)
135
    {
136 14
        if (!file_exists($file)) {
137 2
            throw new InvalidSchemaException(sprintf(
138 2
                'The schema file %s does not exist.',
139 1
                $file
140 2
            ));
141
        }
142
143
        // Retrieve schema and cache in UriRetriever
144 12
        $file = realpath($file);
145 12
        $retriever = new UriRetriever();
146 12
        $schema = $retriever->retrieve('file://'.$file);
147
148
        // Resolve references to other schemas
149 12
        $resolver = new RefResolver($retriever);
150 12
        $resolver->resolve($schema, 'file://'.dirname($file));
151
152
        try {
153 12
            $this->assertSchemaValid($schema);
154 12
        } catch (InvalidSchemaException $e) {
155 2
            throw new InvalidSchemaException(sprintf(
156 2
                'An error occurred while loading the schema %s: %s',
157 2
                $file,
158 2
                $e->getMessage()
159 2
            ), 0, $e);
160
        }
161
162 10
        return $schema;
163
    }
164
}
165