PluginProbe
Elementor Website Builder – more than just a page builder / 3.0.4
Elementor Website Builder – more than just a page builder v3.0.4
4.3.0-beta3 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 All 452 releases
← All changes | includes/libraries/wp-background-process/wp-background-process.php +503 -2 4.1.0-dev23.0.4 View file →
@@ -3,10 +3,511 @@
3 3 if ( ! defined( 'ABSPATH' ) ) {
4 4 exit;
5 5 }
6 6
7 -// TODO: _deprecated_file( __FILE__, '3.0.7', '\Elementor\Core\Base\BackgroundProcess\WP_Background_Process' );
7 +/**
8 + * https://github.com/A5hleyRich/wp-background-processing GPL v2.0
9 + *
10 + * WP Background Process
11 + *
12 + * @package WP-Background-Processing
13 + */
8 14
9 15 if ( ! class_exists( 'WP_Background_Process' ) ) {
10 - abstract class WP_Background_Process extends \Elementor\Core\Base\BackgroundProcess\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 +
11 512 }
12 513 }