Completed
Push — master ( 6254b4...971077 )
by Lars
01:37
created

AbstractCollection::internalSet()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 27

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 5.5726

Importance

Changes 0
Metric Value
cc 4
nc 3
nop 3
dl 0
loc 27
ccs 7
cts 13
cp 0.5385
crap 5.5726
rs 9.488
c 0
b 0
f 0
1
<?php
2
3
/** @noinspection ClassOverridesFieldOfSuperClassInspection */
4
/** @noinspection PropertyInitializationFlawsInspection */
5
/** @noinspection PhpSuperClassIncompatibleWithInterfaceInspection */
6
7
declare(strict_types=1);
8
9
namespace Arrayy\Collection;
10
11
use Arrayy\Arrayy;
12
use Arrayy\ArrayyIterator;
13
use Arrayy\ArrayyRewindableGenerator;
14
use Arrayy\Type\TypeInterface;
15
use Arrayy\TypeCheck\TypeCheckArray;
16
use Arrayy\TypeCheck\TypeCheckInterface;
17
use Arrayy\TypeCheck\TypeCheckSimple;
18
19
/**
20
 * This class provides a full implementation of `CollectionInterface`, to
21
 * minimize the effort required to implement this interface.
22
 *
23
 * INFO: this collection thingy is inspired by https://github.com/ramsey/collection/
24
 *
25
 * @template   TKey of array-key
26
 * @template   T
27
 * @extends    Arrayy<TKey,T>
28
 * @implements CollectionInterface<TKey,T>
29
 */
