PageRenderTime 28ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/laravel/framework/src/Illuminate/Console/Scheduling/Event.php

https://gitlab.com/ealexis.t/trends
PHP | 928 lines | 471 code | 95 blank | 362 comment | 9 complexity | 991a67433d868cc84ae2228a3c3bf786 MD5 | raw file
  1. <?php
  2. namespace Illuminate\Console\Scheduling;
  3. use Closure;
  4. use Carbon\Carbon;
  5. use LogicException;
  6. use Cron\CronExpression;
  7. use GuzzleHttp\Client as HttpClient;
  8. use Illuminate\Contracts\Mail\Mailer;
  9. use Symfony\Component\Process\Process;
  10. use Symfony\Component\Process\ProcessUtils;
  11. use Illuminate\Contracts\Container\Container;
  12. class Event
  13. {
  14. /**
  15. * The command string.
  16. *
  17. * @var string
  18. */
  19. public $command;
  20. /**
  21. * The cron expression representing the event's frequency.
  22. *
  23. * @var string
  24. */
  25. public $expression = '* * * * * *';
  26. /**
  27. * The timezone the date should be evaluated on.
  28. *
  29. * @var \DateTimeZone|string
  30. */
  31. public $timezone;
  32. /**
  33. * The user the command should run as.
  34. *
  35. * @var string
  36. */
  37. public $user;
  38. /**
  39. * The list of environments the command should run under.
  40. *
  41. * @var array
  42. */
  43. public $environments = [];
  44. /**
  45. * Indicates if the command should run in maintenance mode.
  46. *
  47. * @var bool
  48. */
  49. public $evenInMaintenanceMode = false;
  50. /**
  51. * Indicates if the command should not overlap itself.
  52. *
  53. * @var bool
  54. */
  55. public $withoutOverlapping = false;
  56. /**
  57. * Indicates if the command should run in background.
  58. *
  59. * @var bool
  60. */
  61. public $runInBackground = false;
  62. /**
  63. * The array of filter callbacks.
  64. *
  65. * @var array
  66. */
  67. protected $filters = [];
  68. /**
  69. * The array of reject callbacks.
  70. *
  71. * @var array
  72. */
  73. protected $rejects = [];
  74. /**
  75. * The location that output should be sent to.
  76. *
  77. * @var string
  78. */
  79. public $output = '/dev/null';
  80. /**
  81. * Indicates whether output should be appended.
  82. *
  83. * @var bool
  84. */
  85. protected $shouldAppendOutput = false;
  86. /**
  87. * The array of callbacks to be run before the event is started.
  88. *
  89. * @var array
  90. */
  91. protected $beforeCallbacks = [];
  92. /**
  93. * The array of callbacks to be run after the event is finished.
  94. *
  95. * @var array
  96. */
  97. protected $afterCallbacks = [];
  98. /**
  99. * The human readable description of the event.
  100. *
  101. * @var string
  102. */
  103. public $description;
  104. /**
  105. * Create a new event instance.
  106. *
  107. * @param string $command
  108. * @return void
  109. */
  110. public function __construct($command)
  111. {
  112. $this->command = $command;
  113. $this->output = $this->getDefaultOutput();
  114. }
  115. /**
  116. * Get the default output depending on the OS.
  117. *
  118. * @return string
  119. */
  120. protected function getDefaultOutput()
  121. {
  122. return (DIRECTORY_SEPARATOR == '\\') ? 'NUL' : '/dev/null';
  123. }
  124. /**
  125. * Run the given event.
  126. *
  127. * @param \Illuminate\Contracts\Container\Container $container
  128. * @return void
  129. */
  130. public function run(Container $container)
  131. {
  132. if (! $this->runInBackground) {
  133. $this->runCommandInForeground($container);
  134. } else {
  135. $this->runCommandInBackground();
  136. }
  137. }
  138. /**
  139. * Run the command in the foreground.
  140. *
  141. * @param \Illuminate\Contracts\Container\Container $container
  142. * @return void
  143. */
  144. protected function runCommandInForeground(Container $container)
  145. {
  146. $this->callBeforeCallbacks($container);
  147. (new Process(
  148. trim($this->buildCommand(), '& '), base_path(), null, null, null
  149. ))->run();
  150. $this->callAfterCallbacks($container);
  151. }
  152. /**
  153. * Run the command in the background.
  154. *
  155. * @return void
  156. */
  157. protected function runCommandInBackground()
  158. {
  159. (new Process(
  160. $this->buildCommand(), base_path(), null, null, null
  161. ))->run();
  162. }
  163. /**
  164. * Call all of the "before" callbacks for the event.
  165. *
  166. * @param \Illuminate\Contracts\Container\Container $container
  167. * @return void
  168. */
  169. protected function callBeforeCallbacks(Container $container)
  170. {
  171. foreach ($this->beforeCallbacks as $callback) {
  172. $container->call($callback);
  173. }
  174. }
  175. /**
  176. * Call all of the "after" callbacks for the event.
  177. *
  178. * @param \Illuminate\Contracts\Container\Container $container
  179. * @return void
  180. */
  181. protected function callAfterCallbacks(Container $container)
  182. {
  183. foreach ($this->afterCallbacks as $callback) {
  184. $container->call($callback);
  185. }
  186. }
  187. /**
  188. * Build the command string.
  189. *
  190. * @return string
  191. */
  192. public function buildCommand()
  193. {
  194. $output = ProcessUtils::escapeArgument($this->output);
  195. $redirect = $this->shouldAppendOutput ? ' >> ' : ' > ';
  196. if ($this->withoutOverlapping) {
  197. if (windows_os()) {
  198. $command = '(echo \'\' > "'.$this->mutexPath().'" & '.$this->command.' & del "'.$this->mutexPath().'")'.$redirect.$output.' 2>&1 &';
  199. } else {
  200. $command = '(touch '.$this->mutexPath().'; '.$this->command.'; rm '.$this->mutexPath().')'.$redirect.$output.' 2>&1 &';
  201. }
  202. } else {
  203. $command = $this->command.$redirect.$output.' 2>&1 &';
  204. }
  205. return $this->user && ! windows_os() ? 'sudo -u '.$this->user.' -- sh -c \''.$command.'\'' : $command;
  206. }
  207. /**
  208. * Get the mutex path for the scheduled command.
  209. *
  210. * @return string
  211. */
  212. protected function mutexPath()
  213. {
  214. return storage_path('framework'.DIRECTORY_SEPARATOR.'schedule-'.sha1($this->expression.$this->command));
  215. }
  216. /**
  217. * Determine if the given event should run based on the Cron expression.
  218. *
  219. * @param \Illuminate\Contracts\Foundation\Application $app
  220. * @return bool
  221. */
  222. public function isDue($app)
  223. {
  224. if (! $this->runsInMaintenanceMode() && $app->isDownForMaintenance()) {
  225. return false;
  226. }
  227. return $this->expressionPasses() &&
  228. $this->runsInEnvironment($app->environment());
  229. }
  230. /**
  231. * Determine if the Cron expression passes.
  232. *
  233. * @return bool
  234. */
  235. protected function expressionPasses()
  236. {
  237. $date = Carbon::now();
  238. if ($this->timezone) {
  239. $date->setTimezone($this->timezone);
  240. }
  241. return CronExpression::factory($this->expression)->isDue($date->toDateTimeString());
  242. }
  243. /**
  244. * Determine if the filters pass for the event.
  245. *
  246. * @param \Illuminate\Contracts\Foundation\Application $app
  247. * @return bool
  248. */
  249. public function filtersPass($app)
  250. {
  251. foreach ($this->filters as $callback) {
  252. if (! $app->call($callback)) {
  253. return false;
  254. }
  255. }
  256. foreach ($this->rejects as $callback) {
  257. if ($app->call($callback)) {
  258. return false;
  259. }
  260. }
  261. return true;
  262. }
  263. /**
  264. * Determine if the event runs in the given environment.
  265. *
  266. * @param string $environment
  267. * @return bool
  268. */
  269. public function runsInEnvironment($environment)
  270. {
  271. return empty($this->environments) || in_array($environment, $this->environments);
  272. }
  273. /**
  274. * Determine if the event runs in maintenance mode.
  275. *
  276. * @return bool
  277. */
  278. public function runsInMaintenanceMode()
  279. {
  280. return $this->evenInMaintenanceMode;
  281. }
  282. /**
  283. * The Cron expression representing the event's frequency.
  284. *
  285. * @param string $expression
  286. * @return $this
  287. */
  288. public function cron($expression)
  289. {
  290. $this->expression = $expression;
  291. return $this;
  292. }
  293. /**
  294. * Schedule the event to run hourly.
  295. *
  296. * @return $this
  297. */
  298. public function hourly()
  299. {
  300. return $this->cron('0 * * * * *');
  301. }
  302. /**
  303. * Schedule the event to run daily.
  304. *
  305. * @return $this
  306. */
  307. public function daily()
  308. {
  309. return $this->cron('0 0 * * * *');
  310. }
  311. /**
  312. * Schedule the command at a given time.
  313. *
  314. * @param string $time
  315. * @return $this
  316. */
  317. public function at($time)
  318. {
  319. return $this->dailyAt($time);
  320. }
  321. /**
  322. * Schedule the event to run daily at a given time (10:00, 19:30, etc).
  323. *
  324. * @param string $time
  325. * @return $this
  326. */
  327. public function dailyAt($time)
  328. {
  329. $segments = explode(':', $time);
  330. return $this->spliceIntoPosition(2, (int) $segments[0])
  331. ->spliceIntoPosition(1, count($segments) == 2 ? (int) $segments[1] : '0');
  332. }
  333. /**
  334. * Schedule the event to run twice daily.
  335. *
  336. * @param int $first
  337. * @param int $second
  338. * @return $this
  339. */
  340. public function twiceDaily($first = 1, $second = 13)
  341. {
  342. $hours = $first.','.$second;
  343. return $this->spliceIntoPosition(1, 0)
  344. ->spliceIntoPosition(2, $hours);
  345. }
  346. /**
  347. * Schedule the event to run only on weekdays.
  348. *
  349. * @return $this
  350. */
  351. public function weekdays()
  352. {
  353. return $this->spliceIntoPosition(5, '1-5');
  354. }
  355. /**
  356. * Schedule the event to run only on Mondays.
  357. *
  358. * @return $this
  359. */
  360. public function mondays()
  361. {
  362. return $this->days(1);
  363. }
  364. /**
  365. * Schedule the event to run only on Tuesdays.
  366. *
  367. * @return $this
  368. */
  369. public function tuesdays()
  370. {
  371. return $this->days(2);
  372. }
  373. /**
  374. * Schedule the event to run only on Wednesdays.
  375. *
  376. * @return $this
  377. */
  378. public function wednesdays()
  379. {
  380. return $this->days(3);
  381. }
  382. /**
  383. * Schedule the event to run only on Thursdays.
  384. *
  385. * @return $this
  386. */
  387. public function thursdays()
  388. {
  389. return $this->days(4);
  390. }
  391. /**
  392. * Schedule the event to run only on Fridays.
  393. *
  394. * @return $this
  395. */
  396. public function fridays()
  397. {
  398. return $this->days(5);
  399. }
  400. /**
  401. * Schedule the event to run only on Saturdays.
  402. *
  403. * @return $this
  404. */
  405. public function saturdays()
  406. {
  407. return $this->days(6);
  408. }
  409. /**
  410. * Schedule the event to run only on Sundays.
  411. *
  412. * @return $this
  413. */
  414. public function sundays()
  415. {
  416. return $this->days(0);
  417. }
  418. /**
  419. * Schedule the event to run weekly.
  420. *
  421. * @return $this
  422. */
  423. public function weekly()
  424. {
  425. return $this->cron('0 0 * * 0 *');
  426. }
  427. /**
  428. * Schedule the event to run weekly on a given day and time.
  429. *
  430. * @param int $day
  431. * @param string $time
  432. * @return $this
  433. */
  434. public function weeklyOn($day, $time = '0:0')
  435. {
  436. $this->dailyAt($time);
  437. return $this->spliceIntoPosition(5, $day);
  438. }
  439. /**
  440. * Schedule the event to run monthly.
  441. *
  442. * @return $this
  443. */
  444. public function monthly()
  445. {
  446. return $this->cron('0 0 1 * * *');
  447. }
  448. /**
  449. * Schedule the event to run monthly on a given day and time.
  450. *
  451. * @param int $day
  452. * @param string $time
  453. * @return $this
  454. */
  455. public function monthlyOn($day = 1, $time = '0:0')
  456. {
  457. $this->dailyAt($time);
  458. return $this->spliceIntoPosition(3, $day);
  459. }
  460. /**
  461. * Schedule the event to run quarterly.
  462. *
  463. * @return $this
  464. */
  465. public function quarterly()
  466. {
  467. return $this->cron('0 0 1 */3 *');
  468. }
  469. /**
  470. * Schedule the event to run yearly.
  471. *
  472. * @return $this
  473. */
  474. public function yearly()
  475. {
  476. return $this->cron('0 0 1 1 * *');
  477. }
  478. /**
  479. * Schedule the event to run every minute.
  480. *
  481. * @return $this
  482. */
  483. public function everyMinute()
  484. {
  485. return $this->cron('* * * * * *');
  486. }
  487. /**
  488. * Schedule the event to run every five minutes.
  489. *
  490. * @return $this
  491. */
  492. public function everyFiveMinutes()
  493. {
  494. return $this->cron('*/5 * * * * *');
  495. }
  496. /**
  497. * Schedule the event to run every ten minutes.
  498. *
  499. * @return $this
  500. */
  501. public function everyTenMinutes()
  502. {
  503. return $this->cron('*/10 * * * * *');
  504. }
  505. /**
  506. * Schedule the event to run every thirty minutes.
  507. *
  508. * @return $this
  509. */
  510. public function everyThirtyMinutes()
  511. {
  512. return $this->cron('0,30 * * * * *');
  513. }
  514. /**
  515. * Set the days of the week the command should run on.
  516. *
  517. * @param array|mixed $days
  518. * @return $this
  519. */
  520. public function days($days)
  521. {
  522. $days = is_array($days) ? $days : func_get_args();
  523. return $this->spliceIntoPosition(5, implode(',', $days));
  524. }
  525. /**
  526. * State that the command should run in background.
  527. *
  528. * @return $this
  529. */
  530. public function runInBackground()
  531. {
  532. $this->runInBackground = true;
  533. return $this;
  534. }
  535. /**
  536. * Set the timezone the date should be evaluated on.
  537. *
  538. * @param \DateTimeZone|string $timezone
  539. * @return $this
  540. */
  541. public function timezone($timezone)
  542. {
  543. $this->timezone = $timezone;
  544. return $this;
  545. }
  546. /**
  547. * Set which user the command should run as.
  548. *
  549. * @param string $user
  550. * @return $this
  551. */
  552. public function user($user)
  553. {
  554. $this->user = $user;
  555. return $this;
  556. }
  557. /**
  558. * Limit the environments the command should run in.
  559. *
  560. * @param array|mixed $environments
  561. * @return $this
  562. */
  563. public function environments($environments)
  564. {
  565. $this->environments = is_array($environments) ? $environments : func_get_args();
  566. return $this;
  567. }
  568. /**
  569. * State that the command should run even in maintenance mode.
  570. *
  571. * @return $this
  572. */
  573. public function evenInMaintenanceMode()
  574. {
  575. $this->evenInMaintenanceMode = true;
  576. return $this;
  577. }
  578. /**
  579. * Do not allow the event to overlap each other.
  580. *
  581. * @return $this
  582. */
  583. public function withoutOverlapping()
  584. {
  585. $this->withoutOverlapping = true;
  586. return $this->skip(function () {
  587. return file_exists($this->mutexPath());
  588. });
  589. }
  590. /**
  591. * Register a callback to further filter the schedule.
  592. *
  593. * @param \Closure $callback
  594. * @return $this
  595. */
  596. public function when(Closure $callback)
  597. {
  598. $this->filters[] = $callback;
  599. return $this;
  600. }
  601. /**
  602. * Register a callback to further filter the schedule.
  603. *
  604. * @param \Closure $callback
  605. * @return $this
  606. */
  607. public function skip(Closure $callback)
  608. {
  609. $this->rejects[] = $callback;
  610. return $this;
  611. }
  612. /**
  613. * Send the output of the command to a given location.
  614. *
  615. * @param string $location
  616. * @param bool $append
  617. * @return $this
  618. */
  619. public function sendOutputTo($location, $append = false)
  620. {
  621. $this->output = $location;
  622. $this->shouldAppendOutput = $append;
  623. return $this;
  624. }
  625. /**
  626. * Append the output of the command to a given location.
  627. *
  628. * @param string $location
  629. * @return $this
  630. */
  631. public function appendOutputTo($location)
  632. {
  633. return $this->sendOutputTo($location, true);
  634. }
  635. /**
  636. * E-mail the results of the scheduled operation.
  637. *
  638. * @param array|mixed $addresses
  639. * @param bool $onlyIfOutputExists
  640. * @return $this
  641. *
  642. * @throws \LogicException
  643. */
  644. public function emailOutputTo($addresses, $onlyIfOutputExists = false)
  645. {
  646. if (is_null($this->output) || $this->output == $this->getDefaultOutput()) {
  647. throw new LogicException('Must direct output to a file in order to e-mail results.');
  648. }
  649. $addresses = is_array($addresses) ? $addresses : func_get_args();
  650. return $this->then(function (Mailer $mailer) use ($addresses, $onlyIfOutputExists) {
  651. $this->emailOutput($mailer, $addresses, $onlyIfOutputExists);
  652. });
  653. }
  654. /**
  655. * E-mail the results of the scheduled operation if it produces output.
  656. *
  657. * @param array|mixed $addresses
  658. * @return $this
  659. *
  660. * @throws \LogicException
  661. */
  662. public function emailWrittenOutputTo($addresses)
  663. {
  664. return $this->emailOutputTo($addresses, true);
  665. }
  666. /**
  667. * E-mail the output of the event to the recipients.
  668. *
  669. * @param \Illuminate\Contracts\Mail\Mailer $mailer
  670. * @param array $addresses
  671. * @param bool $onlyIfOutputExists
  672. * @return void
  673. */
  674. protected function emailOutput(Mailer $mailer, $addresses, $onlyIfOutputExists = false)
  675. {
  676. $text = file_get_contents($this->output);
  677. if ($onlyIfOutputExists && empty($text)) {
  678. return;
  679. }
  680. $mailer->raw($text, function ($m) use ($addresses) {
  681. $m->subject($this->getEmailSubject());
  682. foreach ($addresses as $address) {
  683. $m->to($address);
  684. }
  685. });
  686. }
  687. /**
  688. * Get the e-mail subject line for output results.
  689. *
  690. * @return string
  691. */
  692. protected function getEmailSubject()
  693. {
  694. if ($this->description) {
  695. return 'Scheduled Job Output ('.$this->description.')';
  696. }
  697. return 'Scheduled Job Output';
  698. }
  699. /**
  700. * Register a callback to ping a given URL before the job runs.
  701. *
  702. * @param string $url
  703. * @return $this
  704. */
  705. public function pingBefore($url)
  706. {
  707. return $this->before(function () use ($url) {
  708. (new HttpClient)->get($url);
  709. });
  710. }
  711. /**
  712. * Register a callback to be called before the operation.
  713. *
  714. * @param \Closure $callback
  715. * @return $this
  716. */
  717. public function before(Closure $callback)
  718. {
  719. $this->beforeCallbacks[] = $callback;
  720. return $this;
  721. }
  722. /**
  723. * Register a callback to ping a given URL after the job runs.
  724. *
  725. * @param string $url
  726. * @return $this
  727. */
  728. public function thenPing($url)
  729. {
  730. return $this->then(function () use ($url) {
  731. (new HttpClient)->get($url);
  732. });
  733. }
  734. /**
  735. * Register a callback to be called after the operation.
  736. *
  737. * @param \Closure $callback
  738. * @return $this
  739. */
  740. public function after(Closure $callback)
  741. {
  742. return $this->then($callback);
  743. }
  744. /**
  745. * Register a callback to be called after the operation.
  746. *
  747. * @param \Closure $callback
  748. * @return $this
  749. */
  750. public function then(Closure $callback)
  751. {
  752. $this->afterCallbacks[] = $callback;
  753. return $this;
  754. }
  755. /**
  756. * Set the human-friendly description of the event.
  757. *
  758. * @param string $description
  759. * @return $this
  760. */
  761. public function name($description)
  762. {
  763. return $this->description($description);
  764. }
  765. /**
  766. * Set the human-friendly description of the event.
  767. *
  768. * @param string $description
  769. * @return $this
  770. */
  771. public function description($description)
  772. {
  773. $this->description = $description;
  774. return $this;
  775. }
  776. /**
  777. * Splice the given value into the given position of the expression.
  778. *
  779. * @param int $position
  780. * @param string $value
  781. * @return $this
  782. */
  783. protected function spliceIntoPosition($position, $value)
  784. {
  785. $segments = explode(' ', $this->expression);
  786. $segments[$position - 1] = $value;
  787. return $this->cron(implode(' ', $segments));
  788. }
  789. /**
  790. * Get the summary of the event for display.
  791. *
  792. * @return string
  793. */
  794. public function getSummaryForDisplay()
  795. {
  796. if (is_string($this->description)) {
  797. return $this->description;
  798. }
  799. return $this->buildCommand();
  800. }
  801. /**
  802. * Get the Cron expression for the event.
  803. *
  804. * @return string
  805. */
  806. public function getExpression()
  807. {
  808. return $this->expression;
  809. }
  810. }