Completed
Push — master ( 3bd5ac...d91a6c )
by Lars
01:37
created

AbstractCollection::extractValue()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 7.3471

Importance

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