Completed
Push — master ( 8d1df5...05ea10 )
by Rudi
03:16
created

Sequence::join()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
crap 1
1
<?php
2
namespace Ds\Traits;
3
4
use OutOfRangeException;
5
use Traversable;
6
use UnderflowException;
7
8
/**
9
 * Common functionality of all structures that implement 'Sequence'. Because the
10
 * polyfill's only goal is to achieve consistent behaviour, all sequences will
11
 * share the same implementation using an internal array.
12
 */
13
trait Sequence
14
{
15
    /**
16
     * @var array
17
     */
18
    private $internal = [];
19
20
    /**
21
     * @inheritDoc
22
     */
23 1698
    public function __construct($values = null)
24
    {
25 1698
        if (func_num_args()) {
26 1679
            $this->push(...$values);
27
        }
28 1698
    }
29
30
    /**
31
     * @inheritdoc
32
     */
33 932
    public function toArray(): array
34
    {
35 932
        return $this->internal;
36
    }
37
38
    /**
39
     * @inheritdoc
40
     */
41 14
    public function apply(callable $callback)
42
    {
43 14
        foreach ($this->internal as &$value) {
44 12
            $value = $callback($value);
45
        }
46 10
    }
47
48
    /**
49
     * @inheritdoc
50
     */
51 20
    public function merge($values): \Ds\Sequence
52
    {
53 20
        if ( ! is_array($values)) {
54 10
            $values = iterator_to_array($values);
55
        }
56
57 20
        return new self(array_merge($this->internal, $values));
58
    }
59
60
    /**
61
     * @inheritdoc
62
     */
63 1409
    public function count(): int
64
    {
65 1409
        return count($this->internal);
66
    }
67
68
    /**
69
     * @inheritDoc
70
     */
71 24
    public function contains(...$values): bool
72
    {
73 24
        foreach ($values as $value) {
74 20
            if ($this->find($value) === false) {
75 20
                return false;
76
            }
77
        }
78
79 12
        return true;
80
    }
81
82
    /**
83
     * @inheritDoc
84
     */
85 16
    public function filter(callable $callback = null): \Ds\Sequence
86
    {
87 16
        return new self(array_filter($this->internal, $callback ?: 'boolval'));
88
    }
89
90
    /**
91
     * @inheritDoc
92
     */
93 40
    public function find($value)
94
    {
95 40
        return array_search($value, $this->internal, true);
96
    }
97
98
    /**
99
     * @inheritDoc
100
     */
101 13
    public function first()
102
    {
103 13
        if (empty($this->internal)) {
104 3
            throw new UnderflowException();
105
        }
106
107 10
        return $this->internal[0];
108
    }
109
110
    /**
111
     * @inheritDoc
112
     */
113 146
    public function get(int $index)
114
    {
115 146
        $this->checkRange($index);
116
117 138
        return $this->internal[$index];
118
    }
119
120
    /**
121
     * @inheritDoc
122
     */
123 46
    public function insert(int $index, ...$values)
124
    {
125 46
        if ($index < 0 || $index > count($this->internal)) {
126 8
            throw new OutOfRangeException();
127
        }
128
129 38
        array_splice($this->internal, $index, 0, $values);
130 38
    }
131
132
    /**
133
     * @inheritDoc
134
     */
135 300
    public function join(string $glue = null): string
136
    {
137 300
        return implode($glue, $this->internal);
138
    }
139
140
    /**
141
     * @inheritDoc
142
     */
143 11
    public function last()
144
    {
145 11
        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...
146 3
            throw new UnderflowException();
147
        }
148
149 8
        return end($this->internal);
150
    }
151
152
    /**
153
     * @inheritDoc
154
     */
155 14
    public function map(callable $callback): \Ds\Sequence
156
    {
157 14
        return new self(array_map($callback, $this->internal));
158
    }
159
160
    /**
161
     * @inheritDoc
162
     */
163 20 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...
164
    {
165 20
        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...
166 5
            throw new UnderflowException();
167
        }
168
169 19
        $value = array_pop($this->internal);
170 19
        $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...
171
172 19
        return $value;
173
    }
174
175
    /**
176
     * @inheritDoc
177
     */
178 1692
    public function push(...$values)
179
    {
180 1692
        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...
181 1247
            array_push($this->internal, ...$values);
182 1247
            $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...
183
        }
184 1692
    }
185
186
    /**
187
     * @inheritDoc
188
     */
189 16
    public function reduce(callable $callback, $initial = null)
190
    {
191 16
        return array_reduce($this->internal, $callback, $initial);
192
    }
193
194
    /**
195
     * @inheritDoc
196
     */
197 35
    public function remove(int $index)
198
    {
199 35
        $this->checkRange($index);
200
201 27
        $value = array_splice($this->internal, $index, 1, null)[0];
202 27
        $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...
203
204 27
        return $value;
205
    }
