Collection::derive()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
1
<?php namespace nyx\core\collections\traits;
2
3
// External dependencies
4
use nyx\utils;
5
6
// Internal dependencies
7
use nyx\core\collections\interfaces;
8
use nyx\core;
9
10
/**
11
 * Collection
12
 *
13
 * A Collection is an object that contains other items which can be set, get and removed from the Collection.
14
 *
15
 * Usage of this trait allows you to implement \IteratorAggregate and the interfaces\Collection interface.
16
 *
17
 * Important notes:
18
 * 1) null is *not* an acceptable value for an item within a Collection. Null is used internally by many methods
19
 *    to denote an item that is *not set*. Likewise the methods will bombard you with exceptions if you attempt
20
 *    to set an item with null as its value. This is done to ensure the return values of the API are consistent
21
 *    and also provides a slight performance gain for some methods;
22
 * 2) Some of the methods, like self::map() or self::filter() for instance, make assumptions as to the constructor
23
 *    of the exhibitor of this trait, assuming that it accepts a Collection, Arrayable object or array as
24
 *    its first argument;
25
 *
26
 * @version     0.1.0
27
 * @author      Michal Chojnacki <[email protected]>
28
 * @copyright   2012-2017 Nyx Dev Team
29
 * @link        https://github.com/unyx/nyx
30
 */