30
abstract class AbstractCollection extends Arrayy implements CollectionInterface
31
{
32
    /**
33
     * @var array
34
     * @psalm-var array<T>
35
     */
36
    protected $array = [];
37
38
    /**
39
     * @var ArrayyRewindableGenerator|null
40
     * @psalm-var \Arrayy\ArrayyRewindableGenerator<TKey,T>|null
41
     */
42
    protected $generator;
43
44
    /**
45
     * @var bool
46
     */
47
    protected $checkPropertyTypes = true;
48
49
    /**
50
     * @var bool
51
     */
52
    protected $checkPropertiesMismatch = false;
53
54
    /**
55
     * @var bool
56
     */
57
    protected $checkForMissingPropertiesInConstructor = true;
58
59
    /**
60
     * Constructs a collection object of the specified type, optionally with the
61
     * specified data.
62
     *
63
     * @param mixed  $data
64
     *                                             <p>
65
     *                                             The initial items to store in the collection.
66
     *                                             </p>
67
     * @param string $iteratorClass                optional <p>
68
     *                                             You can overwrite the ArrayyIterator, but mostly you don't
69
     *                                             need this option.
70
     *                                             </p>
71
     * @param bool   $checkPropertiesInConstructor optional <p>
72
     *                                             You need to extend the "Arrayy"-class and you need to set
73
     *                                             the $checkPropertiesMismatchInConstructor class property
74
     *                                             to
75
     *                                             true, otherwise this option didn't not work anyway.
76
     *                                             </p>
77
     *
78
     * @psalm-param array<TKey,T>|\Arrayy\Arrayy<TKey,T>|\Closure():array<TKey,T>|mixed $data
79
     * @psalm-param class-string<\Arrayy\ArrayyIterator> $iteratorClass
80
     */
81 99
    public function __construct(
82
        $data = [],
83
        string $iteratorClass = ArrayyIterator::class,
84
        bool $checkPropertiesInConstructor = true
85
    ) {
86 99
        $type = $this->getType();
87
88 99
        $type = self::convertIntoTypeCheckArray($type);
89
90 99
        $this->properties = $type;
0 ignored issues
show
Documentation Bug introduced by
It seems like $type of type object<Arrayy\TypeCheck\TypeCheckArray> is incompatible with the declared type array of property $properties.

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...
91
92
        // cast into array, if needed
93
        if (
94 99
            !\is_array($data)
95
            &&
96 99
            !($data instanceof \Traversable)
97
            &&
98 99
            !($data instanceof \Closure)
99
        ) {
100 2
            $data = [$data];
101
        }
102
103 99
        parent::__construct(
104 99
            $data,
105 99
            $iteratorClass,
106 99
            $checkPropertiesInConstructor
107
        );
108 80
    }
109
110
    /**
111
     * Append a (key) + value to the current array.
112
     *
113
     * @param mixed $value
114
     * @param mixed $key
115
     *
116
     * @return $this
117
     *               <p>(Mutable) Return this CollectionInterface object, with the appended values.</p>
118
     *
119
     * @psalm-param T|static $value
120
     * @psalm-param TKey|null $key
121
     * @psalm-return static<TKey,T>
122
     */
123 6 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...
124
    {
125
        if (
126 6
            $value instanceof self
127
            &&
128 6
            !$value instanceof TypeInterface
129
        ) {
130 1
            foreach ($value as $valueTmp) {
131 1
                parent::append($valueTmp, $key);
132
            }
133
134 1
            return $this;
135
        }
136
137 5
        $return = parent::append($value, $key);
138 4
        $this->array = $return->array;
139 4
        $this->generator = null;
140
141 4
        return $this;
142
    }
143
144
    /**
145
     * {@inheritdoc}
146
     */
147 2
    public function offsetSet($offset, $value)
148
    {
149
        if (
150 2
            $value instanceof self
151
            &&
152 2
            !$value instanceof TypeInterface
153
        ) {
154
            foreach ($value as $valueTmp) {
155
                parent::offsetSet($offset, $valueTmp);
156
            }
157
158
            return;
159
        }
160
161 2
        parent::offsetSet($offset, $value);
162 1
    }
163
164
    /**
165
     * Prepend a (key) + value to the current array.
166
     *
167
     * @param mixed $value
168
     * @param mixed $key
169
     *
170
     * @return $this
171
     *               <p>(Mutable) Return this CollectionInterface object, with the prepended value.</p>
172
     *
173
     * @psalm-param T|static $value
174
     * @psalm-param TKey|null $key
175
     * @psalm-return static<TKey,T>
176
     */
177 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...
178
    {
179
        if (
180 3
            $value instanceof self
181
            &&
182 3
            !$value instanceof TypeInterface
183
        ) {
184
            foreach ($value as $valueTmp) {
185
                parent::prepend($valueTmp, $key);
186
            }
187
188
            return $this;
189
        }
190
191 3
        $return = parent::prepend($value, $key);
192 1
        $this->array = $return->array;
193 1
        $this->generator = null;
194
195 1
        return $this;
196
    }
197
198
    /**
199
     * {@inheritdoc}
200
     */
201 1
    public function column(string $keyOrPropertyOrMethod): array
202
    {
203
        // init
204 1
        $temp = [];
205
206 1
        foreach ($this->getGenerator() as $item) {
207 1
            $temp[] = $this->extractValue($item, $keyOrPropertyOrMethod);
208
        }
209
210 1
        return $temp;
211
    }
212
213
    /**
214
     * {@inheritdoc}
215
     */
216 7
    public function getCollection(): array
217
    {
218 7
        return $this->getArray();
219
    }
220
221
    /**
222
     * {@inheritdoc}
223
     */
224
    abstract public function getType();
225
226
    /**
227
     * Merge current items and items of given collections into a new one.
228
     *
229
     * @param CollectionInterface|static ...$collections
230
     *                                                   <p>The collections to merge.</p>
231
     *
232
     *@throws \InvalidArgumentException if any of the given collections are not of the same type
233
     *
234
     * @return $this
235
     *
236
     * @psalm-param CollectionInterface<TKey,T> ...$collections
237
     * @psalm-return static<TKey,T>
238
     */
239 1
    public function merge(CollectionInterface ...$collections): self
240
    {
241 1
        foreach ($collections as $collection) {
242 1
            foreach ($collection as $item) {
243 1
                $this->append($item);
244
            }
245
        }
246
247 1
        return $this;
248
    }
249
250
    /**
251
     * Creates an CollectionInterface object.
252
     *
253
     * @param mixed  $data
254
     * @param string $iteratorClass
255
     * @param bool   $checkPropertiesInConstructor
256
     *
257
     * @return static
258
     *                <p>(Immutable) Returns an new instance of the CollectionInterface object.</p>
259
     *
260
     * @template     TKeyCreate as int|string
261
     * @template     TCreate
262
     * @psalm-param  array<TKeyCreate,TCreate> $data
263
     * @psalm-param  class-string<\Arrayy\ArrayyIterator> $iteratorClass
264
     * @psalm-return static<TKeyCreate,TCreate>
265
     * @psalm-mutation-free
266
     */
267 25
    public static function create(
268
        $data = [],
269
        string $iteratorClass = ArrayyIterator::class,
270
        bool $checkPropertiesInConstructor = true
271
    ) {
272 25
        return new static(
273 25
            $data,
274 25
            $iteratorClass,
275 25
            $checkPropertiesInConstructor
276
        );
277
    }
278
279
    /**
280
     * @param string $json
281
     *
282
     * @return static
283
     *                <p>(Immutable) Returns an new instance of the CollectionInterface object.</p>
284
     *
285
     * @psalm-return static<int,T>
286
     *
287
     * @psalm-mutation-free
288
     */
289 7
    public static function createFromJsonMapper(string $json)
290
    {
291
        // init
292 7
        $return = static::create();
293 7
        $jsonObject = \json_decode($json, false);
294 7
        $mapper = new \Arrayy\Mapper\Json();
295 View Code Duplication
        $mapper->undefinedPropertyHandler = static function ($object, $key, $jsonValue) use ($return) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
296 1
            if ($return->checkForMissingPropertiesInConstructor) {
297 1
                throw new \TypeError('Property mismatch - input: ' . \print_r(['key' => $key, 'jsonValue' => $jsonValue], true) . ' for object: ' . \get_class($object));
0 ignored issues
show
Unused Code introduced by
The call to TypeError::__construct() has too many arguments starting with 'Property mismatch - inp...' . \get_class($object).

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
298
            }
299
        };
300
301 7
        $type = $return->getType();
302
303
        if (
304 7
            \is_string($type)
305
            &&
306 7
            \class_exists($type)
307
        ) {
308 3
            if (\is_array($jsonObject)) {
309 1
                foreach ($jsonObject as $jsonObjectSingle) {
310 1
                    $collectionData = $mapper->map($jsonObjectSingle, $type);
311 1
                    $return->add($collectionData);
312
                }
313
            } else {
314 2
                $collectionData = $mapper->map($jsonObject, $type);
315 2
                $return->add($collectionData);
316
            }
317
        } else {
318 4
            foreach ($jsonObject as $key => $jsonValue) {
319 4
                $return->add($jsonValue, $key);
320
            }
321
        }
322
323 4
        return $return;
324
    }
