Completed
Pull Request — master (#10)
by Jan
03:09
created

PropertiesConstraint::apply()   D

Complexity

Conditions 9
Paths 49

Size

Total Lines 34
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 27
CRAP Score 9

Importance

Changes 3
Bugs 1 Features 0
Metric Value
c 3
b 1
f 0
dl 0
loc 34
ccs 27
cts 27
cp 1
rs 4.909
cc 9
eloc 19
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 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($schema->properties, 'properties');
51 121
        $this->parsePropertiesProperty($schema, $context, $walker);
52
53 119
        $context->enterSibling($schema->additionalProperties, 'additionalProperties');
54 119
        $this->parseAdditionalPropertiesProperty($schema, $context, $walker);
55
56 118
        $context->enterSibling($schema->patternProperties, '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
            $context->enterNode($instance->{$property}, $property);
69 92
            $schemas = [];
70
71 92
            if (isset($schema->properties->{$property})) {
72 65
                $schemas[] = $schema->properties->{$property};
73 65
            }
74
75 92
            foreach ($schema->patternProperties as $regex => $propertySchema) {
76 34
                if (Utils::matchesRegex($property, $regex)) {
77 23
                    $schemas[] = $propertySchema;
78 23
                }
79 92
            }
80
81 92
            if (!$schemas) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $schemas of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
82 36
                if (is_object($schema->additionalProperties)) {
83 27
                    $schemas[] = $schema->additionalProperties;
84 36
                } elseif ($schema->additionalProperties === false) {
85 9
                    $context->leaveNode();
86 9
                    $context->addViolation('additional property "%s" is not allowed', [$property]);
87 9
                    $context->enterNode($instance->{$property}, $property);
88 9
                }
89 36
            }
90
91 92
            foreach ($schemas as $childSchema) {
92 90
                $walker->applyConstraints($value, $childSchema, $context);
93 92
            }
94
95 92
            $context->leaveNode();
96 99
        }
97 99
    }
98
99 121
    private function createDefaults(stdClass $schema)
100
    {
101 121
        if (!property_exists($schema, 'properties')) {
102 17
            $schema->properties = new stdClass();
103 17
        }
104
105 121
        if (!property_exists($schema, 'additionalProperties')
106 121
            || $schema->additionalProperties === true) {
107 33
            $schema->additionalProperties = new stdClass();
108 33
        }
109
110 121
        if (!property_exists($schema, 'patternProperties')) {
111 33
            $schema->patternProperties = new stdClass();
112 33
        }
113 121
    }
114
115 121
    private function parsePropertiesProperty(stdClass $schema, Context $context, Walker $walker)
116
    {
117 121
        if (!is_object($schema->properties)) {
118 1
            throw new InvalidTypeException($context, Types::TYPE_OBJECT);
119
        }
120
121 120 View Code Duplication
        foreach ($schema->properties as $property => $value) {
122 90
            $context->enterNode($schema->properties->{$property}, $property);
123
124 90
            if (!is_object($value)) {
125 1
                throw new InvalidTypeException($context, Types::TYPE_OBJECT);
126
            }
127
128 89
            $walker->parseSchema($value, $context);
129 89
            $context->leaveNode();
130 119
        }
131 119
    }
132
133 119 View Code Duplication
    private function parseAdditionalPropertiesProperty(stdClass $schema, Context $context, Walker $walker)
134
    {
135 119
        if (is_object($schema->additionalProperties)) {
136 94
            $walker->parseSchema($schema->additionalProperties, $context);
137 119
        } elseif (!is_bool($schema->additionalProperties)) {
138 1
            throw new InvalidTypeException($context, [Types::TYPE_OBJECT, Types::TYPE_BOOLEAN]);
139
        }
140 118
    }
141
142 118
    private function parsePatternPropertiesProperty(stdClass $schema, Context $context, Walker $walker)
143
    {
144 118
        if (!is_object($schema->patternProperties)) {
145 1
            throw new InvalidTypeException($context, Types::TYPE_OBJECT);
146
        }
147
148 117
        foreach ($schema->patternProperties as $regex => $value) {
149 42
            $context->enterNode($value, $regex);
150
151 42
            if (!Utils::isValidRegex($regex)) {
152 1
                throw new InvalidRegexException($context);
153
            }
154
155 41
            if (!is_object($value)) {
156 1
                throw new InvalidTypeException($context, Types::TYPE_OBJECT);
157
            }
158
159 40
            $walker->parseSchema($value, $context);
160 40
            $context->leaveNode();
161 115
        }
162 115
    }
163
}
164