PluginProbe ʕ •ᴥ•ʔ
WP Mail SMTP by WPForms – The Most Popular SMTP and Email Log Plugin / 3.11.0
WP Mail SMTP by WPForms – The Most Popular SMTP and Email Log Plugin v3.11.0
4.9.0 0.9.6 1.0.0 1.0.1 1.0.2 1.1.0 1.2.0 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.3.0 1.3.1 1.3.2 1.3.3 1.4.0 1.4.1 1.4.2 1.5.0 1.5.1 1.5.2 1.6.0 1.6.2 1.7.0 1.7.1 1.8.0 1.8.1 1.9.0 2.0.0 2.0.1 2.1.1 2.2.1 2.3.1 2.4.0 2.5.0 2.5.1 2.6.0 2.7.0 2.8.0 2.9.0 3.0.1 3.0.2 3.0.3 3.1.0 3.10.0 3.11.0 3.11.1 3.2.0 3.2.1 3.3.0 3.4.0 3.5.0 3.5.1 3.5.2 3.6.1 3.7.0 3.8.0 3.8.2 3.9.0 4.0.1 4.1.0 4.1.1 4.2.0 4.3.0 4.4.0 4.5.0 4.6.0 4.7.0 4.7.1 4.8.0 trunk 0.10.0 0.10.1 0.11.1 0.11.2 0.3.1 0.3.2 0.4 0.4.1 0.4.2 0.5.0 0.5.1 0.5.2 0.6 0.7 0.8 0.8.2 0.8.3 0.8.4 0.8.5 0.8.6 0.8.7 0.9.0 0.9.1 0.9.2 0.9.3 0.9.4 0.9.5
wp-mail-smtp / vendor / woocommerce / action-scheduler / classes / abstracts / ActionScheduler_Abstract_QueueRunner.php
wp-mail-smtp / vendor / woocommerce / action-scheduler / classes / abstracts Last commit date
ActionScheduler.php 2 years ago ActionScheduler_Abstract_ListTable.php 2 years ago ActionScheduler_Abstract_QueueRunner.php 2 years ago ActionScheduler_Abstract_RecurringSchedule.php 2 years ago ActionScheduler_Abstract_Schedule.php 2 years ago ActionScheduler_Abstract_Schema.php 2 years ago ActionScheduler_Lock.php 2 years ago ActionScheduler_Logger.php 2 years ago ActionScheduler_Store.php 2 years ago ActionScheduler_TimezoneHelper.php 2 years ago
ActionScheduler_Abstract_QueueRunner.php
373 lines
1 <?php
2
3 /**
4 * Abstract class with common Queue Cleaner functionality.
5 */
6 abstract class ActionScheduler_Abstract_QueueRunner extends ActionScheduler_Abstract_QueueRunner_Deprecated {
7
8 /** @var ActionScheduler_QueueCleaner */
9 protected $cleaner;
10
11 /** @var ActionScheduler_FatalErrorMonitor */
12 protected $monitor;
13
14 /** @var ActionScheduler_Store */
15 protected $store;
16
17 /**
18 * The created time.
19 *
20 * Represents when the queue runner was constructed and used when calculating how long a PHP request has been running.
21 * For this reason it should be as close as possible to the PHP request start time.
22 *
23 * @var int
24 */
25 private $created_time;
26
27 /**
28 * ActionScheduler_Abstract_QueueRunner constructor.
29 *
30 * @param ActionScheduler_Store $store
31 * @param ActionScheduler_FatalErrorMonitor $monitor
32 * @param ActionScheduler_QueueCleaner $cleaner
33 */
34 public function __construct( ActionScheduler_Store $store = null, ActionScheduler_FatalErrorMonitor $monitor = null, ActionScheduler_QueueCleaner $cleaner = null ) {
35
36 $this->created_time = microtime( true );
37
38 $this->store = $store ? $store : ActionScheduler_Store::instance();
39 $this->monitor = $monitor ? $monitor : new ActionScheduler_FatalErrorMonitor( $this->store );
40 $this->cleaner = $cleaner ? $cleaner : new ActionScheduler_QueueCleaner( $this->store );
41 }
42
43 /**
44 * Process an individual action.
45 *
46 * @param int $action_id The action ID to process.
47 * @param string $context Optional identifer for the context in which this action is being processed, e.g. 'WP CLI' or 'WP Cron'
48 * Generally, this should be capitalised and not localised as it's a proper noun.
49 */
50 public function process_action( $action_id, $context = '' ) {
51 // Temporarily override the error handler while we process the current action.
52 set_error_handler(
53 /**
54 * Temporary error handler which can catch errors and convert them into exceptions. This faciliates more
55 * robust error handling across all supported PHP versions.
56 *
57 * @throws Exception
58 *
59 * @param int $type Error level expressed as an integer.
60 * @param string $message Error message.
61 */
62 function ( $type, $message ) {
63 throw new Exception( $message );
64 },
65 E_USER_ERROR | E_RECOVERABLE_ERROR
66 );
67
68 /*
69 * The nested try/catch structure is required because we potentially need to convert thrown errors into
70 * exceptions (and an exception thrown from a catch block cannot be caught by a later catch block in the *same*
71 * structure).
72 */
73 try {
74 try {
75 $valid_action = false;
76 do_action( 'action_scheduler_before_execute', $action_id, $context );
77
78 if ( ActionScheduler_Store::STATUS_PENDING !== $this->store->get_status( $action_id ) ) {
79 do_action( 'action_scheduler_execution_ignored', $action_id, $context );
80 return;
81 }
82
83 $valid_action = true;
84 do_action( 'action_scheduler_begin_execute', $action_id, $context );
85
86 $action = $this->store->fetch_action( $action_id );
87 $this->store->log_execution( $action_id );
88 $action->execute();
89 do_action( 'action_scheduler_after_execute', $action_id, $action, $context );
90 $this->store->mark_complete( $action_id );
91 } catch ( Throwable $e ) {
92 // Throwable is defined when executing under PHP 7.0 and up. We convert it to an exception, for
93 // compatibility with ActionScheduler_Logger.
94 throw new Exception( $e->getMessage(), $e->getCode(), $e->getPrevious() );
95 }
96 } catch ( Exception $e ) {
97 // This catch block exists for compatibility with PHP 5.6.
98 $this->handle_action_error( $action_id, $e, $context, $valid_action );
99 } finally {
100 restore_error_handler();
101 }
102
103 if ( isset( $action ) && is_a( $action, 'ActionScheduler_Action' ) && $action->get_schedule()->is_recurring() ) {
104 $this->schedule_next_instance( $action, $action_id );
105 }
106 }
107
108 /**
109 * Marks actions as either having failed execution or failed validation, as appropriate.
110 *
111 * @param int $action_id Action ID.
112 * @param Exception $e Exception instance.
113 * @param string $context Execution context.
114 * @param bool $valid_action If the action is valid.
115 *
116 * @return void
117 */
118 private function handle_action_error( $action_id, $e, $context, $valid_action ) {
119 if ( $valid_action ) {
120 $this->store->mark_failure( $action_id );
121 /**
122 * Runs when action execution fails.
123 *
124 * @param int $action_id Action ID.
125 * @param Exception $e Exception instance.
126 * @param string $context Execution context.
127 */
128 do_action( 'action_scheduler_failed_execution', $action_id, $e, $context );
129 } else {
130 /**
131 * Runs when action validation fails.
132 *
133 * @param int $action_id Action ID.
134 * @param Exception $e Exception instance.
135 * @param string $context Execution context.
136 */
137 do_action( 'action_scheduler_failed_validation', $action_id, $e, $context );
138 }
139 }
140
141 /**
142 * Schedule the next instance of the action if necessary.
143 *
144 * @param ActionScheduler_Action $action
145 * @param int $action_id
146 */
147 protected function schedule_next_instance( ActionScheduler_Action $action, $action_id ) {
148 // If a recurring action has been consistently failing, we may wish to stop rescheduling it.
149 if (
150 ActionScheduler_Store::STATUS_FAILED === $this->store->get_status( $action_id )
151 && $this->recurring_action_is_consistently_failing( $action, $action_id )
152 ) {
153 ActionScheduler_Logger::instance()->log(
154 $action_id,
155 __( 'This action appears to be consistently failing. A new instance will not be scheduled.', 'action-scheduler' )
156 );
157
158 return;
159 }
160
161 try {
162 ActionScheduler::factory()->repeat( $action );
163 } catch ( Exception $e ) {
164 do_action( 'action_scheduler_failed_to_schedule_next_instance', $action_id, $e, $action );
165 }
166 }
167
168 /**
169 * Determine if the specified recurring action has been consistently failing.
170 *
171 * @param ActionScheduler_Action $action The recurring action to be rescheduled.
172 * @param int $action_id The ID of the recurring action.
173 *
174 * @return bool
175 */
176 private function recurring_action_is_consistently_failing( ActionScheduler_Action $action, $action_id ) {
177 /**
178 * Controls the failure threshold for recurring actions.
179 *
180 * Before rescheduling a recurring action, we look at its status. If it failed, we then check if all of the most
181 * recent actions (upto the threshold set by this filter) sharing the same hook have also failed: if they have,
182 * that is considered consistent failure and a new instance of the action will not be scheduled.
183 *
184 * @param int $failure_threshold Number of actions of the same hook to examine for failure. Defaults to 5.
185 */
186 $consistent_failure_threshold = (int) apply_filters( 'action_scheduler_recurring_action_failure_threshold', 5 );
187
188 // This query should find the earliest *failing* action (for the hook we are interested in) within our threshold.
189 $query_args = array(
190 'hook' => $action->get_hook(),
191 'status' => ActionScheduler_Store::STATUS_FAILED,
192 'date' => date_create( 'now', timezone_open( 'UTC' ) )->format( 'Y-m-d H:i:s' ),
193 'date_compare' => '<',
194 'per_page' => 1,
195 'offset' => $consistent_failure_threshold - 1
196 );
197
198 $first_failing_action_id = $this->store->query_actions( $query_args );
199
200 // If we didn't retrieve an action ID, then there haven't been enough failures for us to worry about.
201 if ( empty( $first_failing_action_id ) ) {
202 return false;
203 }
204
205 // Now let's fetch the first action (having the same hook) of *any status* within the same window.
206 unset( $query_args['status'] );
207 $first_action_id_with_the_same_hook = $this->store->query_actions( $query_args );
208
209 /**
210 * If a recurring action is assessed as consistently failing, it will not be rescheduled. This hook provides a
211 * way to observe and optionally override that assessment.
212 *
213 * @param bool $is_consistently_failing If the action is considered to be consistently failing.
214 * @param ActionScheduler_Action $action The action being assessed.
215 */
216 return (bool) apply_filters(
217 'action_scheduler_recurring_action_is_consistently_failing',
218 $first_action_id_with_the_same_hook === $first_failing_action_id,
219 $action
220 );
221 }
222
223 /**
224 * Run the queue cleaner.
225 *
226 * @author Jeremy Pry
227 */
228 protected function run_cleanup() {
229 $this->cleaner->clean( 10 * $this->get_time_limit() );
230 }
231
232 /**
233 * Get the number of concurrent batches a runner allows.
234 *
235 * @return int
236 */
237 public function get_allowed_concurrent_batches() {
238 return apply_filters( 'action_scheduler_queue_runner_concurrent_batches', 1 );
239 }
240
241 /**
242 * Check if the number of allowed concurrent batches is met or exceeded.
243 *
244 * @return bool
245 */
246 public function has_maximum_concurrent_batches() {
247 return $this->store->get_claim_count() >= $this->get_allowed_concurrent_batches();
248 }
249
250 /**
251 * Get the maximum number of seconds a batch can run for.
252 *
253 * @return int The number of seconds.
254 */
255 protected function get_time_limit() {
256
257 $time_limit = 30;
258
259 // Apply deprecated filter from deprecated get_maximum_execution_time() method
260 if ( has_filter( 'action_scheduler_maximum_execution_time' ) ) {
261 _deprecated_function( 'action_scheduler_maximum_execution_time', '2.1.1', 'action_scheduler_queue_runner_time_limit' );
262 $time_limit = apply_filters( 'action_scheduler_maximum_execution_time', $time_limit );
263 }
264
265 return absint( apply_filters( 'action_scheduler_queue_runner_time_limit', $time_limit ) );
266 }
267
268 /**
269 * Get the number of seconds the process has been running.
270 *
271 * @return int The number of seconds.
272 */
273 protected function get_execution_time() {
274 $execution_time = microtime( true ) - $this->created_time;
275
276 // Get the CPU time if the hosting environment uses it rather than wall-clock time to calculate a process's execution time.
277 if ( function_exists( 'getrusage' ) && apply_filters( 'action_scheduler_use_cpu_execution_time', defined( 'PANTHEON_ENVIRONMENT' ) ) ) {
278 $resource_usages = getrusage();
279
280 if ( isset( $resource_usages['ru_stime.tv_usec'], $resource_usages['ru_stime.tv_usec'] ) ) {
281 $execution_time = $resource_usages['ru_stime.tv_sec'] + ( $resource_usages['ru_stime.tv_usec'] / 1000000 );
282 }
283 }
284
285 return $execution_time;
286 }
287
288 /**
289 * Check if the host's max execution time is (likely) to be exceeded if processing more actions.
290 *
291 * @param int $processed_actions The number of actions processed so far - used to determine the likelihood of exceeding the time limit if processing another action
292 * @return bool
293 */
294 protected function time_likely_to_be_exceeded( $processed_actions ) {
295 $execution_time = $this->get_execution_time();
296 $max_execution_time = $this->get_time_limit();
297
298 // Safety against division by zero errors.
299 if ( 0 === $processed_actions ) {
300 return $execution_time >= $max_execution_time;
301 }
302
303 $time_per_action = $execution_time / $processed_actions;
304 $estimated_time = $execution_time + ( $time_per_action * 3 );
305 $likely_to_be_exceeded = $estimated_time > $max_execution_time;
306
307 return apply_filters( 'action_scheduler_maximum_execution_time_likely_to_be_exceeded', $likely_to_be_exceeded, $this, $processed_actions, $execution_time, $max_execution_time );
308 }
309
310 /**
311 * Get memory limit
312 *
313 * Based on WP_Background_Process::get_memory_limit()
314 *
315 * @return int
316 */
317 protected function get_memory_limit() {
318 if ( function_exists( 'ini_get' ) ) {
319 $memory_limit = ini_get( 'memory_limit' );
320 } else {
321 $memory_limit = '128M'; // Sensible default, and minimum required by WooCommerce
322 }
323
324 if ( ! $memory_limit || -1 === $memory_limit || '-1' === $memory_limit ) {
325 // Unlimited, set to 32GB.
326 $memory_limit = '32G';
327 }
328
329 return ActionScheduler_Compatibility::convert_hr_to_bytes( $memory_limit );
330 }
331
332 /**
333 * Memory exceeded
334 *
335 * Ensures the batch process never exceeds 90% of the maximum WordPress memory.
336 *
337 * Based on WP_Background_Process::memory_exceeded()
338 *
339 * @return bool
340 */
341 protected function memory_exceeded() {
342
343 $memory_limit = $this->get_memory_limit() * 0.90;
344 $current_memory = memory_get_usage( true );
345 $memory_exceeded = $current_memory >= $memory_limit;
346
347 return apply_filters( 'action_scheduler_memory_exceeded', $memory_exceeded, $this );
348 }
349
350 /**
351 * See if the batch limits have been exceeded, which is when memory usage is almost at
352 * the maximum limit, or the time to process more actions will exceed the max time limit.
353 *
354 * Based on WC_Background_Process::batch_limits_exceeded()
355 *
356 * @param int $processed_actions The number of actions processed so far - used to determine the likelihood of exceeding the time limit if processing another action
357 * @return bool
358 */
359 protected function batch_limits_exceeded( $processed_actions ) {
360 return $this->memory_exceeded() || $this->time_likely_to_be_exceeded( $processed_actions );
361 }
362
363 /**
364 * Process actions in the queue.
365 *
366 * @author Jeremy Pry
367 * @param string $context Optional identifer for the context in which this action is being processed, e.g. 'WP CLI' or 'WP Cron'
368 * Generally, this should be capitalised and not localised as it's a proper noun.
369 * @return int The number of actions processed.
370 */
371 abstract public function run( $context = '' );
372 }
373