PluginProbe
Elementor Website Builder – more than just a page builder / 3.0.2
Elementor Website Builder – more than just a page builder v3.0.2
4.3.0-beta2 4.3.0-beta1 4.2.4 4.2.3 4.2.2 4.2.1 4.2.0 4.1.5 4.2.0-beta2 4.2.0-dev2 4.2.0-beta1 4.1.4 4.1.3 4.1.2 4.1.1 4.1.0 4.1.0-beta3 4.1.0-dev3 4.0.9 4.1.0-beta2 4.1.0-dev2 4.0.8 4.1.0-beta1 4.1.0-dev1 4.0.7 All 451 releases
elementor / includes / libraries / wp-background-process / wp-background-process.php

wp-background-process.php in Elementor Website Builder – more than just a page builder 3.0.2, at includes/libraries/wp-background-process/wp-background-process.php

514 lines 10.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 if ( ! defined( 'ABSPATH' ) ) {
4 exit;
5 }
6
7 /**
8 * https://github.com/A5hleyRich/wp-background-processing GPL v2.0
9 *
10 * WP Background Process
11 *
12 * @package WP-Background-Processing
13 */
14
15 if ( ! class_exists( 'WP_Background_Process' ) ) {
16
17 /**
18 * Abstract WP_Background_Process class.
19 *
20 * @abstract
21 * @extends WP_Async_Request
22 */
23 abstract class WP_Background_Process extends WP_Async_Request {
24
25 /**
26 * Action
27 *
28 * (default value: 'background_process')
29 *
30 * @var string
31 * @access protected
32 */
33 protected $action = 'background_process';
34
35 /**
36 * Start time of current process.
37 *
38 * (default value: 0)
39 *
40 * @var int
41 * @access protected
42 */
43 protected $start_time = 0;
44
45 /**
46 * Cron_hook_identifier
47 *
48 * @var mixed
49 * @access protected
50 */
51 protected $cron_hook_identifier;
52
53 /**
54 * Cron_interval_identifier
55 *
56 * @var mixed
57 * @access protected
58 */
59 protected $cron_interval_identifier;
60
61 /**
62 * Initiate new background process
63 */
64 public function __construct() {
65 parent::__construct();
66
67 $this->cron_hook_identifier = $this->identifier . '_cron';
68 $this->cron_interval_identifier = $this->identifier . '_cron_interval';
69
70 add_action( $this->cron_hook_identifier, array( $this, 'handle_cron_healthcheck' ) );
71 add_filter( 'cron_schedules', array( $this, 'schedule_cron_healthcheck' ) );
72 }
73
74 /**
75 * Dispatch
76 *
77 * @access public
78 * @return void
79 */
80 public function dispatch() {
81 // Schedule the cron healthcheck.
82 $this->schedule_event();
83
84 // Perform remote post.
85 return parent::dispatch();
86 }
87
88 /**
89 * Push to queue
90 *
91 * @param mixed $data Data.
92 *
93 * @return $this
94 */
95 public function push_to_queue( $data ) {
96 $this->data[] = $data;
97
98 return $this;
99 }
100
101 /**
102 * Save queue
103 *
104 * @return $this
105 */
106 public function save() {
107 $key = $this->generate_key();
108
109 if ( ! empty( $this->data ) ) {
110 update_site_option( $key, $this->data );
111 }
112
113 return $this;
114 }
115
116 /**
117 * Update queue
118 *
119 * @param string $key Key.
120 * @param array $data Data.
121 *
122 * @return $this
123 */
124 public function update( $key, $data ) {
125 if ( ! empty( $data ) ) {
126 update_site_option( $key, $data );
127 }
128
129 return $this;
130 }
131
132 /**
133 * Delete queue
134 *
135 * @param string $key Key.
136 *
137 * @return $this
138 */
139 public function delete( $key ) {
140 delete_site_option( $key );
141
142 return $this;
143 }
144
145 /**
146 * Generate key
147 *
148 * Generates a unique key based on microtime. Queue items are
149 * given a unique key so that they can be merged upon save.
150 *
151 * @param int $length Length.
152 *
153 * @return string
154 */
155 protected function generate_key( $length = 64 ) {
156 $unique = md5( microtime() . rand() );
157 $prepend = $this->identifier . '_batch_';
158
159 return substr( $prepend . $unique, 0, $length );
160 }
161
162 /**
163 * Maybe process queue
164 *
165 * Checks whether data exists within the queue and that
166 * the process is not already running.
167 */
168 public function maybe_handle() {
169 // Don't lock up other requests while processing
170 session_write_close();
171
172 if ( $this->is_process_running() ) {
173 // Background process already running.
174 wp_die();
175 }
176
177 if ( $this->is_queue_empty() ) {
178 // No data to process.
179 wp_die();
180 }
181
182 check_ajax_referer( $this->identifier, 'nonce' );
183
184 $this->handle();
185
186 wp_die();
187 }
188
189 /**
190 * Is queue empty
191 *
192 * @return bool
193 */
194 protected function is_queue_empty() {
195 global $wpdb;
196
197 $table = $wpdb->options;
198 $column = 'option_name';
199
200 if ( is_multisite() ) {
201 $table = $wpdb->sitemeta;
202 $column = 'meta_key';
203 }
204
205 $key = $wpdb->esc_like( $this->identifier . '_batch_' ) . '%';
206
207 $count = $wpdb->get_var( $wpdb->prepare( "
208 SELECT COUNT(*)
209 FROM {$table}
210 WHERE {$column} LIKE %s
211 ", $key ) );
212
213 return ( $count > 0 ) ? false : true;
214 }
215
216 /**
217 * Is process running
218 *
219 * Check whether the current process is already running
220 * in a background process.
221 */
222 protected function is_process_running() {
223 if ( get_site_transient( $this->identifier . '_process_lock' ) ) {
224 // Process already running.
225 return true;
226 }
227
228 return false;
229 }
230
231 /**
232 * Lock process
233 *
234 * Lock the process so that multiple instances can't run simultaneously.
235 * Override if applicable, but the duration should be greater than that
236 * defined in the time_exceeded() method.
237 */
238 protected function lock_process() {
239 $this->start_time = time(); // Set start time of current process.
240
241 $lock_duration = ( property_exists( $this, 'queue_lock_time' ) ) ? $this->queue_lock_time : 60; // 1 minute
242 $lock_duration = apply_filters( $this->identifier . '_queue_lock_time', $lock_duration );
243
244 set_site_transient( $this->identifier . '_process_lock', microtime(), $lock_duration );
245 }
246
247 /**
248 * Unlock process
249 *
250 * Unlock the process so that other instances can spawn.
251 *
252 * @return $this
253 */
254 protected function unlock_process() {
255 delete_site_transient( $this->identifier . '_process_lock' );
256
257 return $this;
258 }
259
260 /**
261 * Get batch
262 *
263 * @return stdClass Return the first batch from the queue
264 */
265 protected function get_batch() {
266 global $wpdb;
267
268 $table = $wpdb->options;
269 $column = 'option_name';
270 $key_column = 'option_id';
271 $value_column = 'option_value';
272
273 if ( is_multisite() ) {
274 $table = $wpdb->sitemeta;
275 $column = 'meta_key';
276 $key_column = 'meta_id';
277 $value_column = 'meta_value';
278 }
279
280 $key = $wpdb->esc_like( $this->identifier . '_batch_' ) . '%';
281
282 $query = $wpdb->get_row( $wpdb->prepare( "
283 SELECT *
284 FROM {$table}
285 WHERE {$column} LIKE %s
286 ORDER BY {$key_column} ASC
287 LIMIT 1
288 ", $key ) );
289
290 $batch = new stdClass();
291 $batch->key = $query->$column;
292 $batch->data = maybe_unserialize( $query->$value_column );
293
294 return $batch;
295 }
296
297 /**
298 * Handle
299 *
300 * Pass each queue item to the task handler, while remaining
301 * within server memory and time limit constraints.
302 */
303 protected function handle() {
304 $this->lock_process();
305
306 do {
307 $batch = $this->get_batch();
308
309 foreach ( $batch->data as $key => $value ) {
310 $task = $this->task( $value );
311
312 if ( false !== $task ) {
313 $batch->data[ $key ] = $task;
314 } else {
315 unset( $batch->data[ $key ] );
316 }
317
318 if ( $this->time_exceeded() || $this->memory_exceeded() ) {
319 // Batch limits reached.
320 break;
321 }
322 }
323
324 // Update or delete current batch.
325 if ( ! empty( $batch->data ) ) {
326 $this->update( $batch->key, $batch->data );
327 } else {
328 $this->delete( $batch->key );
329 }
330 } while ( ! $this->time_exceeded() && ! $this->memory_exceeded() && ! $this->is_queue_empty() );
331
332 $this->unlock_process();
333
334 // Start next batch or complete process.
335 if ( ! $this->is_queue_empty() ) {
336 $this->dispatch();
337 } else {
338 $this->complete();
339 }
340
341 wp_die();
342 }
343
344 /**
345 * Memory exceeded
346 *
347 * Ensures the batch process never exceeds 90%
348 * of the maximum WordPress memory.
349 *
350 * @return bool
351 */
352 protected function memory_exceeded() {
353 $memory_limit = $this->get_memory_limit() * 0.9; // 90% of max memory
354 $current_memory = memory_get_usage( true );
355 $return = false;
356
357 if ( $current_memory >= $memory_limit ) {
358 $return = true;
359 }
360
361 return apply_filters( $this->identifier . '_memory_exceeded', $return );
362 }
363
364 /**
365 * Get memory limit
366 *
367 * @return int
368 */
369 protected function get_memory_limit() {
370 if ( function_exists( 'ini_get' ) ) {
371 $memory_limit = ini_get( 'memory_limit' );
372 } else {
373 // Sensible default.
374 $memory_limit = '128M';
375 }
376
377 if ( ! $memory_limit || -1 === intval( $memory_limit ) ) {
378 // Unlimited, set to 32GB.
379 $memory_limit = '32000M';
380 }
381
382 return intval( $memory_limit ) * 1024 * 1024;
383 }
384
385 /**
386 * Time exceeded.
387 *
388 * Ensures the batch never exceeds a sensible time limit.
389 * A timeout limit of 30s is common on shared hosting.
390 *
391 * @return bool
392 */
393 protected function time_exceeded() {
394 $finish = $this->start_time + apply_filters( $this->identifier . '_default_time_limit', 20 ); // 20 seconds
395 $return = false;
396
397 if ( time() >= $finish ) {
398 $return = true;
399 }
400
401 return apply_filters( $this->identifier . '_time_exceeded', $return );
402 }
403
404 /**
405 * Complete.
406 *
407 * Override if applicable, but ensure that the below actions are
408 * performed, or, call parent::complete().
409 */
410 protected function complete() {
411 // Unschedule the cron healthcheck.
412 $this->clear_scheduled_event();
413 }
414
415 /**
416 * Schedule cron healthcheck
417 *
418 * @access public
419 * @param mixed $schedules Schedules.
420 * @return mixed
421 */
422 public function schedule_cron_healthcheck( $schedules ) {
423 $interval = apply_filters( $this->identifier . '_cron_interval', 5 );
424
425 if ( property_exists( $this, 'cron_interval' ) ) {
426 $interval = apply_filters( $this->identifier . '_cron_interval', $this->cron_interval );
427 }
428
429 // Adds every 5 minutes to the existing schedules.
430 $schedules[ $this->identifier . '_cron_interval' ] = array(
431 'interval' => MINUTE_IN_SECONDS * $interval,
432 'display' => sprintf( __( 'Every %d Minutes', 'elementor' ), $interval ),
433 );
434
435 return $schedules;
436 }
437
438 /**
439 * Handle cron healthcheck
440 *
441 * Restart the background process if not already running
442 * and data exists in the queue.
443 */
444 public function handle_cron_healthcheck() {
445 if ( $this->is_process_running() ) {
446 // Background process already running.
447 exit;
448 }
449
450 if ( $this->is_queue_empty() ) {
451 // No data to process.
452 $this->clear_scheduled_event();
453 exit;
454 }
455
456 $this->handle();
457
458 exit;
459 }
460
461 /**
462 * Schedule event
463 */
464 protected function schedule_event() {
465 if ( ! wp_next_scheduled( $this->cron_hook_identifier ) ) {
466 wp_schedule_event( time(), $this->cron_interval_identifier, $this->cron_hook_identifier );
467 }
468 }
469
470 /**
471 * Clear scheduled event
472 */
473 protected function clear_scheduled_event() {
474 $timestamp = wp_next_scheduled( $this->cron_hook_identifier );
475
476 if ( $timestamp ) {
477 wp_unschedule_event( $timestamp, $this->cron_hook_identifier );
478 }
479 }
480
481 /**
482 * Cancel Process
483 *
484 * Stop processing queue items, clear cronjob and delete batch.
485 *
486 */
487 public function cancel_process() {
488 if ( ! $this->is_queue_empty() ) {
489 $batch = $this->get_batch();
490
491 $this->delete( $batch->key );
492
493 wp_clear_scheduled_hook( $this->cron_hook_identifier );
494 }
495
496 }
497
498 /**
499 * Task
500 *
501 * Override this method to perform any actions required on each
502 * queue item. Return the modified item for further processing
503 * in the next pass through. Or, return false to remove the
504 * item from the queue.
505 *
506 * @param mixed $item Queue item to iterate over.
507 *
508 * @return mixed
509 */
510 abstract protected function task( $item );
511
512 }
513 }
514