1 | <?php |
||
2 | |||
3 | namespace Bdf\Collection\Stream; |
||
4 | |||
5 | use Bdf\Collection\Stream\Accumulator\AccumulatorInterface; |
||
6 | use Bdf\Collection\Stream\Collector\CollectorInterface; |
||
7 | use Bdf\Collection\Util\Functor\Consumer\ConsumerInterface; |
||
8 | use Bdf\Collection\Util\Functor\Predicate\PredicateInterface; |
||
9 | use Bdf\Collection\Util\Functor\Transformer\TransformerInterface; |
||
10 | use Bdf\Collection\Util\OptionalInterface; |
||
11 | use Iterator; |
||
12 | |||
13 | /** |
||
14 | * Stream apply operations on each elements of a Collection |
||
15 | * |
||
16 | * A stream instance can only be used once. It has two types of methods : |
||
17 | * - Transformation methods which return a new stream for applying transformations on elements. Any methods can be called after a transformation method |
||
18 | * - Terminal methods which iterate over the stream and "close" the stream. After calling a terminal method, no more methods can be called |
||
19 | * |
||
20 | * The transformations will be applied only when a termination method is called. So a stream should be used like : |
||
21 | * - Call one or more transformations |
||
22 | * - Finish processing with a terminal method |
||
23 | * |
||
24 | * <code> |
||
25 | * $collection->stream() // Create the stream from the collection |
||
26 | * ->map(...) // Apply transformations |
||
27 | * ->filter(...) |
||
28 | * ->forEach(...) // Terminate the stream |
||
29 | * ; |
||
30 | * </code> |
||
31 | * |
||
32 | * @template T |
||
33 | * @template K |
||
34 | * |
||
35 | * @extends Iterator<K, T> |
||
36 | */ |
||
37 | interface StreamInterface extends Iterator |
||
38 | { |
||
39 | /** |
||
40 | * Apply $transformer to each values of the stream |
||
41 | * |
||
42 | * <code> |
||
43 | * $stream = new ArrayStream([1, 2, 3]); |
||
44 | * $stream |
||
45 | * ->map(function ($element, $key) { return $element * 2; }) |
||
46 | * ->toArray() // [2, 4, 6] |
||
47 | * ; |
||
48 | * </code> |
||
49 | * |
||
50 | * @template R |
||
51 | * @param callable(T, K=):R $transformer The element transformer. |
||
52 | * Should take the element as first parameter an return the transformed element |
||
53 | * The transformer may have (if relevant) the key as second parameter |
||
54 | * |
||
55 | * @return StreamInterface<R, K> |
||
56 | * |
||
57 | * @see TransformerInterface |
||
58 | */ |
||
59 | public function map(callable $transformer): StreamInterface; |
||
60 | |||
61 | /** |
||
62 | * Apply $function to each values of the stream for generates keys |
||
63 | * |
||
64 | * The return type of the function is not checked, and duplicate keys, or illegal array offset may be generated. |
||
65 | * In such cases, toArray(), or other collector methods may have undefined behavior (others stream methods can be used safely). |
||
66 | * For remove elements with same result of the function, use distinct(), or IndexingBy collector. |
||
67 | * |
||
68 | * <code> |
||
69 | * $stream = new ArrayStream([1, 2, 3]); |
||
70 | * $stream |
||
71 | * ->map(function ($element, $key) { return $element * 2; }) |
||
72 | * ->toArray() // [2 => 1, 4 => 2, 6 => 3] |
||
73 | * ; |
||
74 | * |
||
75 | * // Apply transformation to the key (snake_case to PascalCase) |
||
76 | * $stream = new ArrayStream(['first_name' => 'John', 'last_name' => 'Doe']); |
||
77 | * $stream |
||
78 | * ->mapKey(function ($e, $key) { |
||
79 | * return Streams::wrap(explode('_', $key))->map(function ($k) { return ucfirst($k); })->collect(new Joining()); |
||
80 | * }) |
||
81 | * ->toArray() // ['FirstName' => 'John', 'LastName' => 'Doe'] |
||
82 | * ; |
||
83 | * </code> |
||
84 | * |
||
85 | * @template R |
||
86 | * @param callable(T, K):R $function The key generator. |
||
87 | * Should take the element as first parameter, the key as second parameter, and return the new key |
||
88 | * |
||
89 | * @return StreamInterface<T, R> |
||
90 | * |
||
91 | * @see TransformerInterface |
||
92 | */ |
||
93 | public function mapKey(callable $function): StreamInterface; |
||
94 | |||
95 | /** |
||
96 | * Filter the stream |
||
97 | * |
||
98 | * <code> |
||
99 | * $stream = new ArrayStream([1, 2, 3]); |
||
100 | * $stream |
||
101 | * ->filter(function ($element, $key) { return $element % 2 !== 0; }) |
||
102 | * ->toArray() // [1, 3] |
||
103 | * ; |
||
104 | * </code> |
||
105 | * |
||
106 | * @param callable(T, K=):bool $predicate The predicate function. |
||
107 | * Take the element as first parameter and should return a boolean (true for keeping element, or false for skipping) |
||
108 | * May take the key as second parameter (if relevant) |
||
109 | * |
||
110 | * @return StreamInterface<T, K> |
||
111 | * |
||
112 | * @see PredicateInterface |
||
113 | */ |
||
114 | public function filter(callable $predicate): StreamInterface; |
||
115 | |||
116 | /** |
||
117 | * Filter stream elements to get only distinct elements |
||
118 | * Two elements are considered as equals when there hash are equals : `$hashFunction($e1) === $hashFunction($e2)` |
||
119 | * |
||
120 | * If the hash function is not provided, elements will be compared using : |
||
121 | * - If it's an Hashable object, the Hashable::hash() method |
||
122 | * - In other case, compare with value AND type |
||
123 | * |
||
124 | * By default, int(123) and string('123') are not considered as equal, and will be keep into the distinct stream |
||
125 | * |
||
126 | * <code> |
||
127 | * $stream = new ArrayStream([4, 8, 1, 4, 1]); |
||
128 | * $stream->distinct(); // [4, 8, 1] |
||
129 | * |
||
130 | * $stream = new ArrayStream([[1, 2], [2, 3], [2, 1]]); |
||
131 | * $stream->distinct(function ($e) { sort($e); return json_encode($e); }); // [[1, 2], [2, 3]] |
||
132 | * </code> |
||
133 | * |
||
134 | * @param callable(T):array-key|null $hashFunction The hash function. Take as parameter the element, and return the hash value as string |
||
135 | * |
||
136 | * @return StreamInterface<T, K> |
||
137 | */ |
||
138 | public function distinct(?callable $hashFunction = null): StreamInterface; |
||
139 | |||
140 | /** |
||
141 | * Order stream elements |
||
142 | * |
||
143 | * <code> |
||
144 | * $stream = new ArrayStream([8, 4, 5, 3]); |
||
145 | * $stream->sort()->toArray(); // [3, 4, 5, 8] |
||
146 | * |
||
147 | * $stream |
||
148 | * ->sort(function ($a, $b) { return ([$a % 2, $a] <=> [$b % 2, $b]); }) |
||
149 | * ->toArray() // [4, 8, 3, 5] |
||
150 | * ; |
||
151 | * |
||
152 | * // Sort keeping keys |
||
153 | * $stream = new ArrayStream([ |
||
154 | * 'foo' => 3, |
||
155 | * 'bar' => 42, |
||
156 | * 'baz' => 9 |
||
157 | * ]); |
||
158 | * |
||
159 | * $stream->sort(null, true)->toArray(); |
||
160 | * // [ 'foo' => 3, |
||
161 | * // 'baz' => 9, |
||
162 | * // 'bar' => 42 ] |
||
163 | * </code> |
||
164 | * |
||
165 | * /!\ Unlike other transformations, the elements are fetched before execution of the terminal method |
||
166 | * |
||
167 | * Ex : |
||
168 | * <code> |
||
169 | * $stream = new ArrayStream([1, 2, 3]); |
||
170 | * |
||
171 | * // Display : map(1) forEach(1) map(2) forEach(2) map(3) forEach(3) |
||
172 | * $stream |
||
173 | * ->map(function ($e) { echo "map($e) "; return $e; }) |
||
174 | * ->forEach(function ($e) { echo "forEach($e) "; }) |
||
175 | * ; |
||
176 | * |
||
177 | * // Display : map(1) map(2) map(3) forEach(1) forEach(2) forEach(3) |
||
178 | * $stream |
||
179 | * ->map(function ($e) { echo "map($e) "; return $e; }) |
||
180 | * ->sort() // Sort fetch the map stream |
||
181 | * ->forEach(function ($e) { echo "forEach($e) "; }) |
||
182 | * ; |
||
183 | * </code> |
||
184 | * |
||
185 | * @param callable(T,T):int|null $comparator The comparator, or null to use default comparison. |
||
186 | * Take the two values to compare as parameters and should return an integer : |
||
187 | * - $comparator($a, $b) < 0 => $a < $b |
||
188 | * - $comparator($a, $b) == 0 => $a == $b |
||
189 | * - $comparator($a, $b) > 0 => $a > $b |
||
190 | * |
||
191 | * @param boolean $preserveKeys If true, the keys will be kept, else the values will be indexed by an increment integer |
||
192 | * |
||
193 | * @return StreamInterface<T, array-key> |
||
194 | */ |
||
195 | public function sort(callable $comparator = null, bool $preserveKeys = false): StreamInterface; |
||
196 | |||
197 | /** |
||
198 | * Concatenate a new stream after the current stream |
||
199 | * The current stream will be the first executed stream, and the concatenated one will be executed after |
||
200 | * |
||
201 | * /!\ The current stream must not be an infinite stream |
||
202 | * |
||
203 | * <code> |
||
204 | * $stream = new ArrayStream([1, 2, 3]); |
||
205 | * $stream |
||
206 | * ->concat(new ArrayStream([4, 5, 6]), false) |
||
207 | * ->toArray() // [1, 2, 3, 4, 5, 6] |
||
208 | * ; |
||
209 | * </code> |
||
210 | * |
||
211 | * @param StreamInterface<T, mixed> $stream The stream to concat |
||
212 | * @param bool $preserveKeys Preserve the stream keys, or use integer increment index |
||
213 | * |
||
214 | * @return StreamInterface<T, mixed> |
||
215 | */ |
||
216 | public function concat(StreamInterface $stream, bool $preserveKeys = true): StreamInterface; |
||
217 | |||
218 | /** |
||
219 | * Create a stream resulting of concatenation of each elements content extracted by $transformer |
||
220 | * This method reduce by one the depth of multidimensional stream |
||
221 | * |
||
222 | * Example: |
||
223 | * <code> |
||
224 | * $stream = new ArrayStream([ |
||
225 | * ['values' => [1, 2]], |
||
226 | * ['values' => 3], |
||
227 | * ['values' => new ArrayStream([4, 5])], |
||
228 | * ]); |
||
229 | * |
||
230 | * $stream->flatMap(function ($e) { return $e['values']; })->toArray(); // [1, 2, 3, 4, 5] |
||
231 | * </code> |
||
232 | * |
||
233 | * (i) Each transformed elements will be transformed to a Stream using Streams::wrap() |
||
234 | * Empty array and null will be transformed to an EmptyStream, array to an array stream, etc... |
||
235 | * For ensure that no transformation is applied, the transformer should return a StreamInterface |
||
236 | * |
||
237 | * This method is equivalent with : |
||
238 | * <code> |
||
239 | * $stream |
||
240 | * ->map($transformer) |
||
241 | * ->map(function ($e) { return Streams::wrap($e); }) |
||
242 | * ->reduce( |
||
243 | * function (StreamInterface $a, StreamInterface $b) { return $a->concat($b); }, |
||
244 | * EmptyStream::instance() |
||
245 | * ) |
||
246 | * ; |
||
247 | * </code> |
||
248 | * |
||
249 | * @template R |
||
250 | * @param callable(T, K):(StreamInterface<R, mixed>|R[]|R) $transformer The element transformer |
||
251 | * Should take the element as first parameter and return the transformed element |
||
252 | * The transformer may have (if relevant) the key as second parameter |
||
253 | * |
||
254 | * @param bool $preserveKeys Preserve the sub-streams keys, or use integer increment index |
||
255 | * |
||
256 | * @return StreamInterface<R, mixed> |
||
257 | * |
||
258 | * @see TransformerInterface |
||
259 | * @see Streams::wrap() Used to transform each transformed elements to a Stream |
||
260 | */ |
||
261 | public function flatMap(callable $transformer, bool $preserveKeys = false): StreamInterface; |
||
262 | |||
263 | /** |
||
264 | * Skip the $count first elements of the stream. |
||
265 | * Give a count higher than the number of elements of the stream will results of an empty stream. |
||
266 | * |
||
267 | * Example: |
||
268 | * <code> |
||
269 | * $stream = new ArrayStream([1, 2, 3, 4]); |
||
270 | * $stream->skip(2)->toArray(); // [3, 4] |
||
271 | * </code> |
||
272 | * |
||
273 | * @param int $count Number of elements to skip. Must be a positive number. |
||
274 | * |
||
275 | * @return StreamInterface<T, K> |
||
276 | * |
||
277 | * @see StreamInterface::limit() For limit the number of stream's elements |
||
278 | */ |
||
279 | public function skip(int $count): StreamInterface; |
||
280 | |||
281 | /** |
||
282 | * Limit the number of elements of the stream. |
||
283 | * Stop the stream when it reach $count elements. |
||
284 | * |
||
285 | * Example: |
||
286 | * <code> |
||
287 | * $stream = new ArrayStream([1, 2, 3, 4]); |
||
288 | * $stream->limit(2)->toArray(); // [1, 2] |
||
289 | * $stream->limit(2, 1)->toArray(); // [2, 3] |
||
290 | * </code> |
||
291 | * |
||
292 | * @param positive-int|0 $count The maximum number elements |
||
0 ignored issues
–
show
Documentation
Bug
introduced
by
![]() |
|||
293 | * @param positive-int|0 $offset Number of elements to skip at start of the stream |
||
294 | * |
||
295 | * @return StreamInterface<T, K> |
||
296 | * |
||
297 | * @see StreamInterface::skip() For skip firsts elements |
||
298 | */ |
||
299 | public function limit(int $count, int $offset = 0): StreamInterface; |
||
300 | |||
301 | /** |
||
302 | * Iterate over all stream elements. |
||
303 | * This method is a terminal method : the stream must not be used after |
||
304 | * |
||
305 | * <code> |
||
306 | * $stream = new ArrayStream([1, 2, 3]); |
||
307 | * $stream->forEach(function ($element, $key) { |
||
308 | * $element->doSomething(); |
||
309 | * }); |
||
310 | * </code> |
||
311 | * |
||
312 | * @param callable(T, K=):void $consumer |
||
313 | * |
||
314 | * @return void |
||
315 | * |
||
316 | * @see ConsumerInterface |
||
317 | */ |
||
318 | public function forEach(callable $consumer): void; |
||
319 | |||
320 | /** |
||
321 | * Aggregate the stream to an array |
||
322 | * This method is a terminal method : the stream must not be used after |
||
323 | * |
||
324 | * <code> |
||
325 | * $stream = new ArrayStream([ |
||
326 | * 'foo' => 'bar', |
||
327 | * 'value' => 42 |
||
328 | * ]); |
||
329 | * |
||
330 | * $stream->toArray() === ['foo' => 'bar', 'value' => 42]; |
||
331 | * $stream->toArray(false) === ['bar', 42]; |
||
332 | * </code> |
||
333 | * |
||
334 | * @param bool $preserveKeys True to preserve the keys of the stream, or false for reindex with increment integer. |
||
335 | * This parameter must be set to false when stream contains complex keys (not integer or string) |
||
336 | * |
||
337 | * @return T[] |
||
338 | * |
||
339 | * @template PK as bool |
||
340 | * @psalm-param PK $preserveKeys |
||
341 | * @psalm-return (PK is true ? array<K, T> : list<T>) |
||
342 | */ |
||
343 | public function toArray(bool $preserveKeys = true): array; |
||
344 | |||
345 | /** |
||
346 | * Get the first element of the stream |
||
347 | * The element will be wrapped into an Optional for handle empty stream |
||
348 | * |
||
349 | * <code> |
||
350 | * $stream = new ArrayStream([1, 2, 3]); |
||
351 | * $stream->first(); // Optional(1); |
||
352 | * $stream->filter(function () { return false; })->first(); // Empty Optional |
||
353 | * </code> |
||
354 | * |
||
355 | * @return OptionalInterface<T> |
||
356 | */ |
||
357 | public function first(): OptionalInterface; |
||
358 | |||
359 | /** |
||
360 | * Reduce all elements of the stream into a single value |
||
361 | * This method is a terminal method : the stream must not be used after |
||
362 | * |
||
363 | * <code> |
||
364 | * $stream = new ArrayStream([1, 2, 3]); |
||
365 | * $stream->reduce(function ($carry, $item) { return (int) $carry + $item; }); // 6 |
||
366 | * $stream->reduce(Accumulators::sum()); // Same as above, but with a functor |
||
367 | * </code> |
||
368 | * |
||
369 | * @template R |
||
370 | * |
||
371 | * @param callable(R|null,T):R|AccumulatorInterface<T, R> $accumulator The accumulator. |
||
372 | * When a callback is given : takes the reduced value as first parameter and the item to accumulate as second parameter. The function must return the reduced value |
||
373 | * When an AccumulatorInterface is given as only parameter, the initial value will be $accumulator->initial() |
||
374 | * @param R|null $initial The initial value |
||
0 ignored issues
–
show
The type
Bdf\Collection\Stream\R was not found. Maybe you did not declare it correctly or list all dependencies?
The issue could also be caused by a filter entry in the build configuration.
If the path has been excluded in your configuration, e.g. filter:
dependency_paths: ["lib/*"]
For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths ![]() |
|||
375 | * |
||
376 | * @return R The reduced value, or $initial if the stream is empty |
||
377 | * |
||
378 | * @see AccumulatorInterface For functor implementation |
||
379 | */ |
||
380 | public function reduce(callable $accumulator, $initial = null); |
||
381 | |||
382 | /** |
||
383 | * Collect all elements into a single value |
||
384 | * This method is a terminal method : the stream must not be used after |
||
385 | * |
||
386 | * The behavior of this method is very similar to reduce() but with some differences : |
||
387 | * - The collector is not stateless |
||
388 | * - It has a finalisation method whereas reduce perform aggregation on each iterations |
||
389 | * |
||
390 | * @template R |
||
391 | * @param CollectorInterface<T, K, R> $collector The collector |
||
392 | * |
||
393 | * @return R |
||
394 | */ |
||
395 | public function collect(CollectorInterface $collector); |
||
396 | |||
397 | /** |
||
398 | * Check if all elements of the stream match with the predicate |
||
399 | * This method is a terminal method : the stream must not be used after |
||
400 | * |
||
401 | * Note: An empty stream will always return true |
||
402 | * |
||
403 | * /!\ One infinite stream, this method may cause an infinite loop |
||
404 | * |
||
405 | * <code> |
||
406 | * $stream = new ArrayStream([1, 2, 3]); |
||
407 | * |
||
408 | * $stream->allMatch(function ($e) { return $e < 5; }); // true |
||
409 | * $stream->allMatch(function ($e) { return $e % 2 === 0; }); // false |
||
410 | * </code> |
||
411 | * |
||
412 | * @param callable(T, K=):bool $predicate The predicate function. |
||
413 | * Take the element as first parameter and should return a boolean (true if matching) |
||
414 | * May take the key as second parameter (if relevant) |
||
415 | * |
||
416 | * @return bool |
||
417 | * |
||
418 | * @see PredicateInterface |
||
419 | */ |
||
420 | public function matchAll(callable $predicate): bool; |
||
421 | |||
422 | /** |
||
423 | * Check if at least one element of the stream match with the predicate |
||
424 | * This method is a terminal method : the stream must not be used after |
||
425 | * |
||
426 | * /!\ One infinite stream, this method may cause an infinite loop |
||
427 | * |
||
428 | * <code> |
||
429 | * $stream = new ArrayStream([1, 2, 3]); |
||
430 | * |
||
431 | * $stream->allMatch(function ($e) { return $e % 2 === 0; }); // true : 2 % 2 === 0 |
||
432 | * $stream->allMatch(function ($e) { return $e > 5; }); // false |
||
433 | * </code> |
||
434 | * |
||
435 | * @param callable(T, K=):bool $predicate The predicate function. |
||
436 | * Take the element as first parameter and should return a boolean (true if matching) |
||
437 | * May take the key as second parameter (if relevant) |
||
438 | * |
||
439 | * @return bool |
||
440 | * |
||
441 | * @see PredicateInterface |
||
442 | */ |
||
443 | public function matchOne(callable $predicate): bool; |
||
444 | } |
||
445 |