Completed
Push — master ( 8c6794...616477 )
by Lars
17:50 queued 16:10
created

AbstractCollection::internalSet()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 27

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 5.4042

Importance

Changes 0
Metric Value
cc 4
nc 3
nop 3
dl 0
loc 27
ccs 5
cts 9
cp 0.5556
crap 5.4042
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
     * @phpstan-var array<T>
35
     */
36
    protected $array = [];
37
38
    /**
39
     * @var ArrayyRewindableGenerator|null
40
     * @phpstan-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
     * @phpstan-param array<TKey,T>|\Arrayy\Arrayy<TKey,T>|\Closure():array<TKey,T>|mixed $data
79
     * @phpstan-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
            $iteratorClass,
106
            $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
     * @phpstan-param T|static $value
120
     * @phpstan-param TKey|null $key
121
     * @phpstan-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
     * @phpstan-param T|static $value
174
     * @phpstan-param TKey|null $key
175
     * @phpstan-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
     * @phpstan-param CollectionInterface<TKey,T> ...$collections
237
     * @phpstan-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
     *
263
     * @phpstan-param  array<TKeyCreate,TCreate> $data
264
     * @phpstan-param  class-string<\Arrayy\ArrayyIterator> $iteratorClass
265
     * @phpstan-return static<TKeyCreate,TCreate>
266
     *
267
     * @psalm-mutation-free
268
     */
269 25
    public static function create(
270
        $data = [],
271
        string $iteratorClass = ArrayyIterator::class,
272
        bool $checkPropertiesInConstructor = true
273
    ) {
274 25
        return new static(
275 25
            $data,
276
            $iteratorClass,
277
            $checkPropertiesInConstructor
278
        );
279
    }
280
281
    /**
282
     * @param string $json
283
     *
284
     * @return static
285
     *                <p>(Immutable) Returns an new instance of the CollectionInterface object.</p>
286
     *
287
     * @phpstan-return static<int,T>
288
     *
289
     * @psalm-mutation-free
290
     */
291 7
    public static function createFromJsonMapper(string $json)
292
    {
293
        // init
294 7
        $return = static::create();
295 7
        $jsonObject = \json_decode($json, false);
296 7
        $mapper = new \Arrayy\Mapper\Json();
297 7 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...
298 1
            if ($return->checkForMissingPropertiesInConstructor) {
299 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...
300
            }
301
        };
302
303 7
        $type = $return->getType();
304
305
        if (
306 7
            \is_string($type)
307
            &&
308 7
            \class_exists($type)
309
        ) {
310 3
            if (\is_array($jsonObject)) {
311 1
                foreach ($jsonObject as $jsonObjectSingle) {
312 1
                    $collectionData = $mapper->map($jsonObjectSingle, $type);
313 1
                    $return->add($collectionData);
314
                }
315
            } else {
316 2
                $collectionData = $mapper->map($jsonObject, $type);
317 2
                $return->add($collectionData);
318
            }
319
        } else {
320 4
            foreach ($jsonObject as $key => $jsonValue) {
321 4
                $return->add($jsonValue, $key);
322
            }
323
        }
324
325
        /** @phpstan-var static<int,T> */
326 4
        return $return;
327
    }
328
329
    /**
330
     * Internal mechanic of set method.
331
     *
332
     * @param int|string|null $key
333
     * @param mixed           $value
334
     * @param bool            $checkProperties
335
     *
336
     * @return bool
337
     */
338 93
    protected function internalSet(
339
        $key,
340
        &$value,
341
        bool $checkProperties = true
342
    ): bool {
343
        if (
344 93
            $value instanceof self
345
            &&
346 93
            !$value instanceof TypeInterface
347
        ) {
348
            foreach ($value as $valueTmp) {
349
                parent::internalSet(
350
                    $key,
351
                    $valueTmp,
352
                    $checkProperties
353
                );
354
            }
355
356
            return true;
357
        }
358
359 93
        return parent::internalSet(
360 93
            $key,
361
            $value,
362
            $checkProperties
363
        );
364
    }
365
366
    /**
367
     * @param string|string[]|TypeCheckArray|TypeCheckInterface[]|null $type
368
     *
369
     * @return TypeCheckArray
370
     *
371
     * @phpstan-param null|string|string[]|class-string|class-string[]|TypeCheckArray<array-key,TypeCheckInterface>|array<array-key,TypeCheckInterface>|mixed $type
372
     * @phpstan-return TypeCheckArray<array-key,TypeCheckInterface>
373
     */
374 99
    protected static function convertIntoTypeCheckArray($type): TypeCheckArray
375
    {
376 99
        $is_array = false;
377
        if (
378 99
            \is_scalar($type)
379
            ||
380 99
            $is_array = \is_array($type)
381
        ) {
382
            /** @noinspection CallableParameterUseCaseInTypeContextInspection */
383 99
            $type = TypeCheckArray::create(
384
                [
385 99
                    Arrayy::ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES => new TypeCheckSimple($is_array ? $type : (string) $type),
386
                ]
387
            );
388
        }
389
390 99
        return $type;
391
    }
392
}
393