1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* League.Period (https://period.thephpleague.com) |
5
|
|
|
* |
6
|
|
|
* (c) Ignace Nyamagana Butera <[email protected]> |
7
|
|
|
* |
8
|
|
|
* For the full copyright and license information, please view the LICENSE |
9
|
|
|
* file that was distributed with this source code. |
10
|
|
|
*/ |
11
|
|
|
|
12
|
|
|
declare(strict_types=1); |
13
|
|
|
|
14
|
|
|
namespace League\Period; |
15
|
|
|
|
16
|
|
|
use ArrayAccess; |
17
|
|
|
use Countable; |
18
|
|
|
use Iterator; |
19
|
|
|
use IteratorAggregate; |
20
|
|
|
use JsonSerializable; |
21
|
|
|
use function array_filter; |
22
|
|
|
use function array_merge; |
23
|
|
|
use function array_splice; |
24
|
|
|
use function array_unshift; |
25
|
|
|
use function array_values; |
26
|
|
|
use function count; |
27
|
|
|
use function reset; |
28
|
|
|
use function sort; |
29
|
|
|
use function sprintf; |
30
|
|
|
use function uasort; |
31
|
|
|
use function usort; |
32
|
|
|
use const ARRAY_FILTER_USE_BOTH; |
33
|
|
|
|
34
|
|
|
/** |
35
|
|
|
* A class to manipulate interval collection. |
36
|
|
|
* |
37
|
|
|
* @package League.period |
38
|
|
|
* @author Ignace Nyamagana Butera <[email protected]> |
39
|
|
|
* @since 4.1.0 |
40
|
|
|
*/ |
41
|
|
|
final class Sequence implements ArrayAccess, Countable, IteratorAggregate, JsonSerializable |
42
|
|
|
{ |
43
|
|
|
/** |
44
|
|
|
* @var Period[] |
45
|
|
|
*/ |
46
|
|
|
private $intervals = []; |
47
|
|
|
|
48
|
|
|
/** |
49
|
|
|
* new instance. |
50
|
|
|
* |
51
|
|
|
* @param Period ...$intervals |
52
|
|
|
*/ |
53
|
123 |
|
public function __construct(Period ...$intervals) |
54
|
|
|
{ |
55
|
123 |
|
$this->intervals = $intervals; |
56
|
123 |
|
} |
57
|
|
|
|
58
|
|
|
/** |
59
|
|
|
* Returns the sequence boundaries as a Period instance. |
60
|
|
|
* |
61
|
|
|
* If the sequence contains no interval null is returned. |
62
|
|
|
* |
63
|
|
|
* @return ?Period |
64
|
|
|
*/ |
65
|
15 |
|
public function boundaries(): ?Period |
66
|
|
|
{ |
67
|
15 |
|
$period = reset($this->intervals); |
68
|
15 |
|
if (false === $period) { |
69
|
6 |
|
return null; |
70
|
|
|
} |
71
|
|
|
|
72
|
15 |
|
return $period->merge(...$this->intervals); |
73
|
|
|
} |
74
|
|
|
|
75
|
|
|
/** |
76
|
|
|
* Returns the gaps inside the instance. |
77
|
|
|
*/ |
78
|
9 |
|
public function gaps(): self |
79
|
|
|
{ |
80
|
9 |
|
$sequence = new self(); |
81
|
9 |
|
$interval = null; |
82
|
9 |
|
foreach ($this->sorted([$this, 'sortByStartDate']) as $period) { |
83
|
9 |
|
if (null === $interval) { |
84
|
9 |
|
$interval = $period; |
85
|
9 |
|
continue; |
86
|
|
|
} |
87
|
|
|
|
88
|
9 |
|
if (!$interval->overlaps($period) && !$interval->abuts($period)) { |
89
|
6 |
|
$sequence->push($interval->gap($period)); |
90
|
|
|
} |
91
|
|
|
|
92
|
9 |
|
if (!$interval->contains($period)) { |
93
|
7 |
|
$interval = $period; |
94
|
|
|
} |
95
|
|
|
} |
96
|
|
|
|
97
|
9 |
|
return $sequence; |
98
|
|
|
} |
99
|
|
|
|
100
|
|
|
/** |
101
|
|
|
* Sorts two Interval instance using their start datepoint. |
102
|
|
|
*/ |
103
|
15 |
|
private function sortByStartDate(Period $interval1, Period $interval2): int |
104
|
|
|
{ |
105
|
15 |
|
return $interval1->getStartDate() <=> $interval2->getStartDate(); |
106
|
|
|
} |
107
|
|
|
|
108
|
|
|
/** |
109
|
|
|
* Returns the intersections inside the instance. |
110
|
|
|
*/ |
111
|
9 |
|
public function intersections(): self |
112
|
|
|
{ |
113
|
9 |
|
$sequence = new self(); |
114
|
9 |
|
$current = null; |
115
|
9 |
|
$isPreviouslyContained = false; |
116
|
9 |
|
foreach ($this->sorted([$this, 'sortByStartDate']) as $period) { |
117
|
9 |
|
if (null === $current) { |
118
|
9 |
|
$current = $period; |
119
|
9 |
|
continue; |
120
|
|
|
} |
121
|
|
|
|
122
|
9 |
|
$isContained = $current->contains($period); |
123
|
9 |
|
if ($isContained && $isPreviouslyContained) { |
124
|
3 |
|
continue; |
125
|
|
|
} |
126
|
|
|
|
127
|
9 |
|
if ($current->overlaps($period)) { |
128
|
6 |
|
$sequence->push($current->intersect($period)); |
129
|
|
|
} |
130
|
|
|
|
131
|
9 |
|
$isPreviouslyContained = $isContained; |
132
|
9 |
|
if (!$isContained) { |
133
|
7 |
|
$current = $period; |
134
|
|
|
} |
135
|
|
|
} |
136
|
|
|
|
137
|
9 |
|
return $sequence; |
138
|
|
|
} |
139
|
|
|
|
140
|
|
|
/** |
141
|
|
|
* Returns the unions inside the instance. |
142
|
|
|
*/ |
143
|
6 |
|
public function unions(): self |
144
|
|
|
{ |
145
|
|
|
$sequence = $this |
146
|
6 |
|
->sorted([$this, 'sortByStartDate']) |
147
|
6 |
|
->reduce([$this, 'calculateUnion'], new self()) |
148
|
|
|
; |
149
|
|
|
|
150
|
6 |
|
if ($sequence->intervals === $this->intervals) { |
151
|
3 |
|
return $this; |
152
|
|
|
} |
153
|
|
|
|
154
|
3 |
|
return $sequence; |
155
|
|
|
} |
156
|
|
|
|
157
|
|
|
/** |
158
|
|
|
* Iteratively calculate the union sequence. |
159
|
|
|
*/ |
160
|
6 |
|
private function calculateUnion(Sequence $sequence, Period $period): Sequence |
161
|
|
|
{ |
162
|
6 |
|
if ($sequence->isEmpty()) { |
163
|
6 |
|
$sequence->push($period); |
164
|
|
|
|
165
|
6 |
|
return $sequence; |
166
|
|
|
} |
167
|
|
|
|
168
|
3 |
|
$index = $sequence->count() - 1; |
169
|
3 |
|
$interval = $sequence->get($index); |
170
|
3 |
|
if ($interval->overlaps($period) || $interval->abuts($period)) { |
171
|
3 |
|
$sequence->set($index, $interval->merge($period)); |
172
|
|
|
|
173
|
3 |
|
return $sequence; |
174
|
|
|
} |
175
|
|
|
|
176
|
3 |
|
$sequence->push($period); |
177
|
|
|
|
178
|
3 |
|
return $sequence; |
179
|
|
|
} |
180
|
|
|
|
181
|
|
|
/** |
182
|
|
|
* DEPRECATION WARNING! This method will be removed in the next major point release. |
183
|
|
|
* |
184
|
|
|
* @deprecated since version 4.9.0 |
185
|
|
|
* @see ::subtract |
186
|
|
|
*/ |
187
|
15 |
|
public function substract(Sequence $sequence): self |
188
|
|
|
{ |
189
|
15 |
|
return $this->subtract($sequence); |
190
|
|
|
} |
191
|
|
|
|
192
|
|
|
/** |
193
|
|
|
* Subtract a Sequence from the current instance. |
194
|
|
|
* |
195
|
|
|
* This method MUST retain the state of the current instance, and return |
196
|
|
|
* an instance that contains substracted intervals. |
197
|
|
|
*/ |
198
|
15 |
|
public function subtract(Sequence $sequence): self |
199
|
|
|
{ |
200
|
15 |
|
if ($this->isEmpty()) { |
201
|
3 |
|
return $this; |
202
|
|
|
} |
203
|
|
|
|
204
|
15 |
|
$new = $sequence->reduce([$this, 'subtractOne'], $this); |
205
|
15 |
|
if ($new->intervals === $this->intervals) { |
206
|
6 |
|
return $this; |
207
|
|
|
} |
208
|
|
|
|
209
|
9 |
|
return $new; |
210
|
|
|
} |
211
|
|
|
|
212
|
|
|
/** |
213
|
|
|
* Substract an Interval from a Sequence. |
214
|
|
|
*/ |
215
|
12 |
|
private function subtractOne(Sequence $sequence, Period $interval): self |
216
|
|
|
{ |
217
|
12 |
|
if ($sequence->isEmpty()) { |
218
|
3 |
|
return $sequence; |
219
|
|
|
} |
220
|
|
|
|
221
|
|
|
$reducer = function (Sequence $sequence, Period $period) use ($interval) { |
222
|
12 |
|
$substract = $period->substract($interval); |
|
|
|
|
223
|
12 |
|
if (!$substract->isEmpty()) { |
224
|
6 |
|
$sequence->push(...$substract); |
225
|
|
|
} |
226
|
|
|
|
227
|
12 |
|
return $sequence; |
228
|
12 |
|
}; |
229
|
|
|
|
230
|
12 |
|
return $sequence->reduce($reducer, new self()); |
231
|
|
|
} |
232
|
|
|
|
233
|
|
|
/** |
234
|
|
|
* Returns the sequence boundaries as a Period instance. |
235
|
|
|
* |
236
|
|
|
* DEPRECATION WARNING! This method will be removed in the next major point release |
237
|
|
|
* |
238
|
|
|
* @deprecated deprecated since version 4.4.0 |
239
|
|
|
* @see ::boundaries |
240
|
|
|
* |
241
|
|
|
* If the sequence contains no interval null is returned. |
242
|
|
|
* |
243
|
|
|
* @return ?Period |
244
|
|
|
*/ |
245
|
12 |
|
public function getBoundaries(): ?Period |
246
|
|
|
{ |
247
|
12 |
|
return $this->boundaries(); |
248
|
|
|
} |
249
|
|
|
|
250
|
|
|
/** |
251
|
|
|
* Returns the intersections inside the instance. |
252
|
|
|
* |
253
|
|
|
* DEPRECATION WARNING! This method will be removed in the next major point release |
254
|
|
|
* |
255
|
|
|
* @deprecated deprecated since version 4.4.0 |
256
|
|
|
* @see ::intersections |
257
|
|
|
*/ |
258
|
9 |
|
public function getIntersections(): self |
259
|
|
|
{ |
260
|
9 |
|
return $this->intersections(); |
261
|
|
|
} |
262
|
|
|
|
263
|
|
|
/** |
264
|
|
|
* Returns the gaps inside the instance. |
265
|
|
|
* |
266
|
|
|
* DEPRECATION WARNING! This method will be removed in the next major point release |
267
|
|
|
* |
268
|
|
|
* @deprecated deprecated since version 4.4.0 |
269
|
|
|
* @see ::gaps |
270
|
|
|
*/ |
271
|
9 |
|
public function getGaps(): self |
272
|
|
|
{ |
273
|
9 |
|
return $this->gaps(); |
274
|
|
|
} |
275
|
|
|
|
276
|
|
|
/** |
277
|
|
|
* Returns the sum of all instances durations as expressed in seconds. |
278
|
|
|
*/ |
279
|
3 |
|
public function getTotalTimestampInterval(): float |
280
|
|
|
{ |
281
|
3 |
|
$retval = 0; |
282
|
3 |
|
foreach ($this->intervals as $interval) { |
283
|
3 |
|
$retval += $interval->getTimestampInterval(); |
284
|
|
|
} |
285
|
|
|
|
286
|
3 |
|
return $retval; |
287
|
|
|
} |
288
|
|
|
|
289
|
|
|
/** |
290
|
|
|
* Tells whether some intervals in the current instance satisfies the predicate. |
291
|
|
|
*/ |
292
|
3 |
|
public function some(callable $predicate): bool |
293
|
|
|
{ |
294
|
3 |
|
foreach ($this->intervals as $offset => $interval) { |
295
|
3 |
|
if (true === $predicate($interval, $offset)) { |
296
|
3 |
|
return true; |
297
|
|
|
} |
298
|
|
|
} |
299
|
|
|
|
300
|
3 |
|
return false; |
301
|
|
|
} |
302
|
|
|
|
303
|
|
|
/** |
304
|
|
|
* Tells whether all intervals in the current instance satisfies the predicate. |
305
|
|
|
*/ |
306
|
3 |
|
public function every(callable $predicate): bool |
307
|
|
|
{ |
308
|
3 |
|
foreach ($this->intervals as $offset => $interval) { |
309
|
3 |
|
if (true !== $predicate($interval, $offset)) { |
310
|
3 |
|
return false; |
311
|
|
|
} |
312
|
|
|
} |
313
|
|
|
|
314
|
3 |
|
return [] !== $this->intervals; |
315
|
|
|
} |
316
|
|
|
|
317
|
|
|
/** |
318
|
|
|
* Returns the array representation of the sequence. |
319
|
|
|
* |
320
|
|
|
* @return Period[] |
321
|
|
|
*/ |
322
|
6 |
|
public function toArray(): array |
323
|
|
|
{ |
324
|
6 |
|
return $this->intervals; |
325
|
|
|
} |
326
|
|
|
|
327
|
|
|
/** |
328
|
|
|
* {@inheritDoc} |
329
|
|
|
*/ |
330
|
3 |
|
public function jsonSerialize(): array |
331
|
|
|
{ |
332
|
3 |
|
return $this->intervals; |
333
|
|
|
} |
334
|
|
|
|
335
|
|
|
/** |
336
|
|
|
* {@inheritDoc} |
337
|
|
|
*/ |
338
|
24 |
|
public function getIterator(): Iterator |
339
|
|
|
{ |
340
|
24 |
|
foreach ($this->intervals as $offset => $interval) { |
341
|
24 |
|
yield $offset => $interval; |
342
|
|
|
} |
343
|
24 |
|
} |
344
|
|
|
|
345
|
|
|
/** |
346
|
|
|
* {@inheritDoc} |
347
|
|
|
*/ |
348
|
54 |
|
public function count(): int |
349
|
|
|
{ |
350
|
54 |
|
return count($this->intervals); |
351
|
|
|
} |
352
|
|
|
|
353
|
|
|
/** |
354
|
|
|
* @inheritDoc |
355
|
|
|
* |
356
|
|
|
* @param mixed $offset the index of the Period instance to validate. |
357
|
|
|
*/ |
358
|
3 |
|
public function offsetExists($offset): bool |
359
|
|
|
{ |
360
|
3 |
|
return null !== $this->filterOffset($offset); |
361
|
|
|
} |
362
|
|
|
|
363
|
|
|
/** |
364
|
|
|
* Filter and format the Sequence offset. |
365
|
|
|
* |
366
|
|
|
* This methods allows the support of negative offset |
367
|
|
|
* |
368
|
|
|
* if no offset is found null is returned otherwise the return type is int |
369
|
|
|
*/ |
370
|
60 |
|
private function filterOffset(int $offset): ?int |
371
|
|
|
{ |
372
|
60 |
|
if ([] === $this->intervals) { |
373
|
9 |
|
return null; |
374
|
|
|
} |
375
|
|
|
|
376
|
54 |
|
$max = count($this->intervals); |
377
|
54 |
|
if (0 > $max + $offset) { |
378
|
3 |
|
return null; |
379
|
|
|
} |
380
|
|
|
|
381
|
51 |
|
if (0 > $max - $offset - 1) { |
382
|
9 |
|
return null; |
383
|
|
|
} |
384
|
|
|
|
385
|
48 |
|
if (0 > $offset) { |
386
|
6 |
|
return $max + $offset; |
387
|
|
|
} |
388
|
|
|
|
389
|
48 |
|
return $offset; |
390
|
|
|
} |
391
|
|
|
|
392
|
|
|
/** |
393
|
|
|
* @inheritDoc |
394
|
|
|
* @see ::get |
395
|
|
|
* |
396
|
|
|
* @param mixed $offset the index of the Period instance to retrieve. |
397
|
|
|
* |
398
|
|
|
* @throws InvalidIndex If the offset is illegal for the current sequence |
399
|
|
|
*/ |
400
|
15 |
|
public function offsetGet($offset): Period |
401
|
|
|
{ |
402
|
15 |
|
return $this->get($offset); |
403
|
|
|
} |
404
|
|
|
|
405
|
|
|
/** |
406
|
|
|
* @inheritDoc |
407
|
|
|
* @see ::remove |
408
|
|
|
* |
409
|
|
|
* @param mixed $offset the index of the Period instance to remove. |
410
|
|
|
* |
411
|
|
|
* @throws InvalidIndex If the offset is illegal for the current sequence |
412
|
|
|
*/ |
413
|
6 |
|
public function offsetUnset($offset): void |
414
|
|
|
{ |
415
|
6 |
|
$this->remove($offset); |
416
|
3 |
|
} |
417
|
|
|
|
418
|
|
|
/** |
419
|
|
|
* @inheritDoc |
420
|
|
|
* @see ::set |
421
|
|
|
* @see ::push |
422
|
|
|
* |
423
|
|
|
* @param mixed $offset the index of the Period to add or update. |
424
|
|
|
* @param mixed $interval the Period instance to add. |
425
|
|
|
* |
426
|
|
|
* @throws InvalidIndex If the offset is illegal for the current sequence |
427
|
|
|
*/ |
428
|
9 |
|
public function offsetSet($offset, $interval): void |
429
|
|
|
{ |
430
|
9 |
|
if (null !== $offset) { |
431
|
9 |
|
$this->set($offset, $interval); |
432
|
3 |
|
return; |
433
|
|
|
} |
434
|
|
|
|
435
|
3 |
|
$this->push($interval); |
436
|
3 |
|
} |
437
|
|
|
|
438
|
|
|
/** |
439
|
|
|
* Tells whether the sequence is empty. |
440
|
|
|
*/ |
441
|
30 |
|
public function isEmpty(): bool |
442
|
|
|
{ |
443
|
30 |
|
return [] === $this->intervals; |
444
|
|
|
} |
445
|
|
|
|
446
|
|
|
/** |
447
|
|
|
* Tells whether the given interval is present in the sequence. |
448
|
|
|
* |
449
|
|
|
* @param Period ...$intervals |
450
|
|
|
*/ |
451
|
6 |
|
public function contains(Period ...$intervals): bool |
452
|
|
|
{ |
453
|
6 |
|
foreach ($intervals as $period) { |
454
|
6 |
|
if (false === $this->indexOf($period)) { |
455
|
4 |
|
return false; |
456
|
|
|
} |
457
|
|
|
} |
458
|
|
|
|
459
|
6 |
|
return [] !== $intervals; |
460
|
|
|
} |
461
|
|
|
|
462
|
|
|
/** |
463
|
|
|
* Attempts to find the first offset attached to the submitted interval. |
464
|
|
|
* |
465
|
|
|
* If no offset is found the method returns boolean false. |
466
|
|
|
* |
467
|
|
|
* @return int|bool |
468
|
|
|
*/ |
469
|
6 |
|
public function indexOf(Period $interval) |
470
|
|
|
{ |
471
|
6 |
|
foreach ($this->intervals as $offset => $period) { |
472
|
6 |
|
if ($period->equals($interval)) { |
473
|
6 |
|
return $offset; |
474
|
|
|
} |
475
|
|
|
} |
476
|
|
|
|
477
|
6 |
|
return false; |
478
|
|
|
} |
479
|
|
|
|
480
|
|
|
/** |
481
|
|
|
* Returns the interval specified at a given offset. |
482
|
|
|
* |
483
|
|
|
* @throws InvalidIndex If the offset is illegal for the current sequence |
484
|
|
|
*/ |
485
|
51 |
|
public function get(int $offset): Period |
486
|
|
|
{ |
487
|
51 |
|
$index = $this->filterOffset($offset); |
488
|
51 |
|
if (null === $index) { |
489
|
6 |
|
throw new InvalidIndex(sprintf('%s is an invalid offset in the current sequence', $offset)); |
490
|
|
|
} |
491
|
|
|
|
492
|
45 |
|
return $this->intervals[$index]; |
493
|
|
|
} |
494
|
|
|
|
495
|
|
|
/** |
496
|
|
|
* Sort the current instance according to the given comparison callable |
497
|
|
|
* and maintain index association. |
498
|
|
|
* |
499
|
|
|
* Returns true on success or false on failure |
500
|
|
|
*/ |
501
|
6 |
|
public function sort(callable $compare): bool |
502
|
|
|
{ |
503
|
6 |
|
return uasort($this->intervals, $compare); |
504
|
|
|
} |
505
|
|
|
|
506
|
|
|
/** |
507
|
|
|
* Adds new intervals at the front of the sequence. |
508
|
|
|
* |
509
|
|
|
* The sequence is re-indexed after addition |
510
|
|
|
* |
511
|
|
|
* @param Period ...$intervals |
512
|
|
|
*/ |
513
|
6 |
|
public function unshift(Period ...$intervals): void |
514
|
|
|
{ |
515
|
6 |
|
$this->intervals = array_merge($intervals, $this->intervals); |
516
|
6 |
|
} |
517
|
|
|
|
518
|
|
|
/** |
519
|
|
|
* Adds new intervals at the end of the sequence. |
520
|
|
|
* |
521
|
|
|
* @param Period ...$intervals |
522
|
|
|
*/ |
523
|
33 |
|
public function push(Period ...$intervals): void |
524
|
|
|
{ |
525
|
33 |
|
$this->intervals = array_merge($this->intervals, $intervals); |
526
|
33 |
|
} |
527
|
|
|
|
528
|
|
|
/** |
529
|
|
|
* Inserts new intervals at the specified offset of the sequence. |
530
|
|
|
* |
531
|
|
|
* The sequence is re-indexed after addition |
532
|
|
|
* |
533
|
|
|
* @param Period ...$intervals |
534
|
|
|
* |
535
|
|
|
* @throws InvalidIndex If the offset is illegal for the current sequence. |
536
|
|
|
*/ |
537
|
3 |
|
public function insert(int $offset, Period $interval, Period ...$intervals): void |
538
|
|
|
{ |
539
|
3 |
|
if (0 === $offset) { |
540
|
3 |
|
$this->unshift($interval, ...$intervals); |
541
|
|
|
|
542
|
3 |
|
return; |
543
|
|
|
} |
544
|
|
|
|
545
|
3 |
|
if (count($this->intervals) === $offset) { |
546
|
3 |
|
$this->push($interval, ...$intervals); |
547
|
|
|
|
548
|
3 |
|
return; |
549
|
|
|
} |
550
|
|
|
|
551
|
3 |
|
$index = $this->filterOffset($offset); |
552
|
3 |
|
if (null === $index) { |
553
|
3 |
|
throw new InvalidIndex(sprintf('%s is an invalid offset in the current sequence', $offset)); |
554
|
|
|
} |
555
|
|
|
|
556
|
3 |
|
array_unshift($intervals, $interval); |
557
|
3 |
|
array_splice($this->intervals, $index, 0, $intervals); |
558
|
3 |
|
} |
559
|
|
|
|
560
|
|
|
/** |
561
|
|
|
* Updates the interval at the specify offset. |
562
|
|
|
* |
563
|
|
|
* @throws InvalidIndex If the offset is illegal for the current sequence. |
564
|
|
|
*/ |
565
|
12 |
|
public function set(int $offset, Period $interval): void |
566
|
|
|
{ |
567
|
12 |
|
$index = $this->filterOffset($offset); |
568
|
12 |
|
if (null === $index) { |
569
|
6 |
|
throw new InvalidIndex(sprintf('%s is an invalid offset in the current sequence', $offset)); |
570
|
|
|
} |
571
|
|
|
|
572
|
9 |
|
$this->intervals[$index] = $interval; |
573
|
9 |
|
} |
574
|
|
|
|
575
|
|
|
/** |
576
|
|
|
* Removes an interval from the sequence at the given offset and returns it. |
577
|
|
|
* |
578
|
|
|
* The sequence is re-indexed after removal |
579
|
|
|
* |
580
|
|
|
* @throws InvalidIndex If the offset is illegal for the current sequence. |
581
|
|
|
*/ |
582
|
9 |
|
public function remove(int $offset): Period |
583
|
|
|
{ |
584
|
9 |
|
$index = $this->filterOffset($offset); |
585
|
9 |
|
if (null === $index) { |
586
|
6 |
|
throw new InvalidIndex(sprintf('%s is an invalid offset in the current sequence', $offset)); |
587
|
|
|
} |
588
|
|
|
|
589
|
6 |
|
$interval = $this->intervals[$index]; |
590
|
6 |
|
unset($this->intervals[$index]); |
591
|
6 |
|
$this->intervals = array_values($this->intervals); |
592
|
|
|
|
593
|
6 |
|
return $interval; |
594
|
|
|
} |
595
|
|
|
|
596
|
|
|
/** |
597
|
|
|
* Filters the sequence according to the given predicate. |
598
|
|
|
* |
599
|
|
|
* This method MUST retain the state of the current instance, and return |
600
|
|
|
* an instance that contains the interval which validate the predicate. |
601
|
|
|
*/ |
602
|
6 |
|
public function filter(callable $predicate): self |
603
|
|
|
{ |
604
|
6 |
|
$intervals = array_filter($this->intervals, $predicate, ARRAY_FILTER_USE_BOTH); |
605
|
6 |
|
if ($intervals === $this->intervals) { |
606
|
3 |
|
return $this; |
607
|
|
|
} |
608
|
|
|
|
609
|
3 |
|
return new self(...$intervals); |
610
|
|
|
} |
611
|
|
|
|
612
|
|
|
/** |
613
|
|
|
* Removes all intervals from the sequence. |
614
|
|
|
*/ |
615
|
3 |
|
public function clear(): void |
616
|
|
|
{ |
617
|
3 |
|
$this->intervals = []; |
618
|
3 |
|
} |
619
|
|
|
|
620
|
|
|
/** |
621
|
|
|
* Returns an instance sorted according to the given comparison callable |
622
|
|
|
* but does not maintain index association. |
623
|
|
|
* |
624
|
|
|
* This method MUST retain the state of the current instance, and return |
625
|
|
|
* an instance that contains the sorted intervals. The key are re-indexed |
626
|
|
|
*/ |
627
|
24 |
|
public function sorted(callable $compare): self |
628
|
|
|
{ |
629
|
24 |
|
$intervals = $this->intervals; |
630
|
24 |
|
usort($intervals, $compare); |
631
|
24 |
|
if ($intervals === $this->intervals) { |
632
|
12 |
|
return $this; |
633
|
|
|
} |
634
|
|
|
|
635
|
15 |
|
return new self(...$intervals); |
636
|
|
|
} |
637
|
|
|
|
638
|
|
|
/** |
639
|
|
|
* Returns an instance where the given function is applied to each element in |
640
|
|
|
* the collection. The callable MUST return a Period object and takes a Period |
641
|
|
|
* and its associated key as argument. |
642
|
|
|
* |
643
|
|
|
* This method MUST retain the state of the current instance, and return |
644
|
|
|
* an instance that contains the returned intervals. |
645
|
|
|
*/ |
646
|
9 |
|
public function map(callable $func): self |
647
|
|
|
{ |
648
|
9 |
|
$intervals = []; |
649
|
9 |
|
foreach ($this->intervals as $offset => $interval) { |
650
|
9 |
|
$intervals[$offset] = $func($interval, $offset); |
651
|
|
|
} |
652
|
|
|
|
653
|
9 |
|
if ($intervals === $this->intervals) { |
654
|
3 |
|
return $this; |
655
|
|
|
} |
656
|
|
|
|
657
|
6 |
|
$mapped = new self(); |
658
|
6 |
|
$mapped->intervals = $intervals; |
659
|
|
|
|
660
|
6 |
|
return $mapped; |
661
|
|
|
} |
662
|
|
|
|
663
|
|
|
/** |
664
|
|
|
* Iteratively reduces the sequence to a single value using a callback. |
665
|
|
|
* |
666
|
|
|
* @param callable $func Accepts the carry, the current value and the current offset, and |
667
|
|
|
* returns an updated carry value. |
668
|
|
|
* |
669
|
|
|
* @param mixed|null $carry Optional initial carry value. |
670
|
|
|
* |
671
|
|
|
* @return mixed The carry value of the final iteration, or the initial |
672
|
|
|
* value if the sequence was empty. |
673
|
|
|
*/ |
674
|
21 |
|
public function reduce(callable $func, $carry = null) |
675
|
|
|
{ |
676
|
21 |
|
foreach ($this->intervals as $offset => $interval) { |
677
|
18 |
|
$carry = $func($carry, $interval, $offset); |
678
|
|
|
} |
679
|
|
|
|
680
|
21 |
|
return $carry; |
681
|
|
|
} |
682
|
|
|
} |
683
|
|
|
|
This function has been deprecated. The supplier of the function has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.