206
207
    /**
208
     * @inheritDoc
209
     */
210 10
    public function reverse()
211
    {
212 10
        $this->internal = array_reverse($this->internal);
213 10
    }
214
215
    /**
216
     * @inheritDoc
217
     */
218 10
    public function reversed(): \Ds\Sequence
219
    {
220 10
        return new self(array_reverse($this->internal));
221
    }
222
223 32
    private function normalizeRotations(int $rotations, int $count)
224
    {
225 32
        if ($rotations < 0) {
226 14
            return $count - (abs($rotations) % $count);
227
        }
228
229 18
        return $rotations % $count;
230
    }
231
232
    /**
233
     * @inheritDoc
234
     */
235 52
    public function rotate(int $rotations)
236
    {
237 52
        if (count($this) < 2) {
238 20
            return;
239
        }
240
241 32
        $rotations = $this->normalizeRotations($rotations, count($this));
242
243 32
        while ($rotations--) {
244 24
            $this->push($this->shift());
245
        }
246 32
    }
247
248
    /**
249
     * @inheritDoc
250
     */
251 40
    public function set(int $index, $value)
252
    {
253 40
        $this->checkRange($index);
254 24
        $this->internal[$index] = $value;
255 24
    }
256
257
    /**
258
     * @inheritDoc
259
     */
260 50 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...
261
    {
262 50
        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...
263 3
            throw new UnderflowException();
264
        }
265
266 47
        $value = array_shift($this->internal);
267 47
        $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...
268
269 47
        return $value;
270
    }
271
272
    /**
273
     * @inheritDoc
274
     */
275 397
    public function slice(int $offset, int $length = null): \Ds\Sequence
276
    {
277 397
        if (func_num_args() === 1) {
278 198
            $length = count($this);
279
        }
280
281 397
        return new self(array_slice($this->internal, $offset, $length));
282
    }
283
284
    /**
285
     * @inheritDoc
286
     */
287 4
    public function sort(callable $comparator = null)
288
    {
289 4
        if ($comparator) {
290 2
            usort($this->internal, $comparator);
291
        } else {
292 2
            sort($this->internal);
293
        }
294 4
    }
295
296
    /**
297
     * @inheritDoc
298
     */
299 5
    public function sorted(callable $comparator = null): \Ds\Sequence
300
    {
301 5
        $internal = $this->internal;
302
303 5
        if ($comparator) {
304 3
            usort($internal, $comparator);
305
        } else {
306 2
            sort($internal);
307
        }
308
309 5
        return new self($internal);
310
    }
311
312
    /**
313
     * @inheritDoc
314
     */
315 21
    public function sum()
316
    {
317 21
        return array_sum($this->internal);
318
    }
319
320
    /**
321
     * @inheritDoc
322
     */
323 28
    public function unshift(...$values)
324
    {
325 28
        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...
326 26
            array_unshift($this->internal, ...$values);
327 26
            $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...
328
        }
329 28
    }
330
331
    /**
332
     *
333
     *
334
     * @param int $index
335
     */
336 251
    private function checkRange(int $index)
337
    {
338 251
        if ($index < 0 || $index >= count($this->internal)) {
339 40
            throw new OutOfRangeException();
340
        }
341 211
    }
342
343
    /**
344
     *
345
     */
346 52
    public function getIterator()
347
    {
348 52
        foreach ($this->internal as $value) {
349 40
            yield $value;
350
        }
351 52
    }
352
353
    /**
354
     * @inheritdoc
355
     */
356 8
    public function clear()
357
    {
358 8
        $this->internal = [];
359 8
        $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...
360 8
    }
361
362
    /**
363
     * @inheritdoc
364
     */
365 35
    public function offsetSet($offset, $value)
366
    {
367 35
        if ($offset === null) {
368 13
            $this->push($value);
369
        } else {
370 22
            $this->set($offset, $value);
371
        }
372 25
    }
373
374
    /**
375
     * @inheritdoc
376
     */
377 118
    public function &offsetGet($offset)
378
    {
379 118
        $this->checkRange($offset);
380 108
        return $this->internal[$offset];
381
    }
382
383
    /**
384
     * @inheritdoc
385
     */
386 22
    public function offsetUnset($offset)
387
    {
388
        // Unset should be quiet, so we shouldn't allow 'remove' to throw.
389 22
        if (is_integer($offset) && $offset >= 0 && $offset < count($this)) {
390 12
            $this->remove($offset);
391
        }
392 22
    }
393
394
    /**
395
     * @inheritdoc
396
     */
397 156
    public function offsetExists($offset)
398
    {
399 156
        if ($offset < 0 || $offset >= count($this)) {
400 32
            return false;
401
        }
402
403 124
        return $this->get($offset) !== null;
404
    }
405
}
406