Completed
Push — master ( 8507e2...2d694e )
by Lars
01:39
created

AbstractCollection::__construct()   A

Complexity

Conditions 4
Paths 2

Size

Total Lines 28

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 4

Importance

Changes 0
Metric Value
cc 4
nc 2
nop 3
dl 0
loc 28
ccs 13
cts 13
cp 1
crap 4
rs 9.472
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Arrayy\Collection;
6
7
use Arrayy\Arrayy;
8
use Arrayy\ArrayyIterator;
9
use Arrayy\Type\TypeInterface;
10
use Arrayy\TypeCheck\TypeCheckArray;
11
use Arrayy\TypeCheck\TypeCheckInterface;
12
use Arrayy\TypeCheck\TypeCheckSimple;
13
14
/**
15
 * This class provides a full implementation of `CollectionInterface`, to
16
 * minimize the effort required to implement this interface.
17
 *
18
 * INFO: this collection thingy is inspired by https://github.com/ramsey/collection/
19
 */
20
abstract class AbstractCollection extends Arrayy implements CollectionInterface
21
{
22
    /**
23
     * @var bool
24
     */
25
    protected $checkPropertyTypes = true;
26
27
    /**
28
     * @var bool
29
     */
30
    protected $checkPropertiesMismatch = false;
31
32
    /**
33
     * @var bool
34
     */
35
    protected $checkForMissingPropertiesInConstructor = true;
36
37
    /**
38
     * Constructs a collection object of the specified type, optionally with the
39
     * specified data.
40
     *
41
     * @param mixed  $data
42
     *                                             <p>
43
     *                                             The initial items to store in the collection.
44
     *                                             </p>
45
     * @param string $iteratorClass                optional <p>
46
     *                                             You can overwrite the ArrayyIterator, but mostly you don't
47
     *                                             need this option.
48
     *                                             </p>
49
     * @param bool   $checkPropertiesInConstructor optional <p>
50
     *                                             You need to extend the "Arrayy"-class and you need to set
51
     *                                             the $checkPropertiesMismatchInConstructor class property
52
     *                                             to
53
     *                                             true, otherwise this option didn't not work anyway.
54
     *                                             </p>
55
     */
56 67
    public function __construct(
57
        $data = [],
58
        string $iteratorClass = ArrayyIterator::class,
59
        bool $checkPropertiesInConstructor = true
60
    ) {
61 67
        $type = $this->getType();
62
63 67
        $type = self::convertIntoTypeCheckArray($type);
64
65 67
        $this->properties = $type;
66
67
        // cast into array, if needed
68
        if (
69 67
            !\is_array($data)
70
            &&
71 67
            !($data instanceof \Traversable)
72
            &&
73 67
            !($data instanceof \Closure)
74
        ) {
75 2
            $data = [$data];
76
        }
77
78 67
        parent::__construct(
79 67
            $data,
80 67
            $iteratorClass,
81 67
            $checkPropertiesInConstructor
82
        );
83 55
    }
84
85
    /**
86
     * @return static[]
87
     */
88 6
    public function getCollection(): array
89
    {
90 6
        return $this->array;
91
    }
92
93
    /**
94
     * The type (FQCN) associated with this collection.
95
     *
96
     * @return string|string[]|TypeCheckArray|TypeCheckInterface[]
97
     */
98
    abstract public function getType();
99
100
    /**
101
     * Merge current items and items of given collections into a new one.
102
     *
103
     * @param CollectionInterface ...$collections The collections to merge.
104
     *
105
     * @throws \InvalidArgumentException if any of the given collections are not of the same type
106
     *
107
     * @return static
108
     */
109 1
    public function merge(CollectionInterface ...$collections): CollectionInterface
110
    {
111 1
        foreach ($collections as $collection) {
112 1
            if ($collection instanceof Arrayy) {
113 1
                foreach ($collection as $item) {
114 1
                    $this->append($item);
115
                }
116
            }
117
        }
118
119 1
        return $this;
120
    }
121
122
    /**
123
     * Assigns a value to the specified offset + check the type.
124
     *
125
     * @param int|string|null $offset
126
     * @param mixed           $value
127
     */
128 1
    public function offsetSet($offset, $value)
129
    {
130
        if (
131 1
            $value instanceof self
132
            &&
133 1
            !$value instanceof TypeInterface
134
        ) {
135
            foreach ($value as $valueTmp) {
136
                parent::offsetSet($offset, $valueTmp);
137
            }
138
139
            return;
140
        }
141
142 1
        parent::offsetSet($offset, $value);
143
    }
144
145
    /**
146
     * Prepend a (key) + value to the current array.
147
     *
148
     * @param mixed $value
149
     * @param mixed $key
150
     *
151
     * @return static
152
     *                <p>(Mutable) Return this Arrayy object, with the prepended value.</p>
153
     */
154 3 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...
155
    {
156
        if (
157 3
            $value instanceof self
158
            &&
159 3
            !$value instanceof TypeInterface
160
        ) {
161
            foreach ($value as $valueTmp) {
162
                parent::prepend($valueTmp, $key);
163
            }
164
165
            return $this;
166
        }
167
168 3
        return parent::prepend($value, $key);
169
    }
170
171
    /**
172
     * Append a (key) + value to the current array.
173
     *
174
     * @param mixed $value
175
     * @param mixed $key
176
     *
177
     * @return static
178
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
179
     */
180 4 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...
181
    {
182
        if (
183 4
            $value instanceof self
184
            &&
185 4
            !$value instanceof TypeInterface
186
        ) {
187 1
            foreach ($value as $valueTmp) {
188 1
                parent::append($valueTmp, $key);
189
            }
190
191 1
            return $this;
192
        }
193
194 3
        return parent::append($value, $key);
195
    }
196
197
    /**
198
     * Returns the values from given property or method.
199
     *
200
     * @param string $keyOrPropertyOrMethod the property or method name to filter by
201
     *
202
     * @throws \InvalidArgumentException if property or method is not defined
203
     *
204
     * @return array
205
     */
206 1
    public function column(string $keyOrPropertyOrMethod): array
207
    {
208
        // init
209 1
        $temp = [];
210
211 1
        foreach ($this->getGenerator() as $item) {
212 1
            $temp[] = $this->extractValue($item, $keyOrPropertyOrMethod);
213
        }
214
215 1
        return $temp;
216
    }
217
218
    /**
219
     * Returns a collection of matching items.
220
     *
221
     * @param string $keyOrPropertyOrMethod the property or method to evaluate
222
     * @param mixed  $value                 the value to match
223
     *
224
     * @throws \InvalidArgumentException if property or method is not defined
225
     *
226
     * @return static
227
     */
228 1
    public function where(string $keyOrPropertyOrMethod, $value): CollectionInterface
229
    {
230 1
        return $this->filter(
231
            function ($item) use ($keyOrPropertyOrMethod, $value) {
232 1
                $accessorValue = $this->extractValue(
233 1
                    $item,
234 1
                    $keyOrPropertyOrMethod
235
                );
236
237 1
                return $accessorValue === $value;
238 1
            }
239
        );
240
    }
241
242
    /**
243
     * Internal mechanic of set method.
244
     *
245
     * @param string|null $key
246
     * @param mixed       $value
247
     * @param bool        $checkPropertiesAndType
248
     *
249
     * @return bool
250
     */
251 65 View Code Duplication
    protected function internalSet($key, &$value, $checkPropertiesAndType = 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...
252
    {
253
        if (
254 65
            $value instanceof self
255
            &&
256 65
            !$value instanceof TypeInterface
257
        ) {
258
            foreach ($value as $valueTmp) {
259
                parent::internalSet(
260
                    $key,
261
                    $valueTmp,
262
                    $checkPropertiesAndType
263
                );
264
            }
265
266
            return true;
267
        }
268
269 65
        return parent::internalSet(
270 65
            $key,
271 65
            $value,
272 65
            $checkPropertiesAndType
273
        );
274
    }
275
276
    /**
277
     * @param mixed $type
278
     *
279
     * @return TypeCheckArray
280
     */
281 67
    protected static function convertIntoTypeCheckArray($type): TypeCheckArray
282
    {
283 67
        $is_array = false;
284
        if (
285 67
            \is_scalar($type)
286
            ||
287 67
            $is_array = \is_array($type)
288
        ) {
289 67
            $type = TypeCheckArray::create(
290
                [
291 67
                    Arrayy::ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES => new TypeCheckSimple($is_array ? $type : (string) $type),
292
                ]
293
            );
294
        }
295
296 67
        return $type;
297
    }
298
299
    /**
300
     * Extracts the value of the given property or method from the object.
301
     *
302
     * @param Arrayy $object                the object to extract the value from
303
     * @param string $keyOrPropertyOrMethod the property or method for which the
304
     *                                      value should be extracted
305
     *
306
     * @throws \InvalidArgumentException if the method or property is not defined
307
     *
308
     * @return mixed the value extracted from the specified property or method
309
     */
310 2
    private function extractValue(Arrayy $object, string $keyOrPropertyOrMethod)
311
    {
312 2
        if (isset($object[$keyOrPropertyOrMethod])) {
313 2
            $return = $object->get($keyOrPropertyOrMethod);
314
315 2
            if ($return instanceof Arrayy) {
316 1
                return $return->getArray();
317
            }
318
319 1
            return $return;
320
        }
321
322
        if (\property_exists($object, $keyOrPropertyOrMethod)) {
323
            return $object->{$keyOrPropertyOrMethod};
324
        }
325
326
        if (\method_exists($object, $keyOrPropertyOrMethod)) {
327
            return $object->{$keyOrPropertyOrMethod}();
328
        }
329
330
        throw new \InvalidArgumentException(\sprintf('array-key & property & method "%s" not defined in %s', $keyOrPropertyOrMethod, \gettype($object)));
331
    }
332
}
333