PluginProbe ʕ •ᴥ•ʔ
MailPoet – Newsletters, Email Marketing, and Automation / 5.34.2
MailPoet – Newsletters, Email Marketing, and Automation v5.34.2
5.34.2 5.34.1 5.34.0 5.33.1 5.33.0 5.32.0 5.31.0 5.30.0 5.29.0 5.28.1 5.28.0 5.27.0 5.26.0 5.26.1 5.25.0 5.24.0 4.43.0 4.43.1 4.44.0 4.44.1 4.45.0 4.46.0 4.47.0 4.48.0 4.48.1 4.48.2 4.49.0 4.49.1 4.5.0 4.5.1 4.5.2 4.50.0 4.50.1 4.51.0 4.51.1 4.51.2 4.52.0 4.53.0 4.54.0 4.55.0 4.56.0 4.57.0 4.58.0 4.58.1 4.58.2 4.6.0 4.6.1 4.6.2 4.7.0 4.7.1 4.8.0 4.8.1 4.9.0 5.0.0 5.0.1 5.0.2 5.1.0 5.1.1 5.10.0 5.10.1 5.11.0 5.12.0 5.12.1 5.12.10 5.12.11 5.12.12 5.12.13 5.12.2 5.12.3 5.12.4 5.12.5 5.12.6 5.12.7 5.12.8 5.12.9 5.13.0 5.13.1 5.13.2 5.14.0 5.14.1 5.14.2 5.14.3 5.15.0 5.15.1 5.16.0 5.16.1 5.16.2 5.16.3 5.16.4 5.17.0 5.17.1 5.17.2 5.17.3 5.17.4 5.17.5 5.17.6 5.18.0 5.19.0 5.2.0 5.2.1 5.2.2 5.2.3 5.20.0 5.21.0 5.21.1 5.21.2 5.21.3 5.22.0 5.22.1 5.22.2 5.22.3 5.22.4 5.23.0 5.23.1 5.23.2 5.3.0 5.3.1 5.3.2 5.3.3 5.3.4 5.3.5 5.3.6 5.3.7 5.4.0 5.4.1 5.4.2 5.5.0 5.5.1 5.5.2 5.6.0 5.6.1 5.6.2 5.6.3 5.6.4 5.7.0 5.7.1 5.8.0 5.8.1 5.9.0 3.0.0-beta.15 3.7.1 3.0.0-beta.16 3.7.2 3.0.0-beta.17 3.7.3 3.0.0-beta.18 3.7.4 3.0.0-beta.19 3.7.5 3.0.0-beta.2 3.7.6 3.0.0-beta.20 3.7.8 3.0.0-beta.21 3.70.0 3.0.0-beta.22 3.71.0 3.0.0-beta.23 3.71.1 3.0.0-beta.23.1 3.71.2 3.0.0-beta.23.2 3.71.3 3.0.0-beta.24 3.72.0 3.0.0-beta.25 3.73.0 3.0.0-beta.26 3.73.1 3.0.0-beta.27 3.73.2 3.0.0-beta.28 3.74.0 3.0.0-beta.29 3.74.1 3.0.0-beta.3 3.74.2 3.0.0-beta.30 3.74.3 3.0.0-beta.31 3.75.0 3.0.0-beta.32 3.75.1 3.0.0-beta.33 3.76.0 3.0.0-beta.33.1 3.77.0 3.0.0-beta.34.0.0 3.77.1 3.0.0-beta.36.0.0 3.78.0 3.0.0-beta.36.0.1 3.79.0 3.0.0-beta.36.2.0 3.8 3.0.0-beta.36.3.0 3.8.1 3.0.0-beta.36.3.1 3.8.2 3.0.0-beta.37.0.0 3.8.3 3.0.0-beta.4 3.8.4 3.0.0-beta.5 3.8.5 3.0.0-beta.6 3.8.6 3.0.0-beta.7 3.80.0 3.0.0-beta.7.1 3.81.0 3.0.0-beta.8 3.82.0 3.0.0-beta.9 3.83.0 3.0.0-rc.1.0.0 3.84.0 3.0.0-rc.1.0.1 3.84.1 3.0.0-rc.1.0.2 3.85.0 3.0.0-rc.1.0.3 3.85.1 3.0.0-rc.1.0.4 3.86.0 3.0.0-rc.2.0.0 3.87.0 3.0.0-rc.2.0.1 3.87.1 3.0.0-rc.2.0.2 3.87.2 3.0.0-rc.2.0.3 3.88.0 3.0.1 3.88.1 3.0.2 3.88.2 3.0.3 3.89.0 3.0.4 3.89.1 3.0.5 3.89.2 3.0.6 3.89.3 3.0.7 3.89.4 3.0.8 3.9.0 3.0.9 3.9.1 3.1.0 3.90.0 3.10 3.90.1 3.10.1 3.90.2 3.100.0 3.91.0 3.100.1 3.91.1 3.100.2 3.92.0 3.101.0 3.92.1 3.101.1 3.93.0 3.102.0 3.93.1 3.102.1 3.94.0 3.103.0 3.95.0 3.103.1 3.95.1 3.11.0 3.96.0 3.11.1 3.96.1 3.11.2 3.97.0 3.11.3 3.98.0 3.11.4 3.98.1 3.11.5 3.99.0 3.12.0 3.99.1 3.12.1 4.0.0 3.13.0 4.0.1 3.14.0 4.1.0 3.14.1 4.1.1 3.15.0 4.10.0 3.16.0 4.11.0 3.16.1 4.11.1 3.16.2 4.12.0 3.16.3 4.12.1 3.17.0 4.12.2 3.17.1 4.13.0 3.17.2 4.14.0 3.18.0 4.15.0 3.18.1 4.16.0 3.18.2 4.17.0 3.19.0 4.17.1 3.19.1 4.18.0 3.19.2 4.18.1 3.19.3 4.19.0 3.2.0 4.2.0 3.2.1 4.20.0 3.2.2 4.20.1 3.2.3 4.20.2 3.2.4 4.21.0 3.2.5 4.22.0 3.20.0 4.22.1 3.21.0 4.22.2 3.21.1 4.23.0 3.22.0 4.24.0 3.23.0 4.25.0 3.23.1 4.26.0 3.23.2 4.26.1 3.24.0 4.27.0 3.25.0 4.28.0 3.25.1 4.29.0 3.26.0 4.3.0 3.26.1 4.3.1 3.27.0 4.30.0 3.28.0 4.31.0 3.29.0 4.31.1 3.3.0 4.32.0 3.3.1 4.33.0 3.3.2 4.34.0 3.3.3 4.35.0 3.3.4 4.35.1 3.3.5 4.36.0 3.3.6 4.37.0 3.30.0 4.38.0 3.31.0 4.39.0 3.31.1 4.4.0 3.32.0 4.40.0 3.32.1 4.41.0 3.32.2 4.41.1 3.33.0 4.41.2 3.34.0 4.41.3 3.34.1 4.42.0 3.34.2 4.42.1 3.34.3 3.34.4 3.35.0 3.35.1 3.35.3 3.35.4 3.36.0 3.37.0 3.37.1 3.37.2 3.37.3 3.38.0 3.38.1 3.39.0 3.39.1 3.39.2 3.4.0 3.4.1 3.4.2 3.4.3 3.4.4 3.40.0 3.40.1 3.41.0 3.41.1 3.41.2 3.42.0 3.42.1 3.42.2 3.42.3 3.43.0 3.43.1 3.44.0 3.45.0 3.45.1 3.46.0 3.46.1 3.46.10 3.46.11 3.46.12 3.46.13 3.46.14 3.46.2 3.46.3 3.46.4 3.46.5 3.46.6 3.46.7 3.46.8 3.46.9 3.47.0 3.47.1 3.47.10 3.47.11 3.47.2 3.47.3 3.47.5 3.47.6 3.47.7 3.47.9 3.48.0 3.48.1 3.49.0 3.49.1 3.5.0 3.5.1 3.50.0 3.51.0 3.51.1 3.51.2 3.52.0 3.53.0 3.54.0 3.54.1 3.54.2 3.54.3 3.55.0 3.55.1 3.56.0 3.56.1 3.56.2 3.57.0 3.57.1 3.58.0 3.59.0 3.59.1 3.59.2 3.6.0 3.6.1 3.6.2 3.6.3 3.6.4 3.6.5 3.6.6 3.6.7 3.60.0 3.60.1 3.60.10 3.60.11 3.60.12 3.60.2 3.60.3 3.60.4 3.60.6 3.60.7 3.60.8 3.60.9 3.61.0 3.62.0 3.62.1 3.63.0 3.64.0 3.64.1 3.64.2 3.64.3 3.65.0 trunk 3.65.1 3.0.0 3.66.0 3.0.0-beta.1 3.67.0 3.0.0-beta.10 3.67.1 3.0.0-beta.11 3.68.0 3.0.0-beta.12 3.69.0 3.0.0-beta.13 3.69.1 3.0.0-beta.14 3.7.0
mailpoet / lib / Cron / CronHelper.php
mailpoet / lib / Cron Last commit date
ActionScheduler 2 months ago CliCommands 1 month ago Triggers 2 months ago Workers 2 days ago CronHelper.php 2 months ago CronTrigger.php 2 years ago CronWorkerInterface.php 3 years ago CronWorkerRunner.php 2 months ago CronWorkerScheduler.php 10 months ago Daemon.php 2 days ago DaemonActionSchedulerRunner.php 6 months ago DaemonHttpRunner.php 2 months ago Supervisor.php 3 years ago index.php 3 years ago
CronHelper.php
241 lines
1 <?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
2
3 namespace MailPoet\Cron;
4
5 if (!defined('ABSPATH')) exit;
6
7
8 use MailPoet\Router\Endpoints\CronDaemon as CronDaemonEndpoint;
9 use MailPoet\Router\Router;
10 use MailPoet\Settings\SettingsController;
11 use MailPoet\Util\Security;
12 use MailPoet\WP\Functions as WPFunctions;
13
14 class CronHelper {
15 const DAEMON_EXECUTION_LIMIT = 20; // seconds
16 const DAEMON_REQUEST_TIMEOUT = 5; // seconds
17 const DAEMON_SETTING = 'cron_daemon';
18 const DAEMON_STATUS_ACTIVE = 'active';
19 const DAEMON_STATUS_INACTIVE = 'inactive';
20
21 // Error codes
22 const DAEMON_EXECUTION_LIMIT_REACHED = 1001;
23
24 /** @var SettingsController */
25 private $settings;
26
27 /** @var WPFunctions */
28 private $wp;
29
30 public function __construct(
31 SettingsController $settings,
32 WPFunctions $wp
33 ) {
34 $this->settings = $settings;
35 $this->wp = $wp;
36 }
37
38 public function getDaemonExecutionLimit() {
39 $limit = $this->wp->applyFilters('mailpoet_cron_get_execution_limit', self::DAEMON_EXECUTION_LIMIT);
40 return $limit;
41 }
42
43 public function getDaemonExecutionTimeout() {
44 $limit = $this->getDaemonExecutionLimit();
45 $timeout = $limit * 1.75;
46 return $this->wp->applyFilters('mailpoet_cron_get_execution_timeout', $timeout);
47 }
48
49 public function createDaemon($token) {
50 $daemon = [
51 'token' => $token,
52 'status' => self::DAEMON_STATUS_ACTIVE,
53 'run_accessed_at' => null,
54 'run_started_at' => null,
55 'run_completed_at' => null,
56 'last_error' => null,
57 'last_error_date' => null,
58 ];
59 $this->saveDaemon($daemon);
60 return $daemon;
61 }
62
63 public function restartDaemon($token) {
64 return $this->createDaemon($token);
65 }
66
67 public function getDaemon() {
68 return $this->settings->fetch(self::DAEMON_SETTING);
69 }
70
71 public function saveDaemonLastError($error) {
72 $daemon = $this->getDaemon();
73 if ($daemon) {
74 $daemon['last_error'] = $error;
75 $daemon['last_error_date'] = time();
76 $this->saveDaemon($daemon);
77 }
78 }
79
80 public function saveDaemonRunCompleted($runCompletedAt) {
81 $daemon = $this->getDaemon();
82 if ($daemon) {
83 $daemon['run_completed_at'] = $runCompletedAt;
84 $this->saveDaemon($daemon);
85 }
86 }
87
88 public function saveDaemon($daemon) {
89 $daemon['updated_at'] = time();
90 $this->settings->set(
91 self::DAEMON_SETTING,
92 $daemon
93 );
94 }
95
96 public function deactivateDaemon($daemon) {
97 // We do not need to deactivate an inactive daemon
98 if (isset($daemon['status']) && $daemon['status'] === self::DAEMON_STATUS_INACTIVE) {
99 return;
100 }
101 $daemon['status'] = self::DAEMON_STATUS_INACTIVE;
102 $this->settings->set(
103 self::DAEMON_SETTING,
104 $daemon
105 );
106 }
107
108 public function createToken() {
109 return Security::generateRandomString();
110 }
111
112 public function pingDaemon() {
113 $url = $this->getCronUrl(
114 CronDaemonEndpoint::ACTION_PING_RESPONSE
115 );
116 $result = $this->queryCronUrl($url);
117 if (is_wp_error($result)) return $result->get_error_message();
118 $response = $this->wp->wpRemoteRetrieveBody($result);
119 $response = substr(trim($response), -strlen(DaemonHttpRunner::PING_SUCCESS_RESPONSE)) === DaemonHttpRunner::PING_SUCCESS_RESPONSE ?
120 DaemonHttpRunner::PING_SUCCESS_RESPONSE :
121 $response;
122 return $response;
123 }
124
125 public function validatePingResponse($response) {
126 return $response === DaemonHttpRunner::PING_SUCCESS_RESPONSE;
127 }
128
129 public function accessDaemon($token) {
130 $data = ['token' => $token];
131 $url = $this->getCronUrl(
132 CronDaemonEndpoint::ACTION_RUN,
133 $data
134 );
135 $daemon = $this->getDaemon();
136 if (!$daemon) {
137 throw new \LogicException('Daemon does not exist.');
138 }
139 $daemon['run_accessed_at'] = time();
140 $this->saveDaemon($daemon);
141 $result = $this->queryCronUrl($url);
142 return $this->wp->wpRemoteRetrieveBody($result);
143 }
144
145 /**
146 * @return bool|null
147 */
148 public function isDaemonAccessible() {
149 $daemon = $this->getDaemon();
150 if (!$daemon || !isset($daemon['run_accessed_at'])) {
151 return null;
152 }
153 if ($daemon['run_accessed_at'] <= (int)$daemon['run_started_at']) {
154 return true;
155 }
156 if (
157 $daemon['run_accessed_at'] + self::DAEMON_REQUEST_TIMEOUT < time() &&
158 $daemon['run_accessed_at'] > (int)$daemon['run_started_at']
159 ) {
160 return false;
161 }
162 return null;
163 }
164
165 public function queryCronUrl($url) {
166 $defaultArgs = [
167 'blocking' => true,
168 'sslverify' => false,
169 'timeout' => self::DAEMON_REQUEST_TIMEOUT,
170 'user-agent' => 'MailPoet Cron',
171 ];
172 $args = $this->wp->applyFilters('mailpoet_cron_request_args', $defaultArgs);
173 return $this->wp->wpRemotePost($url, is_array($args) ? $args : $defaultArgs);
174 }
175
176 public function getCronUrl($action, $data = false) {
177 $url = Router::buildRequest(
178 CronDaemonEndpoint::ENDPOINT,
179 $action,
180 $data
181 );
182 $url = is_string($url) ? $url : '';
183 $customCronUrl = $this->wp->applyFilters('mailpoet_cron_request_url', $url);
184 if ($customCronUrl === $url) {
185 return str_replace(home_url(), $this->getSiteUrl(), $url);
186 }
187 return is_string($customCronUrl) ? $customCronUrl : $url;
188 }
189
190 public function getSiteUrl($siteUrl = false) {
191 // additional check for some sites running inside a virtual machine or behind
192 // proxy where there could be different ports (e.g., host:8080 => guest:80)
193 if (!$siteUrl) {
194 $siteUrl = defined('MAILPOET_CRON_SITE_URL') ? MAILPOET_CRON_SITE_URL : $this->wp->homeUrl();
195 }
196 $parsedUrl = parse_url($siteUrl);
197 if (!is_array($parsedUrl)) {
198 throw new \Exception(__('Site URL is unreachable.', 'mailpoet'));
199 }
200
201 $callScheme = '';
202 if (isset($parsedUrl['scheme']) && ($parsedUrl['scheme'] === 'https')) {
203 $callScheme = 'ssl://';
204 }
205
206 // 1. if site URL does not contain a port, return the URL
207 if (!isset($parsedUrl['port']) || empty($parsedUrl['port'])) return $siteUrl;
208 // 2. if site URL contains valid port, try connecting to it
209 $urlHost = $parsedUrl['host'] ?? '';
210 $fp = @fsockopen($callScheme . $urlHost, $parsedUrl['port'], $errno, $errstr, 2);
211 if ($fp) return $siteUrl;
212 // 3. if connection fails, attempt to connect the standard port derived from URL
213 // schema
214 $urlScheme = $parsedUrl['scheme'] ?? '';
215 $port = (strtolower($urlScheme) === 'http') ? 80 : 443;
216 $fp = @fsockopen($callScheme . $urlHost, $port, $errno, $errstr, 2);
217 if ($fp) return sprintf('%s://%s', $urlScheme, $urlHost);
218 // 4. throw an error if all connection attempts failed
219 throw new \Exception(__('Site URL is unreachable.', 'mailpoet'));
220 }
221
222 public function enforceExecutionLimit($timer) {
223 $elapsedTime = microtime(true) - $timer;
224 $limit = $this->getDaemonExecutionLimit();
225 if ($elapsedTime >= $limit) {
226 throw new \Exception(
227 sprintf(
228 // translators: %1$d is the number of seconds the daemon is allowed to run, %2$d is how many more seconds the daemon did run.
229 __(
230 'The maximum execution time of %1$d seconds was exceeded by %2$d seconds. This task will resume during the next run.',
231 'mailpoet'
232 ),
233 (int)round($limit),
234 (int)round($elapsedTime - $limit)
235 ),
236 self::DAEMON_EXECUTION_LIMIT_REACHED
237 );
238 }
239 }
240 }
241