PropertiesConstraint::apply()   D
last analyzed

Complexity

Conditions 9
Paths 49

Size

Total Lines 33
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 25
CRAP Score 9

Importance

Changes 0
Metric Value
dl 0
loc 33
c 0
b 0
f 0
ccs 25
cts 25
cp 1
rs 4.909
cc 9
eloc 17
nc 49
nop 4
crap 9
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 373
    public function keywords()
31
    {
32 373
        return ['properties', 'additionalProperties', 'patternProperties'];
33
    }
34
35
    /**
36
     * {@inheritdoc}
37
     */
38 357
    public function supports($type)
39
    {
40 357
        return $type === Types::TYPE_OBJECT;
41
    }
42
43
    /**
44
     * {@inheritdoc}
45
     */
46 124
    public function normalize(stdClass $schema, Context $context, Walker $walker)
47
    {
48 124
        $this->createDefaults($schema);
49
50 124
        $context->enterNode('properties');
51 124
        $this->parsePropertiesProperty($schema, $context, $walker);
52
53 122
        $context->enterSibling('additionalProperties');
54 122
        $this->parseAdditionalPropertiesProperty($schema, $context, $walker);
55
56 121
        $context->enterSibling('patternProperties');
57 121
        $this->parsePatternPropertiesProperty($schema, $context, $walker);
58 118
        $context->leaveNode();
59 118
    }
60
61
    /**
62
     * {@inheritdoc}
63
     */
64 102
    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 102
        foreach ($instance as $property => $value) {
68 95
            $schemas = [];
69
70 95
            if (isset($schema->properties->{$property})) {
71 68
                $schemas[] = $schema->properties->{$property};
72 68
            }
73
74 95
            foreach ($schema->patternProperties as $regex => $propertySchema) {
75 34
                if (Utils::matchesRegex($property, $regex)) {
76 23
                    $schemas[] = $propertySchema;
77 23
                }
78 95
            }
79
80 95
            if (empty($schemas)) {
81 39
                if (is_object($schema->additionalProperties)) {
82 30
                    $schemas[] = $schema->additionalProperties;
83 39
                } elseif ($schema->additionalProperties === false) {
84 9
                    $context->addViolation('additional property "%s" is not allowed', [$property]);
85 9
                }
86 39
            }
87
88 95
            $context->enterNode($property);
89
90 95
            foreach ($schemas as $childSchema) {
91 93
                $walker->applyConstraints($value, $childSchema, $context);
92 95
            }
93
94 95
            $context->leaveNode();
95 102
        }
96 102
    }
97
98 124
    private function createDefaults(stdClass $schema)
99
    {
100 124
        if (!property_exists($schema, 'properties')) {
101 17
            $schema->properties = new stdClass();
102 17
        }
103
104 124
        if (!property_exists($schema, 'additionalProperties')
105 124
            || $schema->additionalProperties === true) {
106 37
            $schema->additionalProperties = new stdClass();
107 37
        }
108
109 124
        if (!property_exists($schema, 'patternProperties')) {
110 37
            $schema->patternProperties = new stdClass();
111 37
        }
112 124
    }
113
114 124
    private function parsePropertiesProperty(stdClass $schema, Context $context, Walker $walker)
115
    {
116 124
        if (!is_object($schema->properties)) {
117 1
            throw new InvalidTypeException($context, Types::TYPE_OBJECT);
118
        }
119
120 123 View Code Duplication
        foreach ($schema->properties as $property => $value) {
121 93
            $context->enterNode($property);
122
123 93
            if (!is_object($value)) {
124 1
                throw new InvalidTypeException($context, Types::TYPE_OBJECT);
125
            }
126
127 92
            $walker->parseSchema($value, $context);
128 92
            $context->leaveNode();
129 122
        }
130 122
    }
131
132 122 View Code Duplication
    private function parseAdditionalPropertiesProperty(stdClass $schema, Context $context, Walker $walker)
133
    {
134 122
        if (is_object($schema->additionalProperties)) {
135 97
            $walker->parseSchema($schema->additionalProperties, $context);
136 122
        } elseif (!is_bool($schema->additionalProperties)) {
137 1
            throw new InvalidTypeException($context, [Types::TYPE_OBJECT, Types::TYPE_BOOLEAN]);
138
        }
139 121
    }
140
141 121
    private function parsePatternPropertiesProperty(stdClass $schema, Context $context, Walker $walker)
142
    {
143 121
        if (!is_object($schema->patternProperties)) {
144 1
            throw new InvalidTypeException($context, Types::TYPE_OBJECT);
145
        }
146
147 120
        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 118
        }
161 118
    }
162
}
163