Issues (590)

src/Collection/ArrayCollection.php (1 issue)

1
<?php
2
3
namespace Bdf\Prime\Collection;
4
5
use ArrayIterator;
6
use Bdf\Prime\PrimeSerializable;
7
use Closure;
8
use IteratorAggregate;
9
10
/**
11
 * Basic array collection
12
 *
13
 * @template E
14
 *
15
 * @implements CollectionInterface<E>
16
 * @implements IteratorAggregate<array-key, E>
17
 */
18
class ArrayCollection extends PrimeSerializable implements IteratorAggregate, CollectionInterface
19
{
20
    /**
21
     * Container of items
22
     *
23
     * @var E[]
24
     */
25
    private $items = [];
26
27
28
    /**
29
     * Create a collection
30
     *
31
     * @param E[]|CollectionInterface<E>|null $items
32
     */
33 126
    public function __construct($items = [])
34
    {
35 126
        $this->items = $this->getArrayFromItems($items);
36
    }
37
38
    /**
39
     * {@inheritdoc}
40
     */
41 12
    public function pushAll(array $items)
42
    {
43 12
        $this->items = $items;
44
45 12
        return $this;
46
    }
47
48
    /**
49
     * {@inheritdoc}
50
     */
51 1
    public function push($item)
52
    {
53 1
        $this->put(null, $item);
54
55 1
        return $this;
56
    }
57
58
    /**
59
     * {@inheritdoc}
60
     */
61 3
    public function put($key, $item)
62
    {
63 3
        if ($key === null) {
64 1
            $this->items[] = $item;
65
        } else {
66 2
            $this->items[$key] = $item;
67
        }
68
69 3
        return $this;
70
    }
71
72
    /**
73
     * SPL - ArrayAccess
74
     *
75
     * {@inheritdoc}
76
     */
77 1
    public function offsetSet($key, $value): void
78
    {
79 1
        $this->put($key, $value);
80
    }
81
82
    /**
83
     * {@inheritdoc}
84
     */
85 46
    public function all()
86
    {
87 46
        return $this->items;
88
    }
89
90
    /**
91
     * {@inheritdoc}
92
     */
93 15
    public function get($key, $default = null)
94
    {
95 15
        if ($this->has($key)) {
96 14
            return $this->items[$key];
97
        }
98
99 2
        return $default;
100
    }
101
102
    /**
103
     * SPL - ArrayAccess
104
     *
105
     * {@inheritdoc}
106
     */
107
    #[\ReturnTypeWillChange]
108 3
    public function offsetGet($key)
109
    {
110 3
        return $this->items[$key];
111
    }
112
113
    /**
114
     * {@inheritdoc}
115
     */
116 15
    public function has($key)
117
    {
118 15
        return isset($this->items[$key]);
119
    }
120
121
    /**
122
     * SPL - ArrayAccess
123
     *
124
     * {@inheritdoc}
125
     */
126 1
    public function offsetExists($key): bool
127
    {
128 1
        return isset($this->items[$key]);
129
    }
130
131
    /**
132
     * {@inheritdoc}
133
     */
134 2
    public function remove($key)
135
    {
136 2
        unset($this->items[$key]);
137
138 2
        return $this;
139
    }
140
141
    /**
142
     * SPL - ArrayAccess
143
     *
144
     * {@inheritdoc}
145
     */
146 1
    public function offsetUnset($key): void
147
    {
148 1
        $this->remove($key);
149
    }
150
151
    /**
152
     * {@inheritdoc}
153
     */
154 1
    public function clear()
155
    {
156 1
        $this->items = [];
157
158 1
        return $this;
159
    }
160
161
    /**
162
     * {@inheritdoc}
163
     */
164 1
    public function keys()
165
    {
166 1
        return array_keys($this->items);
0 ignored issues
show
Bug Best Practice introduced by
The expression return array_keys($this->items) returns the type array which is incompatible with the return type mandated by Bdf\Prime\Collection\CollectionInterface::keys() of Bdf\Prime\Collection\list.

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
167
    }
168
169
    /**
170
     * SPL - Countable
171
     *
172
     * {@inheritdoc}
173
     */
174 15
    public function count(): int
175
    {
176 15
        return count($this->items);
177
    }
178
179
    /**
180
     * SPL - IteratorAggregate
181
     *
182
     * {@inheritdoc}
183
     */
184 21
    public function getIterator(): \Iterator
185
    {
186 21
        return new ArrayIterator($this->items);
187
    }
188
189
    /**
190
     * {@inheritdoc}
191
     */
192 3
    public function isEmpty()
193
    {
194 3
        return empty($this->items);
195
    }
196
197
    /**
198
     * {@inheritdoc}
199
     */
200 3
    public function map($callback)
201
    {
202 3
        $keys  = array_keys($this->items);
203 3
        $items = array_map($callback, $this->items, $keys);
204
205 3
        return new static(array_combine($keys, $items));
206
    }
207
208
    /**
209
     * {@inheritdoc}
210
     */
211 3
    public function filter($callback = null)
212
    {
213 3
        if ($callback !== null) {
214 2
            return new static(array_filter($this->items, $callback));
215
        }
216
217 1
        return new static(array_filter($this->items));
218
    }
219
220
    /**
221
     * {@inheritdoc}
222
     */
223 9
    public function groupBy($groupBy, $mode = self::GROUPBY)
224
    {
225 9
        $results = [];
226
227 9
        if (!is_callable($groupBy)) {
228 5
            if ($mode === self::GROUPBY_CUSTOM) {
229 1
                throw new \LogicException('Custom mode should only used with callable callback');
230
            }
231
232 4
            $groupBy = function ($item, $key, $results) use ($groupBy) {
233 4
                return $this->getDataFromItem($item, $groupBy);
234 4
            };
235
        }
236
237 8
        foreach ($this->items as $key => $item) {
238 8
            $groupKey = $groupBy($item, $key, $results);
239
240
            switch ($mode) {
241 8
                case self::GROUPBY:
242 5
                    $results[$groupKey] = $item;
243 5
                    break;
244
245 3
                case self::GROUPBY_COMBINE:
246 1
                    $results[$groupKey][] = $item;
247 1
                    break;
248
249 2
                case self::GROUPBY_PRESERVE:
250 1
                    $results[$groupKey][$key] = $item;
251 1
                    break;
252
253
                default:
254
                    // Custom combine, should be done in closure
255 1
                    break;
256
            }
257
        }
258
259 8
        return new static($results);
260
    }
261
262
    /**
263
     * {@inheritdoc}
264
     */
265 3
    public function contains($element)
266
    {
267 3
        if (!($element instanceof Closure)) {
268 2
            return in_array($element, $this->items, true);
269
        }
270
271 1
        foreach ($this->items as $key => $item) {
272 1
            if ($element($item, $key)) {
273 1
                return true;
274
            }
275
        }
276
277 1
        return false;
278
    }
279
280
    /**
281
     * {@inheritdoc}
282
     */
283 3
    public function indexOf($value, $strict = false)
284
    {
285 3
        if (!($value instanceof Closure)) {
286 2
            return array_search($value, $this->items, $strict);
287
        }
288
289 1
        foreach ($this->items as $key => $item) {
290 1
            if ($value($item, $key)) {
291 1
                return $key;
292
            }
293
        }
294
295
        return false;
296
    }
297
298
    /**
299
     * {@inheritdoc}
300
     */
301 1
    public function merge($items)
302
    {
303 1
        return new static(array_merge($this->items, $this->getArrayFromItems($items)));
304
    }
305
306
    /**
307
     * {@inheritdoc}
308
     */
309 2
    public function sort(callable $callback = null)
310
    {
311 2
        $items = $this->items;
312 2
        $callback ? uasort($items, $callback) : natcasesort($items);
313
314 2
        return new static($items);
315
    }
316
317
    /**
318
     * Get array of items from the given items
319
     *
320
     * @param mixed $items
321
     * @return array
322
     */
323 126
    protected function getArrayFromItems($items)
324
    {
325 126
        if (is_array($items)) {
326 117
            return $items;
327
        }
328
329 10
        if ($items instanceof self) {
330 1
            return $items->all();
331
        }
332
333 10
        return (array)$items;
334
    }
335
336
    /**
337
     * Get value from item
338
     *
339
     * @param array|object $item
340
     * @param string       $key
341
     * @param mixed        $default
342
     *
343
     * @return mixed
344
     */
345 4
    protected function getDataFromItem($item, $key, $default = null)
346
    {
347 4
        if (is_array($item)) {
348
            return isset($item[$key]) ? $item[$key] : $default;
349
        }
350
351 4
        if (is_object($item)) {
352 4
            if (isset($item->$key)) {
353 4
                return $item->$key;
354
            }
355
356
            if (method_exists($item, $key)) {
357
                return $item->$key();
358
            }
359
360
            $method = 'get'.ucfirst($key);
361
362
            if (method_exists($item, $method)) {
363
                return $item->$method();
364
            }
365
        }
366
367
        return $default;
368
    }
369
}
370