Passed
Branch master (7e4b7e)
by Mariano
04:51
created

Items::validate()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 14
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 3.3332

Importance

Changes 4
Bugs 2 Features 2
Metric Value
c 4
b 2
f 2
dl 0
loc 14
ccs 4
cts 6
cp 0.6667
rs 9.4285
cc 3
eloc 6
nc 3
nop 1
crap 3.3332
1
<?php
2
/**
3
 * This file is part of php-simple-request.
4
 *
5
 * php-simple-request is free software: you can redistribute it and/or modify
6
 * it under the terms of the GNU Lesser General Public License as published by
7
 * the Free Software Foundation, either version 3 of the License, or
8
 * (at your option) any later version.
9
 *
10
 * php-simple-request is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with php-simple-request.  If not, see <http://www.gnu.org/licenses/>.
17
 */
18
namespace Mcustiel\SimpleRequest\Validator;
19
20
use Mcustiel\SimpleRequest\Interfaces\ValidatorInterface;
21
use Mcustiel\SimpleRequest\Annotation\ValidatorAnnotation;
22
23
/**
24
 * Checks that each element of an array validates against its corresponding
25
 * validator in a collection.
26
 * <a href="http://spacetelescope.github.io/understanding-json-schema/UnderstandingJSONSchema.pdf">Here</a>
27
 * you can see examples of use for this validator.
28
 *
29
 * @author mcustiel
30
 */
31
class Items extends AbstractIterableValidator
32
{
33
    const ITEMS_INDEX = 'items';
34
    const ADDITIONAL_ITEMS_INDEX = 'additionalItems';
35
36
    /**
37
     * @var bool|\Mcustiel\SimpleRequest\Interfaces\ValidatorInterface
38
     */
39
    private $additionalItems = true;
40
41
    /**
42
     * {@inheritdoc}
43
     *
44
     * @see \Mcustiel\SimpleRequest\Validator\AbstractIterableValidator::setSpecification()
45
     */
46 79
    public function setSpecification($specification = null)
47
    {
48 79
        $this->checkSpecificationIsArray($specification);
49
50 79
        if (isset($specification[self::ITEMS_INDEX])) {
51 79
            $this->setItems($specification[self::ITEMS_INDEX]);
52 79
        }
53 79
        if (isset($specification[self::ADDITIONAL_ITEMS_INDEX])) {
54 79
            $this->setAdditionalItems($specification[self::ADDITIONAL_ITEMS_INDEX]);
55 79
        }
56 79
    }
57
58
    /**
59
     * {@inheritdoc}
60
     *
61
     * @see \Mcustiel\SimpleRequest\Validator\AbstractAnnotationSpecifiedValidator::validate()
62
     */
63 80
    public function validate($value)
64
    {
65 80
        if (!is_array($value)) {
66
            return false;
67
        }
68
69
        // From json-schema definition: if "items" is not present, or its value is an object,
70
        // validation of the instance always succeeds, regardless of the value of "additionalItems";
71 80
        if (empty($this->items)) {
72 80
            return true;
73
        }
74
75
        return $this->executeItemsValidation($value);
76
    }
77
78
    private function executeItemsValidation($value)
79
    {
80
        if ($this->items instanceof ValidatorInterface) {
81
            return $this->validateArray($value, $this->items);
82
        }
83
84
        // From json-schema definition: if the value of "additionalItems" is boolean value false and
85
        // the value of "items" is an array, the instance is valid if its size is less than, or
86
        // equal to, the size of "items".
87
        if ($this->additionalItems === false) {
88
            return (count($value) <= count($this->items))
89
            && $this->validateTuple($value);
90
        }
91
92
        // From json-schema definition: if the value of "additionalItems" is
93
        // boolean value true or an object, validation of the instance always succeeds;
94
        return $this->validateList($value);
95
    }
96
97
    /**
98
     * Validates each element against its corresponding validator. Then,
99
     * if additionalItems is a validator, checks the rest again those
100
     * validators.
101
     *
102
     * @param array $list
103
     *
104
     * @return bool
105
     */
106
    private function validateList(array $list)
107
    {
108
        $count = count($this->items);
109
        return $this->validateTuple($list) && (
110
            $this->additionalItems === true ||
111
            $this->validateArray(
112
                array_slice($list, $count, count($list) - $count),
113
                $this->additionalItems
0 ignored issues
show
Bug introduced by
It seems like $this->additionalItems can also be of type boolean; however, Mcustiel\SimpleRequest\V...\Items::validateArray() does only seem to accept object<Mcustiel\SimpleRe...ces\ValidatorInterface>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
114
            )
115
        );
116
    }
117
118
    /**
119
     * Validates each one of the elements of the array against
120
     * its corresponding specified validator.
121
     *
122
     * @param array $tuple
123
     *
124
     * @return bool
125
     */
126
    private function validateTuple(array $tuple)
127
    {
128
        $keys = array_keys($tuple);
129
        $count = count($this->items);
130
        for ($index = 0; $index < $count; $index++) {
131
            $validator = $this->items[$index];
132
            // In the specification is not clear what to do when instance size
133
            // is less than items size. I chose to pass null and if null passes
134
            // the validation, it returns true.
135
            if (!$validator->validate(
136
                isset($tuple[$keys[$index]]) ? $tuple[$keys[$index]] : null
137
            )) {
138
                return false;
139
            }
140
        }
141
142
        return true;
143
    }
144
145
    /**
146
     * Checks and sets the specified items values.
147
     *
148
     * @param array|\Mcustiel\SimpleRequest\Interfaces\ValidatorInterface $specification
149
     */
150 79
    private function setItems($specification)
151
    {
152 79
        if ($specification instanceof ValidatorAnnotation) {
153
            $this->items = $this->createValidatorInstanceFromAnnotation(
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->createValidatorIn...otation($specification) of type object<Mcustiel\SimpleRe...ces\ValidatorInterface> is incompatible with the declared type array<integer,object<Mcu...es\ValidatorInterface>> of property $items.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
154
                $specification
155
            );
156
        } else {
157 79
            $this->setSpecification($specification);
158
        }
159 79
    }
160
161
    /**
162
     * Checks and sets the specified additionalItems.
163
     *
164
     * @param bool|\Mcustiel\SimpleRequest\Interfaces\ValidatorInterface $specification
165
     */
166 79
    private function setAdditionalItems($specification)
167
    {
168 79
        if (is_bool($specification)) {
169 79
            $this->additionalItems = $specification;
170 79
        } elseif ($specification instanceof ValidatorAnnotation) {
171
            $this->additionalItems = $this->createValidatorInstanceFromAnnotation(
172
                $specification
173
            );
174
        }
175 79
    }
176
177
    /**
178
     * Validates an array against a specific validator.
179
     *
180
     * @param array                                                 $array
181
     * @param \Mcustiel\SimpleRequest\Interfaces\ValidatorInterface $validator
182
     */
183
    private function validateArray(array $array, ValidatorInterface $validator)
184
    {
185
        foreach ($array as $item) {
186
            if (!$validator->validate($item)) {
187
                return false;
188
            }
189
        }
190
191
        return true;
192
    }
193
}
194