Passed
Push — master ( 987e4b...4b05d5 )
by Vincent
09:02
created

SortStream::forEach()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 3
nc 2
nop 1
dl 0
loc 4
ccs 3
cts 3
cp 1
crap 2
rs 10
c 1
b 0
f 0
1
<?php
2
3
namespace Bdf\Collection\Stream;
4
5
use Bdf\Collection\Util\Optional;
6
use Bdf\Collection\Util\OptionalInterface;
7
use Iterator;
8
use function asort;
9
use function current;
10
use function key;
11
use function next;
12
use function reset;
13
use function sort;
14
use function uasort;
15
use function usort;
16
17
/**
18
 * Implementation of StreamInterface::sort() return value
19
 *
20
 * @template T
21
 *
22
 * @implements StreamInterface<T, array-key>
23
 * @implements Iterator<array-key, T>
24
 *
25
 * @internal
26
 */
27
final class SortStream implements Iterator, StreamInterface
28
{
29
    use StreamTrait;
30
31
    /**
32
     * @var StreamInterface<T, mixed>
33
     */
34
    private $stream;
35
36
    /**
37
     * @var callable(T,T):int|null
38
     */
39
    private $comparator;
40
41
    /**
42
     * @var bool
43
     */
44
    private $preserveKeys;
45
46
    /**
47
     * @var T[]|null
48
     */
49
    private $data = null;
50
51
52
    /**
53
     * SortStream constructor.
54
     *
55
     * @param StreamInterface<T, mixed> $stream
56
     * @param callable(T,T):int|null $comparator
57
     * @param bool $preserveKeys
58
     */
59 39
    public function __construct(StreamInterface $stream, ?callable $comparator = null, bool $preserveKeys = true)
60
    {
61 39
        $this->stream = $stream;
62 39
        $this->comparator = $comparator;
63 39
        $this->preserveKeys = $preserveKeys;
64 39
    }
65
66
    /**
67
     * {@inheritdoc}
68
     *
69
     * @psalm-assert !null $this->data
70
     * @psalm-suppress InvalidReturnType
71
     */
72 22
    public function toArray(bool $preserveKeys = true): array
73
    {
74 22
        if ($this->data === null) {
75 22
            $this->buildData();
76
        }
77
78
        // Built data keep keys, but toArray() request without keys
79
        // So call array_values to remove keys
80 22
        if (!$preserveKeys && $this->preserveKeys) {
81 2
            return array_values($this->data);
0 ignored issues
show
Bug introduced by
It seems like $this->data can also be of type null; however, parameter $array of array_values() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

81
            return array_values(/** @scrutinizer ignore-type */ $this->data);
Loading history...
82
        }
83
84 21
        return $this->data;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->data could return the type null which is incompatible with the type-hinted return array. Consider adding an additional type-check to rule them out.
Loading history...
85
    }
86
87
    /**
88
     * {@inheritdoc}
89
     */
90 1
    public function forEach(callable $consumer): void
91
    {
92 1
        foreach ($this->toArray() as $k => $v) {
93 1
            $consumer($v, $k);
94
        }
95 1
    }
96
97
    /**
98
     * {@inheritdoc}
99
     */
100 2
    public function first(): OptionalInterface
101
    {
102 2
        $empty = true;
103 2
        $min = null;
104
105 2
        foreach ($this->stream as $value) {
106 2
            if ($empty) {
107 2
                $min = $value;
108 2
                $empty = false;
109
            } else {
110 2
                if ($this->comparator === null) {
111 1
                    if ($value < $min) {
112 1
                        $min = $value;
113
                    }
114 1
                } elseif (($this->comparator)($value, $min) < 0) {
115 1
                    $min = $value;
116
                }
117
            }
118
        }
119
120 2
        return Optional::nullable($min);
121
    }
122
123
    /**
124
     * {@inheritdoc}
125
     */
126 15
    public function current()
127
    {
128 15
        if ($this->data === null) {
129
            $this->buildData();
130
        }
131
132 15
        return current($this->data);
0 ignored issues
show
Bug introduced by
It seems like $this->data can also be of type null; however, parameter $array of current() does only seem to accept array|object, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

132
        return current(/** @scrutinizer ignore-type */ $this->data);
Loading history...
133
    }
134
135
    /**
136
     * {@inheritdoc}
137
     */
138 15
    public function next()
139
    {
140 15
        if ($this->data === null) {
141
            $this->buildData();
142
        }
143
144 15
        next($this->data);
0 ignored issues
show
Bug introduced by
It seems like $this->data can also be of type null; however, parameter $array of next() does only seem to accept array|object, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

144
        next(/** @scrutinizer ignore-type */ $this->data);
Loading history...
145 15
    }
146
147
    /**
148
     * {@inheritdoc}
149
     */
150 13
    public function key()
151
    {
152 13
        if ($this->data === null) {
153
            $this->buildData();
154
        }
155
156 13
        return key($this->data);
0 ignored issues
show
Bug introduced by
It seems like $this->data can also be of type null; however, parameter $array of key() does only seem to accept array|object, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

156
        return key(/** @scrutinizer ignore-type */ $this->data);
Loading history...
157
    }
158
159
    /**
160
     * {@inheritdoc}
161
     */
162 15
    public function valid()
163
    {
164 15
        if ($this->data === null) {
165 1
            $this->buildData();
166
        }
167
168 15
        return key($this->data) !== null;
0 ignored issues
show
Bug introduced by
It seems like $this->data can also be of type null; however, parameter $array of key() does only seem to accept array|object, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

168
        return key(/** @scrutinizer ignore-type */ $this->data) !== null;
Loading history...
169
    }
170
171
    /**
172
     * {@inheritdoc}
173
     */
174 14
    public function rewind()
175
    {
176 14
        if ($this->data === null) {
177 14
            $this->buildData();
178
        }
179
180 14
        reset($this->data);
0 ignored issues
show
Bug introduced by
It seems like $this->data can also be of type null; however, parameter $array of reset() does only seem to accept array|object, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

180
        reset(/** @scrutinizer ignore-type */ $this->data);
Loading history...
181 14
    }
182
183
    /**
184
     * Build the inner sorted data array
185
     *
186
     * @psalm-assert !null $this->data
187
     */
188 37
    private function buildData(): void
189
    {
190 37
        $data = $this->stream->toArray($this->preserveKeys);
191
192 37
        if ($this->comparator) {
193 7
            if ($this->preserveKeys) {
194 2
                uasort($data, $this->comparator);
195
            } else {
196 7
                usort($data, $this->comparator);
197
            }
198 31
        } elseif($this->preserveKeys) {
199 18
            asort($data);
200
        } else {
201 15
            sort($data);
202
        }
203
204 37
        $this->data = $data;
205 37
    }
206
}
207