1 | <?php |
||
16 | abstract class AbstractWorker implements WorkerInterface |
||
17 | { |
||
18 | const DEFAULT_ITERATION_INTERVAL = 'PT60S'; |
||
19 | |||
20 | /** @var DateTime */ |
||
21 | private $from; |
||
22 | |||
23 | /** @var DateInterval */ |
||
24 | private $interval; |
||
25 | |||
26 | /** @var bool */ |
||
27 | private $shutdown = false; |
||
28 | |||
29 | /** @var integer */ |
||
30 | private $iteration = 0; |
||
31 | |||
32 | /** @var integer */ |
||
33 | private $maxIterations = 1000; |
||
34 | |||
35 | /** |
||
36 | * @param integer $startTime |
||
37 | * @param string $interval |
||
38 | * @return mixed|string |
||
39 | * @throws SchedulerException |
||
40 | */ |
||
41 | 2 | public function run($startTime, $interval) |
|
42 | { |
||
43 | 2 | $this->init($startTime, $interval); |
|
44 | 1 | $jobRunner = $this->getJobRunner(); |
|
45 | |||
46 | 1 | $from = clone($this->from); |
|
47 | 1 | $oneSecondInterval = new \DateInterval('PT1S'); |
|
48 | |||
49 | 1 | while ($this->isRunning()) { |
|
50 | 1 | $to = DateTime::createFromFormat('U', time(), new \DateTimeZone('UTC')); |
|
51 | 1 | $jobRunner->run($this->getScheduler(), $from, $to, true); |
|
52 | 1 | $from = clone($to); |
|
53 | 1 | $from->add($oneSecondInterval); |
|
54 | 1 | sleep($this->getSeconds($this->interval)); |
|
55 | 1 | $this->iteration++; |
|
56 | } |
||
57 | |||
58 | 1 | return 'Shut down scheduler worker'; |
|
59 | } |
||
60 | |||
61 | /** |
||
62 | * @return JobRunnerInterface |
||
63 | */ |
||
64 | abstract protected function getJobRunner(); |
||
65 | |||
66 | /** |
||
67 | * @return SchedulerInterface |
||
68 | */ |
||
69 | abstract protected function getScheduler(); |
||
70 | |||
71 | /** |
||
72 | * @return bool |
||
73 | */ |
||
74 | 1 | public function isRunning() |
|
75 | { |
||
76 | 1 | pcntl_signal_dispatch(); |
|
77 | |||
78 | 1 | if ($this->iteration >= $this->getMaxIterations()) { |
|
79 | 1 | $this->shutdown(); |
|
80 | } |
||
81 | |||
82 | 1 | if ($this->shutdown) { |
|
83 | 1 | return false; |
|
84 | } |
||
85 | |||
86 | 1 | return true; |
|
87 | } |
||
88 | |||
89 | /** |
||
90 | * Set marker to shutdown after finishing current iteration |
||
91 | */ |
||
92 | 1 | public function shutdown() |
|
93 | { |
||
94 | 1 | $this->shutdown = true; |
|
95 | 1 | } |
|
96 | |||
97 | /** |
||
98 | * @param $iterations |
||
99 | * @throws SchedulerException |
||
100 | * @return mixed|void |
||
101 | */ |
||
102 | 2 | public function setMaxIterations($iterations) |
|
103 | { |
||
104 | 2 | if (!is_integer($iterations)) { |
|
105 | 1 | throw new SchedulerException('$iterations parameter must be integer'); |
|
106 | } |
||
107 | 1 | $this->maxIterations = $iterations; |
|
108 | 1 | } |
|
109 | |||
110 | /** |
||
111 | * Get amount of seconds in date interval |
||
112 | * |
||
113 | * @param DateInterval $interval |
||
114 | * @return int |
||
115 | * @throws \Exception |
||
116 | */ |
||
117 | 1 | private function getSeconds(DateInterval $interval) |
|
118 | { |
||
119 | 1 | $date = new \DateTimeImmutable('now', new \DateTimeZone('UTC')); |
|
120 | 1 | $date2 = $date->add($interval); |
|
121 | 1 | return $date2->getTimestamp() - $date->getTimestamp(); |
|
122 | } |
||
123 | |||
124 | /** |
||
125 | * @param integer $startTime |
||
126 | * @param string $interval |
||
127 | * @throws |
||
128 | */ |
||
129 | 2 | private function init($startTime, $interval) |
|
130 | { |
||
131 | 2 | if (!is_numeric($startTime)) { |
|
132 | 1 | throw new SchedulerException('Start time parameter must be numeric'); |
|
133 | } |
||
134 | 1 | $this->from = new DateTime('@' . $startTime, new \DateTimeZone('UTC')); |
|
135 | |||
136 | 1 | $this->interval = new \DateInterval(self::DEFAULT_ITERATION_INTERVAL); |
|
137 | 1 | if ($interval !== null) { |
|
138 | 1 | $this->interval = new \DateInterval($interval); |
|
139 | } |
||
140 | |||
141 | 1 | $this->registerSigHandlers(); |
|
142 | 1 | } |
|
143 | |||
144 | /** |
||
145 | * Register signal handlers that a worker should respond to. |
||
146 | * |
||
147 | * TERM/INT/QUIT: Shutdown after the current job is finished then exit. |
||
148 | */ |
||
149 | 1 | private function registerSigHandlers() |
|
150 | { |
||
151 | 1 | if (!function_exists('pcntl_signal')) { |
|
152 | //worker ran without pcntl |
||
153 | function pcntl_signal() { |
||
156 | define('SIGTERM', 15); |
||
157 | define('SIGINT', 2); |
||
158 | define('SIGQUIT', 3); |
||
159 | } |
||
160 | 1 | if (!function_exists('pcntl_signal_dispatch')) { |
|
161 | function pcntl_signal_dispatch() { |
||
164 | } |
||
165 | |||
166 | declare(ticks = 1); |
||
167 | |||
168 | 1 | pcntl_signal(SIGTERM, [$this, 'shutdown']); |
|
169 | 1 | pcntl_signal(SIGINT, [$this, 'shutdown']); |
|
170 | 1 | pcntl_signal(SIGQUIT, [$this, 'shutdown']); |
|
172 | |||
173 | /** |
||
174 | * @return integer |
||
175 | */ |
||
176 | 1 | public function getMaxIterations() |
|
180 | } |
This check looks for type mismatches where the missing type is
false
. This is usually indicative of an error condtion.Consider the follow example
This function either returns a new
DateTime
object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returnedfalse
before passing on the value to another function or method that may not be able to handle afalse
.