325
326
    /**
327
     * Internal mechanic of set method.
328
     *
329
     * @param int|string|null $key
330
     * @param mixed           $value
331
     * @param bool            $checkProperties
332
     *
333
     * @return bool
334
     */
335 93
    protected function internalSet(
336
        $key,
337
        &$value,
338
        bool $checkProperties = true
339
    ): bool {
340
        if (
341 93
            $value instanceof self
342
            &&
343 93
            !$value instanceof TypeInterface
344
        ) {
345
            foreach ($value as $valueTmp) {
346
                parent::internalSet(
347
                    $key,
348
                    $valueTmp,
349
                    $checkProperties
350
                );
351
            }
352
353
            return true;
354
        }
355
356 93
        return parent::internalSet(
357 93
            $key,
358 93
            $value,
359 93
            $checkProperties
360
        );
361
    }
362
363
    /**
364
     * @param string|string[]|TypeCheckArray|TypeCheckInterface[]|null $type
365
     *
366
     * @return TypeCheckArray
367
     *
368
     * @psalm-param null|string|string[]|class-string|class-string[]|TypeCheckArray<array-key,TypeCheckInterface>|array<array-key,TypeCheckInterface>|mixed $type
369
     * @psalm-return TypeCheckArray<array-key,TypeCheckInterface>
370
     */
371 99
    protected static function convertIntoTypeCheckArray($type): TypeCheckArray
372
    {
373 99
        $is_array = false;
374
        if (
375 99
            \is_scalar($type)
376
            ||
377 99
            $is_array = \is_array($type)
378
        ) {
379
            /** @noinspection CallableParameterUseCaseInTypeContextInspection */
380 99
            $type = TypeCheckArray::create(
381
                [
382 99
                    Arrayy::ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES => new TypeCheckSimple($is_array ? $type : (string) $type),
383
                ]
384
            );
385
        }
386
387 99
        return $type;
388
    }
389
}
390