Completed
Push — master ( b4bffd...ccaae2 )
by Stéphane
9s
created

Walker::getConstraints()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 8
ccs 5
cts 5
cp 1
rs 9.4285
cc 2
eloc 4
nc 2
nop 2
crap 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
        if ($this->isLooping($schema, $this->resolvedSchemas)) {
67 20
            return $schema;
68
        }
69
70 299
        if (!$this->resolver->hasBaseSchema()) {
71 299
            $this->resolver->setBaseSchema($schema, $uri);
72 299
        }
73
74 299
        $inScope = false;
75
76 299
        if (property_exists($schema, 'id') && is_string($schema->id)) {
77 7
            $this->resolver->enter(new Uri($schema->id));
78 7
            $inScope = true;
79 7
        }
80
81 299
        if (property_exists($schema, '$ref')) {
82 22
            $resolved = $this->resolver->resolve($schema);
83 22
            $this->resolver->enter($resolved[0], $resolved[1]);
84 22
            $schema = $this->resolveReferences($resolved[1], $resolved[0]);
85 22
            $this->resolver->leave();
86 22
        } else {
87 299
            foreach ($schema as $property => $value) {
88 299
                if (is_object($value)) {
89 114
                    $schema->{$property} = $this->resolveReferences($value, $uri);
90 299
                } elseif (is_array($value)) {
91 75
                    foreach ($value as $index => $element) {
92 73
                        if (is_object($element)) {
93 44
                            $schema->{$property}[$index] = $this->resolveReferences($element, $uri);
94 44
                        }
95 75
                    }
96 75
                }
97 299
            }
98
        }
99
100 299
        if ($inScope) {
101 7
            $this->resolver->leave();
102 7
        }
103
104 299
        return $schema;
105
    }
106
107
    /**
108
     * Recursively normalizes a given schema and validates its syntax.
109
     *
110
     * @param stdClass $schema
111
     * @param Context  $context
112
     *
113
     * @return stdClass
114
     */
115 357
    public function parseSchema(stdClass $schema, Context $context)
116
    {
117 357
        if ($this->isLooping($schema, $this->parsedSchemas)) {
118 12
            return $schema;
119
        }
120
121 357
        foreach ($this->getConstraints($schema, $context) as $constraint) {
122 357
            foreach ($constraint->keywords() as $keyword) {
123 357
                if (property_exists($schema, $keyword)) {
124 333
                    $constraint->normalize($schema, $context, $this);
125 333
                    break;
126
                }
127 357
            }
128 357
        }
129
130 357
        return $schema;
131
    }
132
133
    /**
134
     * Validates an instance against a given schema, populating a context
135
     * with encountered violations.
136
     *
137
     * @param $instance
138
     * @param stdClass $schema
139
     * @param Context  $context
140
     */
141 348
    public function applyConstraints($instance, stdClass $schema, Context $context)
142
    {
143 348
        $instanceType = Types::getPrimitiveTypeOf($instance);
144
145 348
        foreach ($this->getConstraints($schema, $context) as $constraint) {
146 348
            foreach ($constraint->keywords() as $keyword) {
147 348
                if ($constraint->supports($instanceType)) {
148 348
                    if (property_exists($schema, $keyword)) {
149 307
                        $constraint->apply($instance, $schema, $context, $this);
150 307
                        break;
151
                    }
152 348
                }
153 348
            }
154 348
        }
155 348
    }
156
157 363
    private function isLooping($item, array &$stack)
158
    {
159 363
        $isKnown = false;
160
161 363
        foreach ($stack as $knownItem) {
162 202
            if ($item === $knownItem) {
163 20
                $isKnown = true;
164 20
                break;
165
            }
166 363
        }
167
168 363
        if (!$isKnown) {
169 363
            $stack[] = $item;
170 363
        }
171
172 363
        return $isKnown;
173
    }
174
175 357
    private function getConstraints(stdClass $schema, Context $context)
176
    {
177 357
        if (property_exists($schema, '$schema')) {
178 4
            $context->setVersion($schema->{'$schema'});
179 4
        }
180
181 357
        return $this->registry->getConstraints($context->getVersion());
182
    }
183
}
184