Completed
Push — master ( d26e7d...811b94 )
by Lars
02:24
created

AbstractCollection   A

Complexity

Total Complexity 38

Size/Duplication

Total Lines 365
Duplicated Lines 12.33 %

Coupling/Cohesion

Components 2
Dependencies 4

Test Coverage

Coverage 86.6%

Importance

Changes 0
Metric Value
dl 45
loc 365
ccs 84
cts 97
cp 0.866
rs 9.36
c 0
b 0
f 0
wmc 38
lcom 2
cbo 4

12 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 28 4
A append() 20 21 4
A offsetSet() 0 16 4
A prepend() 20 21 4
A column() 0 11 2
A getCollection() 0 4 1
getType() 0 1 ?
A merge() 0 10 3
A create() 0 11 1
B createFromJsonMapper() 5 37 7
A internalSet() 0 27 4
A convertIntoTypeCheckArray() 0 18 4

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

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 100
    public function __construct(
82
        $data = [],
83
        string $iteratorClass = ArrayyIterator::class,
84
        bool $checkPropertiesInConstructor = true
85
    ) {
86 100
        $type = $this->getType();
87
88 100
        $type = self::convertIntoTypeCheckArray($type);
89
90 100
        $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 100
            !\is_array($data)
95
            &&
96 100
            !($data instanceof \Traversable)
97
            &&
98 100
            !($data instanceof \Closure)
99
        ) {
100 2
            $data = [$data];
101
        }
102
103 100
        parent::__construct(
104 100
            $data,
105 100
            $iteratorClass,
106 100
            $checkPropertiesInConstructor
107
        );
108 81
    }
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
        /** @phpstan-ignore-next-line | special? */
138 5
        $return = parent::append($value, $key);
139 4
        $this->array = $return->array;
140 4
        $this->generator = null;
141
142 4
        return $this;
143
    }
144
145
    /**
146
     * {@inheritdoc}
147
     */
148 2
    public function offsetSet($offset, $value)
149
    {
150
        if (
151 2
            $value instanceof self
152
            &&
153 2
            !$value instanceof TypeInterface
154
        ) {
155
            foreach ($value as $valueTmp) {
156
                parent::offsetSet($offset, $valueTmp);
157
            }
158
159
            return;
160
        }
161
162 2
        parent::offsetSet($offset, $value);
163 1
    }
164
165
    /**
166
     * Prepend a (key) + value to the current array.
167
     *
168
     * @param mixed $value
169
     * @param mixed $key
170
     *
171
     * @return $this
172
     *               <p>(Mutable) Return this CollectionInterface object, with the prepended value.</p>
173
     *
174
     * @phpstan-param T|static $value
175
     * @phpstan-param TKey|null $key
176
     * @phpstan-return static<TKey,T>
177
     */
178 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...
179
    {
180
        if (
181 3
            $value instanceof self
182
            &&
183 3
            !$value instanceof TypeInterface
184
        ) {
185
            foreach ($value as $valueTmp) {
186
                parent::prepend($valueTmp, $key);
187
            }
188
189
            return $this;
190
        }
191
192
        /** @phpstan-ignore-next-line | special? */
193 3
        $return = parent::prepend($value, $key);
194 1
        $this->array = $return->array;
195 1
        $this->generator = null;
196
197 1
        return $this;
198
    }
199
200
    /**
201
     * {@inheritdoc}
202
     */
203 1
    public function column(string $keyOrPropertyOrMethod): array
204
    {
205
        // init
206 1
        $temp = [];
207
208 1
        foreach ($this->getGenerator() as $item) {
209 1
            $temp[] = $this->extractValue($item, $keyOrPropertyOrMethod);
210
        }
211
212 1
        return $temp;
213
    }
214
215
    /**
216
     * {@inheritdoc}
217
     */
218 7
    public function getCollection(): array
219
    {
220 7
        return $this->getArray();
221
    }
222
223
    /**
224
     * {@inheritdoc}
225
     */
226
    abstract public function getType();
227
228
    /**
229
     * Merge current items and items of given collections into a new one.
230
     *
231
     * @param CollectionInterface|static ...$collections
232
     *                                                   <p>The collections to merge.</p>
233
     *
234
     *@throws \InvalidArgumentException if any of the given collections are not of the same type
235
     *
236
     * @return $this
237
     *
238
     * @phpstan-param CollectionInterface<TKey,T> ...$collections
239
     * @phpstan-return static<TKey,T>
240
     */
241 1
    public function merge(CollectionInterface ...$collections): self
