Completed
Branch develop (a0cb70)
by Mariano
06:03
created

Items::validateTuple()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 18
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 5
Bugs 3 Features 3
Metric Value
c 5
b 3
f 3
dl 0
loc 18
ccs 0
cts 11
cp 0
rs 9.2
cc 4
eloc 9
nc 3
nop 1
crap 20
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
     * @see \Mcustiel\SimpleRequest\Validator\AbstractIterableValidator::setSpecification()
44
     */
45 74
    public function setSpecification($specification = null)
46
    {
47 74
        $this->checkSpecificationIsArray($specification);
48
49 74
        if (isset($specification[self::ITEMS_INDEX])) {
50 74
            $this->setItems($specification[self::ITEMS_INDEX]);
51 74
        }
52 74
        if (isset($specification[self::ADDITIONAL_ITEMS_INDEX])) {
53 74
            $this->setAdditionalItems($specification[self::ADDITIONAL_ITEMS_INDEX]);
54 74
        }
55 74
    }
56
57
    /**
58
     * {@inheritdoc}
59
     * @see \Mcustiel\SimpleRequest\Validator\AbstractAnnotationSpecifiedValidator::validate()
60
     */
61 75
    public function validate($value)
62
    {
63 75
        if (!is_array($value)) {
64
            return false;
65
        }
66
67
        // From json-schema definition: if "items" is not present, or its value is an object,
68
        // validation of the instance always succeeds, regardless of the value of "additionalItems";
69 75
        if (empty($this->items)) {
70 75
            return true;
71
        }
72
73
        return $this->executeItemsValidation($value);
74
    }
75
76
    private function executeItemsValidation($value)
77
    {
78
        if ($this->items instanceof ValidatorInterface) {
79
            return $this->validateArray($value, $this->items);
80
        }
81
82
        // From json-schema definition: if the value of "additionalItems" is boolean value false and
83
        // the value of "items" is an array, the instance is valid if its size is less than, or
84
        // equal to, the size of "items".
85
        if ($this->additionalItems === false) {
86
            return (count($value) <= count($this->items))
87
            && $this->validateTuple($value);
88
        }
89
90
        // From json-schema definition: if the value of "additionalItems" is
91
        // boolean value true or an object, validation of the instance always succeeds;
92
        return $this->validateList($value);
93
    }
94
95
    /**
96
     * Validates each element against its corresponding validator. Then,
97
     * if additionalItems is a validator, checks the rest again those
98
     * validators.
99
     *
100
     * @param array $list
101
     *
102
     * @return bool
103
     */
104
    private function validateList(array $list)
105
    {
106
        $count = count($this->items);
107
        return $this->validateTuple($list) && (
108
            $this->additionalItems === true ||
109
            $this->validateArray(
110
                array_slice($list, $count, count($list) - $count),
111
                $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...
112
            )
113
        );
114
    }
115
116
    /**
117
     * Validates each one of the elements of the array against
118
     * its corresponding specified validator.
119
     *
120
     * @param array $tuple
121
     *
122
     * @return bool
123
     */
124
    private function validateTuple(array $tuple)
125
    {
126
        $keys = array_keys($tuple);
127
        $count = count($this->items);
128
        for ($index = 0; $index < $count; $index++) {
129
            $validator = $this->items[$index];
130
            // In the specification is not clear what to do when instance size
131
            // is less than items size. I chose to pass null and if null passes
132
            // the validation, it returns true.
133
            if (!$validator->validate(
134
                isset($tuple[$keys[$index]]) ? $tuple[$keys[$index]] : null
135
            )) {
136
                return false;
137
            }
138
        }
139
140
        return true;
141
    }
142
143
    /**
144
     * Checks and sets the specified items values.
145
     *
146
     * @param array|\Mcustiel\SimpleRequest\Interfaces\ValidatorInterface $specification
147
     */
148 74
    private function setItems($specification)
149
    {
150 74
        if ($specification instanceof ValidatorAnnotation) {
151
            $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...
152
                $specification
153
            );
154
        } else {
155 74
            $this->setSpecification($specification);
156
        }
157 74
    }
158
159
    /**
160
     * Checks and sets the specified additionalItems.
161
     *
162
     * @param bool|\Mcustiel\SimpleRequest\Interfaces\ValidatorInterface $specification
163
     */
164 74
    private function setAdditionalItems($specification)
165
    {
166 74
        if (is_bool($specification)) {
167 74
            $this->additionalItems = $specification;
168 74
        } elseif ($specification instanceof ValidatorAnnotation) {
169
            $this->additionalItems = $this->createValidatorInstanceFromAnnotation(
170
                $specification
171
            );
172
        }
173 74
    }
174
175
    /**
176
     * Validates an array against a specific validator.
177
     *
178
     * @param array $array
179
     * @param \Mcustiel\SimpleRequest\Interfaces\ValidatorInterface $validator
180
     */
181
    private function validateArray(array $array, ValidatorInterface $validator)
182
    {
183
        foreach ($array as $item) {
184
            if (!$validator->validate($item)) {
185
                return false;
186
            }
187
        }
188
189
        return true;
190
    }
191
}
192