friendship ended with social-app. php is my new best friend
1<?php
2
3namespace React\EventLoop\Timer;
4
5use React\EventLoop\TimerInterface;
6
7/**
8 * A scheduler implementation that can hold multiple timer instances
9 *
10 * This class should only be used internally, see TimerInterface instead.
11 *
12 * @see TimerInterface
13 * @internal
14 */
15final class Timers
16{
17 private $time;
18 private $timers = array();
19 private $schedule = array();
20 private $sorted = true;
21 private $useHighResolution;
22
23 public function __construct()
24 {
25 // prefer high-resolution timer, available as of PHP 7.3+
26 $this->useHighResolution = \function_exists('hrtime');
27 }
28
29 public function updateTime()
30 {
31 return $this->time = $this->useHighResolution ? \hrtime(true) * 1e-9 : \microtime(true);
32 }
33
34 public function getTime()
35 {
36 return $this->time ?: $this->updateTime();
37 }
38
39 public function add(TimerInterface $timer)
40 {
41 $id = \PHP_VERSION_ID < 70200 ? \spl_object_hash($timer) : \spl_object_id($timer);
42 $this->timers[$id] = $timer;
43 $this->schedule[$id] = $timer->getInterval() + $this->updateTime();
44 $this->sorted = false;
45 }
46
47 public function contains(TimerInterface $timer)
48 {
49 $id = \PHP_VERSION_ID < 70200 ? \spl_object_hash($timer) : \spl_object_id($timer);
50 return isset($this->timers[$id]);
51 }
52
53 public function cancel(TimerInterface $timer)
54 {
55 $id = \PHP_VERSION_ID < 70200 ? \spl_object_hash($timer) : \spl_object_id($timer);
56 unset($this->timers[$id], $this->schedule[$id]);
57 }
58
59 public function getFirst()
60 {
61 // ensure timers are sorted to simply accessing next (first) one
62 if (!$this->sorted) {
63 $this->sorted = true;
64 \asort($this->schedule);
65 }
66
67 return \reset($this->schedule);
68 }
69
70 public function isEmpty()
71 {
72 return \count($this->timers) === 0;
73 }
74
75 public function tick()
76 {
77 // hot path: skip timers if nothing is scheduled
78 if (!$this->schedule) {
79 return;
80 }
81
82 // ensure timers are sorted so we can execute in order
83 if (!$this->sorted) {
84 $this->sorted = true;
85 \asort($this->schedule);
86 }
87
88 $time = $this->updateTime();
89
90 foreach ($this->schedule as $id => $scheduled) {
91 // schedule is ordered, so loop until first timer that is not scheduled for execution now
92 if ($scheduled >= $time) {
93 break;
94 }
95
96 // skip any timers that are removed while we process the current schedule
97 if (!isset($this->schedule[$id]) || $this->schedule[$id] !== $scheduled) {
98 continue;
99 }
100
101 $timer = $this->timers[$id];
102 \call_user_func($timer->getCallback(), $timer);
103
104 // re-schedule if this is a periodic timer and it has not been cancelled explicitly already
105 if ($timer->isPeriodic() && isset($this->timers[$id])) {
106 $this->schedule[$id] = $timer->getInterval() + $time;
107 $this->sorted = false;
108 } else {
109 unset($this->timers[$id], $this->schedule[$id]);
110 }
111 }
112 }
113}