Нет описания

DaemonHttpRunner.php 5.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. <?php
  2. namespace MailPoet\Cron;
  3. if (!defined('ABSPATH')) exit;
  4. use MailPoet\Cron\Triggers\WordPress;
  5. use MailPoet\Settings\SettingsController;
  6. use MailPoet\WP\Functions as WPFunctions;
  7. use Tracy\Debugger;
  8. class DaemonHttpRunner {
  9. public $settingsDaemonData;
  10. public $timer;
  11. public $token;
  12. /** @var Daemon|null */
  13. private $daemon;
  14. /** @var CronHelper */
  15. private $cronHelper;
  16. /** @var SettingsController */
  17. private $settings;
  18. const PING_SUCCESS_RESPONSE = 'pong';
  19. /** @var WordPress */
  20. private $wordpressTrigger;
  21. public function __construct(
  22. Daemon $daemon = null,
  23. CronHelper $cronHelper,
  24. SettingsController $settings,
  25. WordPress $wordpressTrigger
  26. ) {
  27. $this->cronHelper = $cronHelper;
  28. $this->settingsDaemonData = $this->cronHelper->getDaemon();
  29. $this->token = $this->cronHelper->createToken();
  30. $this->timer = microtime(true);
  31. $this->daemon = $daemon;
  32. $this->settings = $settings;
  33. $this->wordpressTrigger = $wordpressTrigger;
  34. }
  35. public function ping() {
  36. // if Tracy enabled & called by 'MailPoet Cron' user agent, disable Tracy Bar
  37. // (happens in CronHelperTest because it's not a real integration test - calls other WP instance)
  38. $userAgent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : null;
  39. if (class_exists(Debugger::class) && $userAgent === 'MailPoet Cron') {
  40. Debugger::$showBar = false;
  41. }
  42. $this->addCacheHeaders();
  43. $this->terminateRequest(self::PING_SUCCESS_RESPONSE);
  44. }
  45. public function run($requestData) {
  46. ignore_user_abort(true);
  47. if (strpos((string)@ini_get('disable_functions'), 'set_time_limit') === false) {
  48. set_time_limit(0);
  49. }
  50. $this->addCacheHeaders();
  51. if (!$requestData) {
  52. $error = WPFunctions::get()->__('Invalid or missing request data.', 'mailpoet');
  53. } else {
  54. if (!$this->settingsDaemonData) {
  55. $error = WPFunctions::get()->__('Daemon does not exist.', 'mailpoet');
  56. } else {
  57. if (!isset($requestData['token']) ||
  58. $requestData['token'] !== $this->settingsDaemonData['token']
  59. ) {
  60. $error = 'Invalid or missing token.';
  61. }
  62. }
  63. }
  64. if (!empty($error)) {
  65. return $this->abortWithError($error);
  66. }
  67. if ($this->daemon === null) {
  68. return $this->abortWithError(WPFunctions::get()->__('Daemon does not set correctly.', 'mailpoet'));
  69. }
  70. $this->settingsDaemonData['token'] = $this->token;
  71. $this->daemon->run($this->settingsDaemonData);
  72. // If we're using the WordPress trigger, check the conditions to stop cron if necessary
  73. $enableCronSelfDeactivation = WPFunctions::get()->applyFilters('mailpoet_cron_enable_self_deactivation', false);
  74. if ($enableCronSelfDeactivation
  75. && $this->isCronTriggerMethodWordPress()
  76. && !$this->checkWPTriggerExecutionRequirements()
  77. ) {
  78. $this->stopCron();
  79. } else {
  80. // if workers took less time to execute than the daemon execution limit,
  81. // pause daemon execution to ensure that daemon runs only once every X seconds
  82. $elapsedTime = microtime(true) - $this->timer;
  83. if ($elapsedTime < $this->cronHelper->getDaemonExecutionLimit()) {
  84. $this->pauseExecution($this->cronHelper->getDaemonExecutionLimit() - $elapsedTime);
  85. }
  86. }
  87. // after each execution, re-read daemon data in case it changed
  88. $settingsDaemonData = $this->cronHelper->getDaemon();
  89. if ($this->shouldTerminateExecution($settingsDaemonData)) {
  90. return $this->terminateRequest();
  91. }
  92. return $this->callSelf();
  93. }
  94. public function pauseExecution($pauseTime) {
  95. return sleep($pauseTime);
  96. }
  97. public function callSelf() {
  98. $this->cronHelper->accessDaemon($this->token);
  99. $this->terminateRequest();
  100. }
  101. public function abortWithError($message) {
  102. WPFunctions::get()->statusHeader(404, $message);
  103. exit;
  104. }
  105. public function terminateRequest($message = false) {
  106. die($message);
  107. }
  108. public function isCronTriggerMethodWordPress() {
  109. return $this->settings->get(CronTrigger::SETTING_NAME . '.method') === CronTrigger::METHOD_WORDPRESS;
  110. }
  111. public function checkWPTriggerExecutionRequirements() {
  112. return $this->wordpressTrigger->checkExecutionRequirements();
  113. }
  114. public function stopCron() {
  115. return $this->wordpressTrigger->stop();
  116. }
  117. /**
  118. * @param array|null $settingsDaemonData
  119. *
  120. * @return bool
  121. */
  122. private function shouldTerminateExecution(array $settingsDaemonData = null) {
  123. return !$settingsDaemonData ||
  124. $settingsDaemonData['token'] !== $this->token ||
  125. (isset($settingsDaemonData['status']) && $settingsDaemonData['status'] !== CronHelper::DAEMON_STATUS_ACTIVE);
  126. }
  127. private function addCacheHeaders() {
  128. if (headers_sent()) {
  129. return;
  130. }
  131. // Common Cache Control header. Should be respected by cache proxies and CDNs.
  132. header('Cache-Control: no-cache');
  133. // Mark as blacklisted for SG Optimizer for sites hosted on SiteGround.
  134. header('X-Cache-Enabled: False');
  135. // Set caching header for LiteSpeed server.
  136. header('X-LiteSpeed-Cache-Control: no-cache');
  137. }
  138. }