Completed
Push — master ( 98b55e...93f825 )
by Rudi
02:23
created

Sequence::union()   A

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
2
namespace Ds\Traits;
3
4
use Error;
5
use OutOfRangeException;
6
use Traversable;
7
use UnderflowException;
8
9
/**
10
 * Sequence
11
 *
12
 * @package Ds\Traits
13
 */
14
trait Sequence
15
{
16
    /**
17
     * @var array
18
     */
19
    private $internal = [];
20
21
    /**
22
     * @inheritDoc
23
     */
24
    public function __construct($values = null)
25
    {
26
        if (func_num_args()) {
27
            $this->push(...$values);
28
        }
29
    }
30
31
    /**
32
     * @inheritdoc
33
     */
34
    public function toArray(): array
35
    {
36
        return $this->internal;
37
    }
38
39
    /**
40
     * @inheritdoc
41
     */
42
    public function apply(callable $callback)
43
    {
44
        foreach ($this->internal as &$value) {
45
            $value = $callback($value);
46
        }
47
    }
48
49
    /**
50
     * @inheritdoc
51
     */
52
    public function merge($values): \Ds\Sequence
53
    {
54
        if ( ! is_array($values)) {
55
            $values = iterator_to_array($values);
56
        }
57
58
        return new self(array_merge($this->internal, $values));
59
    }
60
61
    /**
62
     * @inheritdoc
63
     */
64
    public function count(): int
65
    {
66
        return count($this->internal);
67
    }
68
69
    /**
70
     * @inheritDoc
71
     */
72
    public function contains(...$values): bool
73
    {
74
        foreach ($values as $value) {
75
            if ($this->find($value) === false) {
76
                return false;
77
            }
78
        }
79
80
        return true;
81
    }
82
83
    /**
84
     * @inheritDoc
85
     */
86
    public function filter(callable $callback = null): \Ds\Sequence
87
    {
88
        return new self(array_filter($this->internal, $callback ?: 'boolval'));
89
    }
90
91
    /**
92
     * @inheritDoc
93
     */
94
    public function find($value)
95
    {
96
        return array_search($value, $this->internal, true);
97
    }
98
99
    /**
100
     * @inheritDoc
101
     */
102
    public function first()
103
    {
104
        if (empty($this->internal)) {
105
            throw new UnderflowException();
106
        }
107
108
        return $this->internal[0];
109
    }
110
111
    /**
112
     * @inheritDoc
113
     */
114
    public function get(int $index)
115
    {
116
        $this->checkRange($index);
117
118
        return $this->internal[$index];
119
    }
120
121
    /**
122
     * @inheritDoc
123
     */
124
    public function insert(int $index, ...$values)
125
    {
126
        if ($index < 0 || $index > count($this->internal)) {
127
            throw new OutOfRangeException();
128
        }
129
130
        array_splice($this->internal, $index, 0, $values);
131
    }
132
133
    /**
134
     * @inheritDoc
135
     */
136
    public function join(string $glue = null): string
137
    {
138
        return implode($glue, $this->internal);
139
    }
140
141
    /**
142
     * @inheritDoc
143
     */
144
    public function last()
145
    {
146
        if ($this->isEmpty()) {
0 ignored issues
show
Bug introduced by
It seems like isEmpty() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
147
            throw new UnderflowException();
148
        }
149
150
        return end($this->internal);
151
    }
152
153
    /**
154
     * @inheritDoc
155
     */
156
    public function map(callable $callback): \Ds\Sequence
157
    {
158
        return new self(array_map($callback, $this->internal));
159
    }
160
161
    /**
162
     * @inheritDoc
163
     */
164 View Code Duplication
    public function pop()
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...
165
    {
166
        if ($this->isEmpty()) {
0 ignored issues
show
Bug introduced by
It seems like isEmpty() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
167
            throw new UnderflowException();
168
        }
169
170
        $value = array_pop($this->internal);
171
        $this->adjustCapacity();
0 ignored issues
show
Bug introduced by
It seems like adjustCapacity() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
172
173
        return $value;
174
    }
175
176
    /**
177
     * @inheritDoc
178
     */
179
    public function push(...$values)
180
    {
181
        if ($values) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $values of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
182
            array_push($this->internal, ...$values);
183
            $this->adjustCapacity();
0 ignored issues
show
Bug introduced by
It seems like adjustCapacity() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
184
        }
185
    }
186
187
    /**
188
     * @inheritDoc
189
     */
190
    public function reduce(callable $callback, $initial = null)
191
    {
192
        return array_reduce($this->internal, $callback, $initial);
193
    }
194
195
    /**
196
     * @inheritDoc
197
     */
198
    public function remove(int $index)
199
    {
200
        $this->checkRange($index);
201
202
        $value = array_splice($this->internal, $index, 1, null)[0];
203
        $this->adjustCapacity();
0 ignored issues
show
Bug introduced by
It seems like adjustCapacity() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
204
205
        return $value;
206
    }
