Completed
Push — master ( ce8c86...b1da28 )
by Lars
02:05
created

AbstractCollection::checkType()   C

Complexity

Conditions 16
Paths 16

Size

Total Lines 34

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 34.1389

Importance

Changes 0
Metric Value
cc 16
nc 16
nop 2
dl 0
loc 34
ccs 17
cts 29
cp 0.5862
crap 34.1389
rs 5.5666
c 0
b 0
f 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
declare(strict_types=1);
4
5
namespace Arrayy\Collection;
6
7
use Arrayy\Arrayy;
8
9
/**
10
 * This class provides a full implementation of `CollectionInterface`, to
11
 * minimize the effort required to implement this interface.
12
 */
13
abstract class AbstractCollection extends Arrayy implements CollectionInterface
14
{
15
    /**
16
     * The type of elements stored in this collection.
17
     *
18
     * @var string
19
     */
20
    private $collectionType;
21
22
    /**
23
     * Constructs a collection object of the specified type, optionally with the
24
     * specified data.
25
     *
26
     * @param mixed $data
27
     *                     <p>
28
     *                     The initial items to store in the collection.
29
     *                     </p>
30
     *                     <p>
31
     *                     Should be an array or a generator, otherwise it will try
32
     *                     to convert it into an array.
33
     *                     </p>
34
     */
35 11
    public function __construct($data = [])
36
    {
37 11
        $this->collectionType = $this->getType();
38 11
        parent::__construct($data);
39 10
    }
40
41
    /**
42
     * @return static[]
43
     */
44 3
    public function getCollection(): array
45
    {
46 3
        return $this->array;
47
    }
48
49
    /**
50
     * The type (FQCN) associated with this collection.
51
     *
52
     * @return string
53
     */
54
    abstract public function getType(): string;
55
56
    /**
57
     * Merge current items and items of given collections into a new one.
58
     *
59
     * @param CollectionInterface ...$collections The collections to merge.
60
     *
61
     * @throws \InvalidArgumentException if any of the given collections are not of the same type
62
     *
63
     * @return CollectionInterface
64
     */
65 1
    public function merge(CollectionInterface ...$collections): CollectionInterface
66
    {
67 1
        foreach ($collections as $collection) {
68 1
            foreach ($collection as $item) {
0 ignored issues
show
Bug introduced by
The expression $collection of type object<Arrayy\Collection\CollectionInterface> is not traversable.
Loading history...
69 1
                $this->append($item);
70
            }
71
        }
72
73 1
        return $this;
74
    }
75
76
    /**
77
     * Assigns a value to the specified offset + check the type.
78
     *
79
     * @param int|string|null $offset
80
     * @param mixed           $value
81
     */
82 1 View Code Duplication
    public function offsetSet($offset, $value)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
83
    {
84 1
        if ($this->checkType($this->collectionType, $value) === false) {
85 1
            throw new \InvalidArgumentException(
86 1
                'Value must be of type ' . $this->collectionType . '; value is ' . $this->valueToString($value)
87
            );
88
        }
89
90
        parent::offsetSet($offset, $value);
91
    }
92
93
    /**
94
     * Prepend a (key) + value to the current array.
95
     *
96
     * @param mixed $value
97
     * @param mixed $key
98
     *
99
     * @return static
100
     *                <p>(Mutable) Return this Arrayy object, with the prepended value.</p>
101
     */
102 2 View Code Duplication
    public function prepend($value, $key = null): Arrayy
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
103
    {
104 2
        if ($this->checkType($this->collectionType, $value) === false) {
105 2
            throw new \InvalidArgumentException(
106 2
                'Value must be of type ' . $this->collectionType . '; value is ' . $this->valueToString($value)
107
            );
108
        }
109
110
        return parent::prepend($value, $key);
111
    }
112
113
    /**
114
     * Append a (key) + value to the current array.
115
     *
116
     * @param mixed $value
117
     * @param mixed $key
118
     *
119
     * @return static
120
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
121
     */
122 3 View Code Duplication
    public function append($value, $key = null): Arrayy
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
123
    {
124 3
        if ($this->checkType($this->collectionType, $value) === false) {
125 1
            throw new \InvalidArgumentException(
126 1
                'Value must be of type ' . $this->collectionType . '; value is ' . $this->valueToString($value)
127
            );
128
        }
129
130 2
        return parent::append($value, $key);
131
    }
132
133
    /**
134
     * Returns the values from given property or method.
135
     *
136
     * @param string $keyOrPropertyOrMethod the property or method name to filter by
137
     *
138
     * @throws \InvalidArgumentException if property or method is not defined
139
     *
140
     * @return array
141
     */
142 1
    public function column(string $keyOrPropertyOrMethod): array
143
    {
144
        // init
145 1
        $temp = [];
146
147 1
        foreach ($this->array as $item) {
148 1
            $temp[] = $this->extractValue($item, $keyOrPropertyOrMethod);
149
        }
150
151 1
        return $temp;
152
    }
153
154
    /**
155
     * Returns a collection of matching items.
156
     *
157
     * @param string $keyOrPropertyOrMethod the property or method to evaluate
158
     * @param mixed  $value                 the value to match
159
     *
160
     * @throws \InvalidArgumentException if property or method is not defined
161
     *
162
     * @return CollectionInterface
163
     */
164 1
    public function where(string $keyOrPropertyOrMethod, $value): CollectionInterface
165
    {
166 1
        return $this->filter(
167
            function ($item) use ($keyOrPropertyOrMethod, $value) {
168 1
                $accessorValue = $this->extractValue(
169 1
                    $item,
170 1
                    $keyOrPropertyOrMethod
171
                );
172
173 1
                return $accessorValue === $value;
174 1
            }
175
        );
176
    }
177
178
    /**
179
     * Internal mechanic of set method.
180
     *
181
     * @param string|null $key
182
     * @param mixed       $value
183
     * @param bool        $checkProperties
184
     *
185
     * @return bool
186
     */
187 10 View Code Duplication
    protected function internalSet($key, $value, $checkProperties = true): bool
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
188
    {
189 10
        if ($this->checkType($this->collectionType, $value) === false) {
190 1
            throw new \InvalidArgumentException(
191 1
                'Value must be of type ' . $this->collectionType . '; value is ' . $this->valueToString($value)
192
            );
193
        }
194
195 9
        return parent::internalSet($key, $value, $checkProperties);
196
    }
197
198
    /**
199
     * Extracts the value of the given property or method from the object.
200
     *
201
     * @param object $object                the object to extract the value from
202
     * @param string $keyOrPropertyOrMethod the property or method for which the
203
     *                                      value should be extracted
204
     *
205
     * @throws \InvalidArgumentException if the method or property is not defined
206
     *
207
     * @return mixed the value extracted from the specified property or method
208
     */
209 2
    private function extractValue($object, string $keyOrPropertyOrMethod)
210
    {
211 2
        if (isset($object[$keyOrPropertyOrMethod])) {
212 2
            $return = $object->get($keyOrPropertyOrMethod);
213
214 2
            if ($return instanceof Arrayy) {
215 1
                return $return->getArray();
216
            }
217
218 1
            return $return;
219
        }
220
221
        $isObject = \is_object($object);
222
223
        if ($isObject && \property_exists($object, $keyOrPropertyOrMethod)) {
224
            return $object->{$keyOrPropertyOrMethod};
225
        }
226
227
        if ($isObject && \method_exists($object, $keyOrPropertyOrMethod)) {
228
            return $object->{$keyOrPropertyOrMethod}();
229
        }
230
231
        throw new \InvalidArgumentException(
232
            \sprintf('array-key & property & method "%s" not defined in %s', $keyOrPropertyOrMethod, \gettype($object))
233
        );
234
    }
235
236
    /**
237
     * Returns `true` if value is of the specified type.
238
     *
239
     * @param string $type  the type to check the value against
240
     * @param mixed  $value the value to check
241
     *
242
     * @return bool
243
     */
244 10
    private function checkType(string $type, $value): bool
245
    {
246
        switch ($type) {
247 10
            case 'array':
248
                return \is_array($value);
249 10
            case 'bool':
250 10
            case 'boolean':
251
                return \is_bool($value);
252 10
            case 'callable':
253
                return \is_callable($value);
254 10
            case 'float':
255 10
            case 'double':
256
                return \is_float($value);
257 10
            case 'int':
258 10
            case 'integer':
259
                return \is_int($value);
260 10
            case 'null':
261
                return $value === null;
262 10
            case 'numeric':
263
                return \is_numeric($value);
264 10
            case 'object':
265
                return \is_object($value);
266 10
            case 'resource':
267
                return \is_resource($value);
268 10
            case 'scalar':
269
                return \is_scalar($value);
270 10
            case 'string':
271
                return \is_string($value);
272 10
            case 'mixed':
273
                return true;
274
            default:
275 10
                return $value instanceof $type;
276
        }
277
    }
278
279
    /**
280
     * @param mixed $value
281
     *
282
     * @return string
283
     */
284 5
    private function valueToString($value): string
285
    {
286
        // null
287 5
        if ($value === null) {
288
            return 'NULL';
289
        }
290
291
        // bool
292 5
        if (\is_bool($value)) {
293
            return $value ? 'TRUE' : 'FALSE';
294
        }
295
296
        // array
297 5
        if (\is_array($value)) {
298
            return 'Array';
299
        }
300
301
        // scalar types (integer, float, string)
302 5
        if (\is_scalar($value)) {
303
            return (string) $value;
304
        }
305
306
        // resource
307 5
        if (\is_resource($value)) {
308
            return \get_resource_type($value) . ' resource #' . (int) $value;
309
        }
310
311
        // object
312 5
        return \get_class($value) . ' Object';
313
    }
314
}
315