friendship ended with social-app. php is my new best friend
at main 3.2 kB view raw
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}