242
    {
243 1
        foreach ($collections as $collection) {
244 1
            foreach ($collection as $item) {
245 1
                $this->append($item);
246
            }
247
        }
248
249 1
        return $this;
250
    }
251
252
    /**
253
     * Creates an CollectionInterface object.
254
     *
255
     * @param mixed  $data
256
     * @param string $iteratorClass
257
     * @param bool   $checkPropertiesInConstructor
258
     *
259
     * @return static
260
     *                <p>(Immutable) Returns an new instance of the CollectionInterface object.</p>
261
     *
262
     * @template     TKeyCreate as int|string
263
     * @template     TCreate
264
     *
265
     * @phpstan-param  array<TKeyCreate,TCreate> $data
266
     * @phpstan-param  class-string<\Arrayy\ArrayyIterator> $iteratorClass
267
     * @phpstan-return static<TKeyCreate,TCreate>
268
     *
269
     * @psalm-mutation-free
270
     */
271 26
    public static function create(
272
        $data = [],
273
        string $iteratorClass = ArrayyIterator::class,
274
        bool $checkPropertiesInConstructor = true
275
    ) {
276 26
        return new static(
277 26
            $data,
278 26
            $iteratorClass,
279 26
            $checkPropertiesInConstructor
280
        );
281
    }
282
283
    /**
284
     * @param string $json
285
     *
286
     * @return static
287
     *                <p>(Immutable) Returns an new instance of the CollectionInterface object.</p>
288
     *
289
     * @phpstan-return static<int,T>
290
     *
291
     * @psalm-mutation-free
292
     */
293 7
    public static function createFromJsonMapper(string $json)
294
    {
295
        // init
296 7
        $return = static::create();
297 7
        $jsonObject = \json_decode($json, false);
298 7
        $mapper = new \Arrayy\Mapper\Json();
299 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...
300 1
            if ($return->checkForMissingPropertiesInConstructor) {
301 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...
302
            }
303
        };
304
305 7
        $type = $return->getType();
306
307
        if (
308 7
            \is_string($type)
309
            &&
310 7
            \class_exists($type)
311
        ) {
312 3
            if (\is_array($jsonObject)) {
313 1
                foreach ($jsonObject as $jsonObjectSingle) {
314 1
                    $collectionData = $mapper->map($jsonObjectSingle, $type);
315 1
                    $return->add($collectionData);
316
                }
317
            } else {
318 2
                $collectionData = $mapper->map($jsonObject, $type);
319 2
                $return->add($collectionData);
320
            }
321
        } else {
322 4
            foreach ($jsonObject as $key => $jsonValue) {
323 4
                $return->add($jsonValue, $key);
324
            }
325
        }
326
327
        /** @phpstan-var static<int,T> */
328 4
        return $return;
329
    }
330
331
    /**
332
     * Internal mechanic of set method.
333
     *
334
     * @param int|string|null $key
335
     * @param mixed           $value
336
     * @param bool            $checkProperties
337
     *
338
     * @return bool
339
     */
340 93
    protected function internalSet(
341
        $key,
342
        &$value,
343
        bool $checkProperties = true
344
    ): bool {
345
        if (
346 93
            $value instanceof self
347
            &&
348 93
            !$value instanceof TypeInterface
349
        ) {
350
            foreach ($value as $valueTmp) {
351
                parent::internalSet(
352
                    $key,
353
                    $valueTmp,
354
                    $checkProperties
355
                );
356
            }
357
358
            return true;
359
        }
360
361 93
        return parent::internalSet(
362 93
            $key,
363 93
            $value,
364 93
            $checkProperties
365
        );
366
    }
367
368
    /**
369
     * @param string|string[]|TypeCheckArray|TypeCheckInterface[]|null $type
370
     *
371
     * @return TypeCheckArray
372
     *
373
     * @phpstan-param null|string|string[]|class-string|class-string[]|TypeCheckArray<array-key,TypeCheckInterface>|array<array-key,TypeCheckInterface>|mixed $type
374
     * @phpstan-return TypeCheckArray<array-key,TypeCheckInterface>
375
     */
376 100
    protected static function convertIntoTypeCheckArray($type): TypeCheckArray
377
    {
378 100
        $is_array = false;
379
        if (
380 100
            \is_scalar($type)
381
            ||
382 100
            $is_array = \is_array($type)
383
        ) {
384
            /** @noinspection CallableParameterUseCaseInTypeContextInspection */
385 100
            $type = TypeCheckArray::create(
386
                [
387 100
                    Arrayy::ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES => new TypeCheckSimple($is_array ? $type : (string) $type),
388
                ]
389
            );
390
        }
391
392 100
        return $type;
393
    }
394
}
395