Scheduler.php 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. <?php namespace GO;
  2. use DateTime;
  3. use Exception;
  4. use InvalidArgumentException;
  5. class Scheduler
  6. {
  7. /**
  8. * The queued jobs.
  9. *
  10. * @var array
  11. */
  12. private $jobs = [];
  13. /**
  14. * Successfully executed jobs.
  15. *
  16. * @var array
  17. */
  18. private $executedJobs = [];
  19. /**
  20. * Failed jobs.
  21. *
  22. * @var FailedJob[]
  23. */
  24. private $failedJobs = [];
  25. /**
  26. * The verbose output of the scheduled jobs.
  27. *
  28. * @var array
  29. */
  30. private $outputSchedule = [];
  31. /**
  32. * @var array
  33. */
  34. private $config;
  35. /**
  36. * Create new instance.
  37. *
  38. * @param array $config
  39. */
  40. public function __construct(array $config = [])
  41. {
  42. $this->config = $config;
  43. }
  44. /**
  45. * Queue a job for execution in the correct queue.
  46. *
  47. * @param Job $job
  48. * @return void
  49. */
  50. private function queueJob(Job $job)
  51. {
  52. $this->jobs[] = $job;
  53. }
  54. /**
  55. * Prioritise jobs in background.
  56. *
  57. * @return array
  58. */
  59. private function prioritiseJobs()
  60. {
  61. $background = [];
  62. $foreground = [];
  63. foreach ($this->jobs as $job) {
  64. if ($job->canRunInBackground()) {
  65. $background[] = $job;
  66. } else {
  67. $foreground[] = $job;
  68. }
  69. }
  70. return array_merge($background, $foreground);
  71. }
  72. /**
  73. * Get the queued jobs.
  74. *
  75. * @return array
  76. */
  77. public function getQueuedJobs()
  78. {
  79. return $this->prioritiseJobs();
  80. }
  81. /**
  82. * Queues a function execution.
  83. *
  84. * @param callable $fn The function to execute
  85. * @param array $args Optional arguments to pass to the php script
  86. * @param string $id Optional custom identifier
  87. * @return Job
  88. */
  89. public function call(callable $fn, $args = [], $id = null)
  90. {
  91. $job = new Job($fn, $args, $id);
  92. $this->queueJob($job->configure($this->config));
  93. return $job;
  94. }
  95. /**
  96. * Queues a php script execution.
  97. *
  98. * @param string $script The path to the php script to execute
  99. * @param string $bin Optional path to the php binary
  100. * @param array $args Optional arguments to pass to the php script
  101. * @param string $id Optional custom identifier
  102. * @return Job
  103. */
  104. public function php($script, $bin = null, $args = [], $id = null)
  105. {
  106. if (! is_string($script)) {
  107. throw new InvalidArgumentException('The script should be a valid path to a file.');
  108. }
  109. $bin = $bin !== null && is_string($bin) && file_exists($bin) ?
  110. $bin : (PHP_BINARY === '' ? '/usr/bin/php' : PHP_BINARY);
  111. $job = new Job($bin . ' ' . $script, $args, $id);
  112. if (! file_exists($script)) {
  113. $this->pushFailedJob(
  114. $job,
  115. new InvalidArgumentException('The script should be a valid path to a file.')
  116. );
  117. }
  118. $this->queueJob($job->configure($this->config));
  119. return $job;
  120. }
  121. /**
  122. * Queue a raw shell command.
  123. *
  124. * @param string $command The command to execute
  125. * @param array $args Optional arguments to pass to the command
  126. * @param string $id Optional custom identifier
  127. * @return Job
  128. */
  129. public function raw($command, $args = [], $id = null)
  130. {
  131. $job = new Job($command, $args, $id);
  132. $this->queueJob($job->configure($this->config));
  133. return $job;
  134. }
  135. /**
  136. * Run the scheduler.
  137. *
  138. * @param DateTime $runTime Optional, run at specific moment
  139. * @return array Executed jobs
  140. */
  141. public function run(Datetime $runTime = null)
  142. {
  143. $jobs = $this->getQueuedJobs();
  144. if (is_null($runTime)) {
  145. $runTime = new DateTime('now');
  146. }
  147. foreach ($jobs as $job) {
  148. if ($job->isDue($runTime)) {
  149. try {
  150. $job->run();
  151. $this->pushExecutedJob($job);
  152. } catch (\Exception $e) {
  153. $this->pushFailedJob($job, $e);
  154. }
  155. }
  156. }
  157. return $this->getExecutedJobs();
  158. }
  159. /**
  160. * Reset all collected data of last run.
  161. *
  162. * Call before run() if you call run() multiple times.
  163. */
  164. public function resetRun()
  165. {
  166. // Reset collected data of last run
  167. $this->executedJobs = [];
  168. $this->failedJobs = [];
  169. $this->outputSchedule = [];
  170. return $this;
  171. }
  172. /**
  173. * Add an entry to the scheduler verbose output array.
  174. *
  175. * @param string $string
  176. * @return void
  177. */
  178. private function addSchedulerVerboseOutput($string)
  179. {
  180. $now = '[' . (new DateTime('now'))->format('c') . '] ';
  181. $this->outputSchedule[] = $now . $string;
  182. // Print to stdoutput in light gray
  183. // echo "\033[37m{$string}\033[0m\n";
  184. }
  185. /**
  186. * Push a succesfully executed job.
  187. *
  188. * @param Job $job
  189. * @return Job
  190. */
  191. private function pushExecutedJob(Job $job)
  192. {
  193. $this->executedJobs[] = $job;
  194. $compiled = $job->compile();
  195. // If callable, log the string Closure
  196. if (is_callable($compiled)) {
  197. $compiled = 'Closure';
  198. }
  199. $this->addSchedulerVerboseOutput("Executing {$compiled}");
  200. return $job;
  201. }
  202. /**
  203. * Get the executed jobs.
  204. *
  205. * @return array
  206. */
  207. public function getExecutedJobs()
  208. {
  209. return $this->executedJobs;
  210. }
  211. /**
  212. * Push a failed job.
  213. *
  214. * @param Job $job
  215. * @param Exception $e
  216. * @return Job
  217. */
  218. private function pushFailedJob(Job $job, Exception $e)
  219. {
  220. $this->failedJobs[] = new FailedJob($job, $e);
  221. $compiled = $job->compile();
  222. // If callable, log the string Closure
  223. if (is_callable($compiled)) {
  224. $compiled = 'Closure';
  225. }
  226. $this->addSchedulerVerboseOutput("{$e->getMessage()}: {$compiled}");
  227. return $job;
  228. }
  229. /**
  230. * Get the failed jobs.
  231. *
  232. * @return FailedJob[]
  233. */
  234. public function getFailedJobs()
  235. {
  236. return $this->failedJobs;
  237. }
  238. /**
  239. * Get the scheduler verbose output.
  240. *
  241. * @param string $type Allowed: text, html, array
  242. * @return mixed The return depends on the requested $type
  243. */
  244. public function getVerboseOutput($type = 'text')
  245. {
  246. switch ($type) {
  247. case 'text':
  248. return implode("\n", $this->outputSchedule);
  249. case 'html':
  250. return implode('<br>', $this->outputSchedule);
  251. case 'array':
  252. return $this->outputSchedule;
  253. default:
  254. throw new InvalidArgumentException('Invalid output type');
  255. }
  256. }
  257. /**
  258. * Remove all queued Jobs.
  259. */
  260. public function clearJobs()
  261. {
  262. $this->jobs = [];
  263. return $this;
  264. }
  265. /**
  266. * Start a worker.
  267. *
  268. * @param array $seconds - When the scheduler should run
  269. */
  270. public function work(array $seconds = [0])
  271. {
  272. while (true) {
  273. if (in_array((int) date('s'), $seconds)) {
  274. $this->run();
  275. sleep(1);
  276. }
  277. }
  278. }
  279. }