Completed
Push — master ( fdc4c7...a2204f )
by Stéphane
7s
created

PropertiesConstraint::keywords()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 1
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\Constraint;
11
12
use JVal\Constraint;
13
use JVal\Context;
14
use JVal\Exception\Constraint\InvalidRegexException;
15
use JVal\Exception\Constraint\InvalidTypeException;
16
use JVal\Types;
17
use JVal\Utils;
18
use JVal\Walker;
19
use stdClass;
20
21
/**
22
 * Constraint for the "properties", "additionalProperties" and
23
 * "patternProperties" keywords.
24
 */
25
class PropertiesConstraint implements Constraint
26
{
27
    /**
28
     * {@inheritdoc}
29
     */
30 357
    public function keywords()
31
    {
32 357
        return ['properties', 'additionalProperties', 'patternProperties'];
33
    }
34
35
    /**
36
     * {@inheritdoc}
37
     */
38 348
    public function supports($type)
39
    {
40 348
        return $type === Types::TYPE_OBJECT;
41
    }
42
43
    /**
44
     * {@inheritdoc}
45
     */
46 121
    public function normalize(stdClass $schema, Context $context, Walker $walker)
47
    {
48 121
        $this->createDefaults($schema);
49
50 121
        $context->enterNode('properties');
51 121
        $this->parsePropertiesProperty($schema, $context, $walker);
52
53 119
        $context->enterSibling('additionalProperties');
54 119
        $this->parseAdditionalPropertiesProperty($schema, $context, $walker);
55
56 118
        $context->enterSibling('patternProperties');
57 118
        $this->parsePatternPropertiesProperty($schema, $context, $walker);
58 115
        $context->leaveNode();
59 115
    }
60
61
    /**
62
     * {@inheritdoc}
63
     */
64 99
    public function apply($instance, stdClass $schema, Context $context, Walker $walker)
65
    {
66
        // implementation of the algorithms described in 5.4.4.4 and in 8.3
67 99
        foreach ($instance as $property => $value) {
68 92
            $schemas = [];
69
70 92
            if (isset($schema->properties->{$property})) {
71 65
                $schemas[] = $schema->properties->{$property};
72 65
            }
73
74 92
            foreach ($schema->patternProperties as $regex => $propertySchema) {
75 34
                if (Utils::matchesRegex($property, $regex)) {
76 23
                    $schemas[] = $propertySchema;
77 23
                }
78 92
            }
79
80 92
            if (empty($schemas)) {
81 36
                if (is_object($schema->additionalProperties)) {
82 27
                    $schemas[] = $schema->additionalProperties;
83 36
                } elseif ($schema->additionalProperties === false) {
84 9
                    $context->addViolation('additional property "%s" is not allowed', [$property]);
85 9
                }
86 36
            }
87
88 92
            $context->enterNode($property);
89
90 92
            foreach ($schemas as $childSchema) {
91 90
                $walker->applyConstraints($value, $childSchema, $context);
92 92
            }
93
94 92
            $context->leaveNode();
95 99
        }
96 99
    }
97
98 121
    private function createDefaults(stdClass $schema)
99
    {
100 121
        if (!property_exists($schema, 'properties')) {
101 17
            $schema->properties = new stdClass();
102 17
        }
103
104 121
        if (!property_exists($schema, 'additionalProperties')
105 121
            || $schema->additionalProperties === true) {
106 33
            $schema->additionalProperties = new stdClass();
107 33
        }
108
109 121
        if (!property_exists($schema, 'patternProperties')) {
110 33
            $schema->patternProperties = new stdClass();
111 33
        }
112 121
    }
113
114 121
    private function parsePropertiesProperty(stdClass $schema, Context $context, Walker $walker)
115
    {
116 121
        if (!is_object($schema->properties)) {
117 1
            throw new InvalidTypeException($context, Types::TYPE_OBJECT);
118
        }
119
120 120 View Code Duplication
        foreach ($schema->properties as $property => $value) {
121 90
            $context->enterNode($property);
122
123 90
            if (!is_object($value)) {
124 1
                throw new InvalidTypeException($context, Types::TYPE_OBJECT);
125
            }
126
127 89
            $walker->parseSchema($value, $context);
128 89
            $context->leaveNode();
129 119
        }
130 119
    }
131
132 119 View Code Duplication
    private function parseAdditionalPropertiesProperty(stdClass $schema, Context $context, Walker $walker)
133
    {
134 119
        if (is_object($schema->additionalProperties)) {
135 94
            $walker->parseSchema($schema->additionalProperties, $context);
136 119
        } elseif (!is_bool($schema->additionalProperties)) {
137 1
            throw new InvalidTypeException($context, [Types::TYPE_OBJECT, Types::TYPE_BOOLEAN]);
138
        }
139 118
    }
140
141 118
    private function parsePatternPropertiesProperty(stdClass $schema, Context $context, Walker $walker)
142
    {
143 118
        if (!is_object($schema->patternProperties)) {
144 1
            throw new InvalidTypeException($context, Types::TYPE_OBJECT);
145
        }
146
147 117
        foreach ($schema->patternProperties as $regex => $value) {
148 42
            $context->enterNode($regex);
149
150 42
            if (!Utils::isValidRegex($regex)) {
151 1
                throw new InvalidRegexException($context);
152
            }
153
154 41
            if (!is_object($value)) {
155 1
                throw new InvalidTypeException($context, Types::TYPE_OBJECT);
156
            }
157
158 40
            $walker->parseSchema($value, $context);
159 40
            $context->leaveNode();
160 115
        }
161 115
    }
162
}
163