Lines 100.00% 12 / 12
Functions and Methods 100.00% 5 / 5
Classes and Traits 100.00% 1 / 1
Name Lines Functions and Methods CRAP Classes and Traits
RepeatingTimer 100.00% 12 / 12 100.00% 5 / 5 6 100.00% 1 / 1
 __construct 100.00% 1 / 1 100.00% 1 / 1 1
 start 100.00% 2 / 2 100.00% 1 / 1 1
 stop 100.00% 2 / 2 100.00% 1 / 1 1
 isRunning 100.00% 1 / 1 100.00% 1 / 1 1
 scheduleNextTick 100.00% 6 / 6 100.00% 1 / 1 2
1<?php
2
3declare(strict_types=1);
4
5namespace Stewart\Runtime\Time;
6
7use Closure;
8use Stewart\Contracts\Time\Duration;
9use Stewart\Contracts\Time\TimerHandle;
10use Stewart\Contracts\Time\Timers;
11use Throwable;
12
13final class RepeatingTimer
14{
15    private ?TimerHandle $pending = null;
16
17    /**
18     * @param Closure(): void $tick
19     * @param Closure(Throwable): void $onTickFailed
20     */
21    public function __construct(
22        private readonly Timers $timers,
23        private readonly Duration $interval,
24        private readonly Closure $tick,
25        private readonly Closure $onTickFailed,
26    ) {}
27
28    public function start(): void
29    {
30        $this->stop();
31        $this->scheduleNextTick();
32    }
33
34    public function stop(): void
35    {
36        $this->pending?->cancel();
37        $this->pending = null;
38    }
39
40    public function isRunning(): bool
41    {
42        return $this->pending !== null;
43    }
44
45    private function scheduleNextTick(): void
46    {
47        $this->pending = $this->timers->startTimer($this->interval, function (): void {
48            // Re-armed before the tick, so a tick that stops the timer stays stopped.
49            $this->scheduleNextTick();
50
51            try {
52                ($this->tick)();
53            } catch (Throwable $e) {
54                ($this->onTickFailed)($e);
55            }
56        });
57    }
58}