Issues (50)

src/Stream/StreamInterface.php (2 issues)

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
The doc comment positive-int|0 at position 0 could not be parsed: Unknown type name 'positive-int' at position 0 in positive-int|0.
Loading history...
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. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
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