Completed
Push — master ( 9a0752...0003b1 )
by Stéphane
8s
created

Walker   A

Complexity

Total Complexity 29

Size/Duplication

Total Lines 172
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 100%

Importance

Changes 4
Bugs 1 Features 2
Metric Value
wmc 29
c 4
b 1
f 2
lcom 1
cbo 6
dl 0
loc 172
ccs 77
cts 77
cp 1
rs 10

7 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A resolveReferences() 0 8 1
C doResolveReferences() 0 37 11
B parseSchema() 0 17 5
B applyConstraints() 0 15 5
A isLooping() 0 17 4
A getConstraints() 0 8 2
1
<?php
2
3
/*
4
 * This file is part of the JVal package.
5
 *
6
 * For the full copyright and license information, please view the LICENSE
7
 * file that was distributed with this source code.
8
 */
9
10
namespace JVal;
11
12
use stdClass;
13
14
/**
15
 * Implements the three steps needed to perform a JSON Schema validation,
16
 * i.e. distinct methods to recursively:.
17
 *
18
 * 1) resolve JSON pointer references within schema
19
 * 2) normalize and validate the syntax of the schema
20
 * 3) validate a given instance against it
21
 */
22
class Walker
23
{
24
    /**
25
     * @var Registry
26
     */
27
    private $registry;
28
29
    /**
30
     * @var Resolver
31
     */
32
    private $resolver;
33
34
    /**
35
     * @var stdClass[]
36
     */
37
    private $parsedSchemas = [];
38
39
    /**
40
     * @var stdClass[]
41
     */
42
    private $resolvedSchemas = [];
43
44
    /**
45
     * Constructor.
46
     *
47
     * @param Registry $registry
48
     * @param Resolver $resolver
49
     */
50 485
    public function __construct(Registry $registry, Resolver $resolver)
51
    {
52 485
        $this->registry = $registry;
53 485
        $this->resolver = $resolver;
54 485
    }
55
56
    /**
57
     * Recursively resolve JSON pointer references within a given schema.
58
     *
59
     * @param stdClass $schema The schema to resolve
60
     * @param Uri      $uri    The URI of the schema
61
     *
62
     * @return stdClass
63
     */
64 299
    public function resolveReferences(stdClass $schema, Uri $uri)
65
    {
66 299
        $this->resolver->setBaseSchema($schema, $uri);
67 299
        $schema = $this->doResolveReferences($schema, $uri);
68 299
        $this->resolver->clearStack();
69
70 299
        return $schema;
71
    }
72
73
    /**
74
     * @param stdClass $schema
75
     * @param Uri      $uri
76
     *
77
     * @return stdClass
78
     */
79 299
    private function doResolveReferences(stdClass $schema, Uri $uri)
80
    {
81 299
        if ($this->isLooping($schema, $this->resolvedSchemas)) {
82 20
            return $schema;
83
        }
84
85 299
        $inScope = false;
86 299
        if (property_exists($schema, 'id') && is_string($schema->id)) {
87 7
            $this->resolver->enter(new Uri($schema->id));
88 7
            $inScope = true;
89 7
        }
90
91 299
        if (property_exists($schema, '$ref')) {
92 22
            $resolved = $this->resolver->resolve($schema);
93 22
            $this->resolver->enter($resolved[0], $resolved[1]);
94 22
            $schema = $this->doResolveReferences($resolved[1], $resolved[0]);
95 22
            $this->resolver->leave();
96 22
        } else {
97 299
            foreach ($schema as $property => $value) {
98 299
                if (is_object($value)) {
99 114
                    $schema->{$property} = $this->doResolveReferences($value, $uri);
100 299
                } elseif (is_array($value)) {
101 75
                    foreach ($value as $index => $element) {
102 73
                        if (is_object($element)) {
103 44
                            $schema->{$property}[$index] = $this->doResolveReferences($element, $uri);
104 44
                        }
105 75
                    }
106 75
                }
107 299
            }
108
        }
109
110 299
        if ($inScope) {
111 7
            $this->resolver->leave();
112 7
        }
113
114 299
        return $schema;
115
    }
116
117
    /**
118
     * Recursively normalizes a given schema and validates its syntax.
119
     *
120
     * @param stdClass $schema
121
     * @param Context  $context
122
     *
123
     * @return stdClass
124
     */
125 357
    public function parseSchema(stdClass $schema, Context $context)
126
    {
127 357
        if ($this->isLooping($schema, $this->parsedSchemas)) {
128 12
            return $schema;
129
        }
130
131 357
        foreach ($this->getConstraints($schema, $context) as $constraint) {
132 357
            foreach ($constraint->keywords() as $keyword) {
133 357
                if (property_exists($schema, $keyword)) {
134 333
                    $constraint->normalize($schema, $context, $this);
135 333
                    break;
136
                }
137 357
            }
138 357
        }
139
140 357
        return $schema;
141
    }
142
143
    /**
144
     * Validates an instance against a given schema, populating a context
145
     * with encountered violations.
146
     *
147
     * @param $instance
148
     * @param stdClass $schema
149
     * @param Context  $context
150
     */
151 348
    public function applyConstraints($instance, stdClass $schema, Context $context)
152
    {
153 348
        $instanceType = Types::getPrimitiveTypeOf($instance);
154
155 348
        foreach ($this->getConstraints($schema, $context) as $constraint) {
156 348
            foreach ($constraint->keywords() as $keyword) {
157 348
                if ($constraint->supports($instanceType)) {
158 348
                    if (property_exists($schema, $keyword)) {
159 307
                        $constraint->apply($instance, $schema, $context, $this);
160 307
                        break;
161
                    }
162 348
                }
163 348
            }
164 348
        }
165 348
    }
166
167 363
    private function isLooping($item, array &$stack)
168
    {
169 363
        $isKnown = false;
170
171 363
        foreach ($stack as $knownItem) {
172 202
            if ($item === $knownItem) {
173 20
                $isKnown = true;
174 20
                break;
175
            }
176 363
        }
177
178 363
        if (!$isKnown) {
179 363
            $stack[] = $item;
180 363
        }
181
182 363
        return $isKnown;
183
    }
184
185 357
    private function getConstraints(stdClass $schema, Context $context)
186
    {
187 357
        if (property_exists($schema, '$schema')) {
188 4
            $context->setVersion($schema->{'$schema'});
189 4
        }
190
191 357
        return $this->registry->getConstraints($context->getVersion());
192
    }
193
}
194