Collection::validate()   D
last analyzed

Complexity

Conditions 10
Paths 6

Size

Total Lines 39
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
c 3
b 0
f 0
dl 0
loc 39
rs 4.8196
cc 10
eloc 20
nc 6
nop 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Copyright (c) 2014-2016 Ryan Parman.
4
 *
5
 * Permission is hereby granted, free of charge, to any person obtaining a copy
6
 * of this software and associated documentation files (the "Software"), to deal
7
 * in the Software without restriction, including without limitation the rights
8
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
 * copies of the Software, and to permit persons to whom the Software is
10
 * furnished to do so, subject to the following conditions:
11
 *
12
 * The above copyright notice and this permission notice shall be included in
13
 * all copies or substantial portions of the Software.
14
 *
15
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
 * THE SOFTWARE.
22
 *
23
 * http://opensource.org/licenses/MIT
24
 */
25
26
namespace Skyzyx\StrongTypes;
27
28
use \ArrayAccess;
29
use \ArrayObject;
30
use \Countable;
31
use \DomainException;
32
use \IteratorAggregate;
33
use \InvalidArgumentException;
34
use \UnexpectedValueException;
35
36
class Collection extends AbstractShape implements CollectionInterface, IteratorAggregate, ArrayAccess, Countable, MultiValueInterface
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 133 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
37
{
38
    use Collection\AliasTrait;
39
    use Collection\ArrayAccessTrait;
40
    use Collection\CountableTrait;
41
    use Collection\IteratorAggregateTrait;
42
    use Collection\SeekableInterfaceTrait;
43
44
    /** @var \ArrayObject */
45
    private $collection;
46
47
    /** @var array */
48
    private $required_keys = [];
49
50
51
    /**************************************************************************/
52
    // CONSTRUCTOR
53
54
    /**
55
     * Constructs a new instance of this class.
56
     *
57
     * @param array $value The standard array to convert into a collection. The default value is an empty string.
58
     */
59
    public function __construct($value = [])
60
    {
61
        $this->value = $value;
62
        $this->validate();
63
64
        $this->collection = new ArrayObject($value, ArrayObject::ARRAY_AS_PROPS);
65
        $this->iterator   = $this->collection->getIterator();
66
    }
67
68
69
    /**************************************************************************/
70
    // ShapeInterface
71
72
    /**
73
     * {@inheritdoc}
74
     */
75
    public function validate()
76
    {
77
        if ($this->value !== null && !is_array($this->value)) {
78
            throw new UnexpectedValueException(
79
                sprintf(self::TYPE_EXCEPTION_MESSAGE, get_called_class(), 'array', gettype($this->value))
80
            );
81
        }
82
83
        $map = $this->validateValue();
84
        $this->storeRequiredKeys($this->requiredKeys());
85
86
        if (is_array($this->value)) {
87
            foreach ($this->value as $k => $v) {
88
                // If the key is defined...
89
                if (isset($map[$k])) {
90
                    // ...validate its shape.
91
                    if (!($v instanceof $map[$k] || gettype($v) === $this->getNativeType($map[$k]))) {
92
                        throw new InvalidArgumentException(
93
                            sprintf('The %s shape expects the %s key to be of type %s.',
94
                                get_called_class(), $k, get_class($map[$k]))
95
                        );
96
                    }
97
                }
98
99
                if ($this->isRequiredKey($k)) {
100
                    $this->pluckFromRequiredKeys($k);
101
                }
102
            }
103
        }
104
105
        if (count($this->required_keys) !== 0) {
106
            throw new DomainException(
107
                sprintf('The %s collection is missing one or more required keys: %s.',
108
                    get_called_class(),
109
                    implode(', ', $this->getRemainingRequiredKeys())
110
                )
111
            );
112
        }
113
    }
114
115
    /**
116
     * {@inheritdoc}
117
     */
118
    public function getValue()
119
    {
120
        return $this->value;
121
    }
122
123
    /**
124
     * The expected keys and data types of this shape.
125
     *
126
     * @return array The expected keys and data types of this shape.
127
     */
128
    public function validateValue()
129
    {
130
        /** @var array */
131
        return [];
132
    }
133
134
    /**
135
     * {@inheritdoc}
136
     */
137
    public function requiredKeys()
138
    {
139
        return [];
140
    }
141
142
    /**
143
     * {@inheritdoc}
144
     */
145
    public function __toString()
146
    {
147
        return json_encode($this->value);
148
    }
149
150
    /**
151
     * Checks to see whether or not this is an indexed array (e.g., list).
152
     *
153
     * @return boolean Whether or not this Collection is a List. A value of `true` means that the Collection is an
154
     *                 indexed array. A value of `false` means that the Collection is an associative array.
155
     */
156
    public function isList()
157
    {
158
        foreach ($this->value as $a => $b) {
159
            if ($a !== (integer) $a) {
160
                /** @var boolean */
161
                return false;
162
            }
163
        }
164
165
        /** @var boolean */
166
        return true;
167
    }
168
169
    /**
170
     * Checks to see whether or not this is an associative array (e.g., hash, dictionary).
171
     *
172
     * @return boolean Whether or not this Collection is a Hash. A value of `true` means that the Collection is an
173
     *                 associative array. A value of `false` means that the Collection is an indexed array.
174
     */
175
    public function isMap()
176
    {
177
        foreach ($this->value as $a => $b) {
178
            if ($a !== (string) $a) {
179
                /** @var boolean */
180
                return false;
181
            }
182
        }
183
184
        /** @var boolean */
185
        return true;
186
    }
187
188
189
    /**************************************************************************/
190
    // Helper Methods
191
192
    /**
193
     * Stores the list of required keys for processing.
194
     *
195
     * @param  array $keys The response from a call to `requiredKeys()`.
196
     * @return void
197
     */
198
    protected function storeRequiredKeys(array $keys)
199
    {
200
        $this->required_keys = array_flip($keys);
201
    }
202
203
    /**
204
     * Checks to see if a key is required.
205
     *
206
     * @param  string  $key The key to remove from the list of required keys.
207
     * @return boolean Whether or not a key is required. A value of `true` means that the key is required.
208
     *                     A value of `false` means that the key is NOT required.
209
     */
210
    protected function isRequiredKey($key)
211
    {
212
        return isset($this->required_keys[$key]);
213
    }
214
215
    /**
216
     * Plucks a key from the list of required keys, and returns the list of remaining keys.
217
     *
218
     * @param  string $key The key to remove from the list of required keys.
219
     * @return void
220
     */
221
    protected function pluckFromRequiredKeys($key)
222
    {
223
        if (isset($this->required_keys[$key]) === true) {
224
            unset($this->required_keys[$key]);
225
        }
226
    }
227
228
    /**
229
     * Gets the required keys that have not yet been set.
230
     *
231
     * @return array<string> The required keys that have not yet been set.
232
     */
233
    protected function getRemainingRequiredKeys()
234
    {
235
        return array_flip($this->required_keys);
236
    }
237
}
238