SortStream::first()   A
last analyzed

Complexity

Conditions 6
Paths 6

Size

Total Lines 21
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 6

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 13
nc 6
nop 0
dl 0
loc 21
ccs 13
cts 13
cp 1
crap 6
rs 9.2222
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-suppress InvalidReturnType
70
     */
71 22
    public function toArray(bool $preserveKeys = true): array
72
    {
73 22
        if ($this->data === null) {
74 22
            $this->buildData();
75
        }
76
77
        // Built data keep keys, but toArray() request without keys
78
        // So call array_values to remove keys
79
        /** @psalm-suppress RedundantConditionGivenDocblockType */
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
    #[\ReturnTypeWillChange]
127 15
    public function current()
128
    {
129 15
        if ($this->data === null) {
130
            $this->buildData();
131
        }
132
133 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

133
        return current(/** @scrutinizer ignore-type */ $this->data);
Loading history...
134
    }
135
136
    /**
137
     * {@inheritdoc}
138
     */
139 15
    public function next(): void
140
    {
141 15
        if ($this->data === null) {
142
            $this->buildData();
143
        }
144
145 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

145
        next(/** @scrutinizer ignore-type */ $this->data);
Loading history...
146 15
    }
147
148
    /**
149
     * {@inheritdoc}
150
     */
151
    #[\ReturnTypeWillChange]
152 13
    public function key()
153
    {
154 13
        if ($this->data === null) {
155
            $this->buildData();
156
        }
157
158 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

158
        return key(/** @scrutinizer ignore-type */ $this->data);
Loading history...
159
    }
160
161
    /**
162
     * {@inheritdoc}
163
     */
164 15
    public function valid(): bool
165
    {
166 15
        if ($this->data === null) {
167 1
            $this->buildData();
168
        }
169
170 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

170
        return key(/** @scrutinizer ignore-type */ $this->data) !== null;
Loading history...
171
    }
172
173
    /**
174
     * {@inheritdoc}
175
     */
176 14
    public function rewind(): void
177
    {
178 14
        if ($this->data === null) {
179 14
            $this->buildData();
180
        }
181
182 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

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