207
208
    /**
209
     * @inheritDoc
210
     */
211
    public function reverse()
212
    {
213
        $this->internal = array_reverse($this->internal);
214
    }
215
216
    /**
217
     * @inheritDoc
218
     */
219
    public function reversed(): \Ds\Sequence
220
    {
221
        return new self(array_reverse($this->internal));
222
    }
223
224
    private function normalizeRotations(int $rotations, int $count)
225
    {
226
        if ($rotations < 0) {
227
            return $count - (abs($rotations) % $count);
228
        }
229
230
        return $rotations % $count;
231
    }
232
233
    /**
234
     * @inheritDoc
235
     */
236
    public function rotate(int $rotations)
237
    {
238
        if (count($this) < 2) {
239
            return;
240
        }
241
242
        $rotations = $this->normalizeRotations($rotations, count($this));
243
244
        while ($rotations--) {
245
            $this->push($this->shift());
246
        }
247
    }
248
249
    /**
250
     * @inheritDoc
251
     */
252
    public function set(int $index, $value)
253
    {
254
        $this->checkRange($index);
255
        $this->internal[$index] = $value;
256
    }
257
258
    /**
259
     * @inheritDoc
260
     */
261 View Code Duplication
    public function shift()
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...
262
    {
263
        if ($this->isEmpty()) {
0 ignored issues
show
Bug introduced by
It seems like isEmpty() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
264
            throw new UnderflowException();
265
        }
266
267
        $value = array_shift($this->internal);
268
        $this->adjustCapacity();
0 ignored issues
show
Bug introduced by
It seems like adjustCapacity() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
269
270
        return $value;
271
    }
272
273
    /**
274
     * @inheritDoc
275
     */
276
    public function slice(int $offset, int $length = null): \Ds\Sequence
277
    {
278
        if (func_num_args() === 1) {
279
            $length = count($this);
280
        }
281
282
        return new self(array_slice($this->internal, $offset, $length));
283
    }
284
285
    /**
286
     * @inheritDoc
287
     */
288
    public function sort(callable $comparator = null)
289
    {
290
        if ($comparator) {
291
            usort($this->internal, $comparator);
292
        } else {
293
            sort($this->internal);
294
        }
295
    }
296
297
    /**
298
     * @inheritDoc
299
     */
300
    public function sorted(callable $comparator = null): \Ds\Sequence
301
    {
302
        $internal = $this->internal;
303
304
        if ($comparator) {
305
            usort($internal, $comparator);
306
        } else {
307
            sort($internal);
308
        }
309
310
        return new self($internal);
311
    }
312
313
    /**
314
     * @inheritDoc
315
     */
316
    public function sum()
317
    {
318
        return array_sum($this->internal);
319
    }
320
321
    /**
322
     * @inheritDoc
323
     */
324
    public function unshift(...$values)
325
    {
326
        if ($values) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $values of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
327
            array_unshift($this->internal, ...$values);
328
            $this->adjustCapacity();
0 ignored issues
show
Bug introduced by
It seems like adjustCapacity() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
329
        }
330
    }
331
332
    /**
333
     *
334
     *
335
     * @param int $index
336
     */
337
    private function checkRange(int $index)
338
    {
339
        if ($index < 0 || $index >= count($this->internal)) {
340
            throw new OutOfRangeException();
341
        }
342
    }
343
344
    /**
345
     *
346
     */
347
    public function getIterator()
348
    {
349
        foreach ($this->internal as $value) {
350
            yield $value;
351
        }
352
    }
353
354
    /**
355
     * @inheritdoc
356
     */
357
    public function clear()
358
    {
359
        $this->internal = [];
360
        $this->capacity = self::MIN_CAPACITY;
0 ignored issues
show
Bug introduced by
The property capacity does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
361
    }
362
363
    /**
364
     * @inheritdoc
365
     */
366
    public function offsetSet($offset, $value)
367
    {
368
        if ($offset === null) {
369
            $this->push($value);
370
        } else {
371
            $this->set($offset, $value);
372
        }
373
    }
374
375
    /**
376
     * @inheritdoc
377
     */
378
    public function &offsetGet($offset)
379
    {
380
        $this->checkRange($offset);
381
        return $this->internal[$offset];
382
    }
383
384
    /**
385
     * @inheritdoc
386
     */
387
    public function offsetUnset($offset)
388
    {
389
        // Unset should be quiet, so we shouldn't allow 'remove' to throw.
390
        if (is_integer($offset) && $offset >= 0 && $offset < count($this)) {
391
            $this->remove($offset);
392
        }
393
    }
394
395
    /**
396
     * @inheritdoc
397
     */
398
    public function offsetExists($offset)
399
    {
400
        if ($offset < 0 || $offset >= count($this)) {
401
            return false;
402
        }
403
404
        return $this->get($offset) !== null;
405
    }
406
}
407