31
trait Collection
32
{
33
    /**
34
     * The traits of a Collection trait.
35
     */
36
    use core\traits\Serializable;
37
38
    /**
39
     * @var array   The items contained in the Collection.
40
     */
41
    protected $items = [];
42
43
    /**
44
     * @see \nyx\core\collections\interfaces\Collection::all()
45
     */
46
    public function all() : array
47
    {
48
        return $this->items;
49
    }
50
51
    /**
52
     * @see \nyx\core\collections\interfaces\Collection::find()
53
     */
54
    public function find(callable $callback, $default = null)
55
    {
56
        return utils\Arr::find($this->items, $callback, $default);
57
    }
58
59
    /**
60
     * @see \nyx\core\collections\interfaces\Collection::first()
61
     */
62
    public function first($elements = null, $default = null)
63
    {
64
        return utils\Arr::first($this->items, $elements, $default);
65
    }
66
67
    /**
68
     * @see \nyx\core\collections\interfaces\Collection::last()
69
     */
70
    public function last($elements = null, $default = null)
71
    {
72
        return utils\Arr::last($this->items, $elements, $default);
73
    }
74
75
    /**
76
     * @see \nyx\core\collections\interfaces\Collection::initial()
77
     */
78
    public function initial($callback = false, $default = null)
79
    {
80
        return utils\Arr::initial($this->items, $callback, $default);
0 ignored issues
show
Documentation introduced by
$callback is of type boolean, but the function expects a callable|integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
81
    }
82
83
    /**
84
     * @see \nyx\core\collections\interfaces\Collection::rest()
85
     */
86
    public function rest($callback = false, $default = null)
87
    {
88
        return utils\Arr::rest($this->items, $callback, $default);
89
    }
90
91
    /**
92
     * @see \nyx\core\collections\interfaces\Collection::slice()
93
     */
94
    public function slice(int $offset, int $length = null, bool $preserveKeys = true) : interfaces\Collection
95
    {
96
        return $this->derive(array_slice($this->items, $offset, $length, $preserveKeys));
97
    }
98
99
    /**
100
     * @see \nyx\core\collections\interfaces\Collection::pluck()
101
     */
102
    public function pluck($key, $index = null) : array
103
    {
104
        return utils\Arr::pluck($this->items, $key, $index);
105
    }
106
107
    /**
108
     * @see \nyx\core\collections\interfaces\Collection::select()
109
     */
110
    public function select(callable $filter) : interfaces\Collection
111
    {
112
        return $this->derive(array_filter($this->items, $filter, ARRAY_FILTER_USE_BOTH));
113
    }
114
115
    /**
116
     * @see \nyx\core\collections\interfaces\Collection::reject()
117
     */
118
    public function reject(callable $filter) : interfaces\Collection
119
    {
120
        $result = [];
121
122
        foreach ($this->items as $key => $item) {
123
            if (!$filter($item, $key)) {
124
                $result[$key] = $item;
125
            }
126
        }
127
128
        return $this->derive($result);
129
    }
130
131
    /**
132
     * @see \nyx\core\collections\interfaces\Collection::map()
133
     */
134
    public function map(callable $callback) : interfaces\Collection
135
    {
136
        return $this->derive(array_map($callback, $this->items, array_keys($this->items)));
137
    }
138
139
    /**
140
     * @see \nyx\core\collections\interfaces\Collection::each()
141
     */
142
    public function each(callable $callback) : interfaces\Collection
143
    {
144
        array_walk($this->items, $callback);
145
146
        return $this;
147
    }
148
149
    /**
150
     * @see \nyx\core\collections\interfaces\Collection::reduce()
151
     */
152
    public function reduce(callable $callback, $initial = null)
153
    {
154
        return array_reduce($this->items, $callback, $initial);
155
    }
156
157
    /**
158
     * @see \nyx\core\collections\interfaces\Collection::implode()
159
     */
160
    public function implode($value, string $glue = '') : string
161
    {
162
        return implode($glue, $this->pluck($value));
163
    }
164
165
    /**
166
     * @see \nyx\core\collections\interfaces\Collection::reverse()
167
     */
168
    public function reverse() : interfaces\Collection
169
    {
170
        return $this->derive(array_reverse($this->items));
171
    }
172
173
    /**
174
     * @see \nyx\core\collections\interfaces\Collection::collapse()
175
     */
176
    public function collapse() : interfaces\Collection
177
    {
178
        return $this->derive(utils\Arr::collapse($this->items));
179
    }
180
181
    /**
182
     * @see \nyx\core\collections\interfaces\Collection::flatten()
183
     */
184
    public function flatten() : interfaces\Collection
185
    {
186
        return $this->derive(utils\Arr::flatten($this->items));
187
    }
188
189
    /**
190
     * @see \nyx\core\collections\interfaces\Collection::fetch()
191
     */
192
    public function fetch($key) : interfaces\Collection
193
    {
194
        return $this->derive(utils\Arr::fetch($this->items, $key));
195
    }
196
197
    /**
198
     * @see \nyx\core\collections\interfaces\Collection::merge()
199
     */
200 View Code Duplication
    public function merge(...$with) : interfaces\Collection
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...
201
    {
202
        $arrays = [$this->items];
203
204
        foreach ($with as $items) {
205
            $arrays[] = $this->extractItems($items);
206
        }
207
208
        return $this->derive(array_merge(...$arrays));
209
    }
210
211
    /**
212
     * @see \nyx\core\collections\interfaces\Collection::diff()
213
     */
214 View Code Duplication
    public function diff(...$against) : interfaces\Collection
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...
215
    {
216
        $arrays = [$this->items];
217
218
        foreach ($against as $items) {
219
            $arrays[] = $this->extractItems($items);
220
        }
221
222
        return $this->derive(array_diff(...$arrays));
223
    }
224
225
    /**
226
     * @see \nyx\core\collections\interfaces\Collection::isEmpty()
227
     */
228
    public function isEmpty() : bool
229
    {
230
        return empty($this->items);
231
    }
232
233
    /**
234
     * @see \Countable::count()
235
     */
236
    public function count() : int
237
    {
238
        return count($this->items);
239
    }
240
241
    /**
242
     * Returns an Iterator for the items in this Collection. Allows for the implementation of \IteratorAggregate.
243
     *
244
     * @return  \ArrayIterator
245
     */
246
    public function getIterator() : \ArrayIterator
247
    {
248
        return new \ArrayIterator($this->items);
249
    }
250
251
    /**
252
     * @see \Serializable::unserialize()
253
     */
254
    public function unserialize($data)
255
    {
256
        $this->items = unserialize($data);
257
    }
258
259
    /**
260
     * @see \nyx\core\interfaces\Arrayable::toArray()
261
     */
262
    public function toArray() : array
263
    {
264
        return array_map(function($value) {
265
            return $value instanceof core\interfaces\Arrayable ? $value->toArray() : $value;
266
        }, $this->items);
267
    }
268
269
    /**
270
     * Make sure we're able to handle deep copies properly. This will work for instances of the exhibitor of this
271
     * trait contained within the exhibitor's Collection itself, but may require overrides for customized
272
     * Collections.
273
     */
274
    public function __clone()
275
    {
276
        foreach ($this->items as $key => $value) {
277
            if ($value instanceof interfaces\Collection) {
278
                $this->items[$key] = clone $value;
279
            }
280
        }
281
    }
282
283
    /**
284
     * Creates a new instance based on this one, but populated by the given items.
285
     *
286
     * This can be useful as an override in child classes which take required parameters other than the items
287
     * in their constructor, allowing you to pass them in by overriding this method and thus more easily
288
     * retain the functionality of methods like map() or reduce() which return new instances of the Collection.
289
     *
290
     * @param   mixed   $items          The items to populate the new Collection with.
291
     * @return  interfaces\Collection
292
     */
293
    protected function derive($items) : interfaces\Collection
294
    {
295
        return new static($items);
0 ignored issues
show
Unused Code introduced by
The call to Collection::__construct() has too many arguments starting with $items.

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...
296
    }
297
298
    /**
299
     * Inspects the given $items and attempts to resolve them to an iterable collection of items.
300
     *
301
     * @param   mixed   $items
302
     * @return  iterable|array
303
     */
304
    protected function extractItems($items) : iterable
305
    {
306
        // Catch arrays and Traversable objects.
307
        // By extension, this also includes other Collections (via their Iterator) *and* generators.
308
        // Keep especially the latter in mind when overriding concrete Collections.
309
        if (is_iterable($items)) {
310
            return $items;
311
        }
312
313
        // Arbitrary interfaces in order of preference.
314
        if ($items instanceof core\interfaces\Arrayable) {
315
            return $items->toArray();
316
        }
317
318
        if ($items instanceof \JsonSerializable) {
319
            return $items->jsonSerialize();
320
        }
321
322
        if ($items instanceof core\interfaces\Jsonable) {
323
            return json_decode($items->toJson(), true);
324
        }
325
326
        // Worst case scenario - use PHP's internals to attempt to cast it to an array.
327
        return (array) $items;
328
    }
329
}
330