PluginProbe
Elementor Website Builder – more than just a page builder / 3.30.3
Elementor Website Builder – more than just a page builder v3.30.3
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 / core / base / background-task.php

background-task.php in Elementor Website Builder – more than just a page builder 3.30.3, at core/base/background-task.php

390 lines 8.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Based on https://github.com/woocommerce/woocommerce/blob/master/includes/abstracts/class-wc-background-process.php
4 * & https://github.com/woocommerce/woocommerce/blob/master/includes/class-wc-background-updater.php
5 *
6 * @package Elementor\Core\Base
7 */
8
9 namespace Elementor\Core\Base;
10
11 use Elementor\Plugin;
12 use Elementor\Core\Base\BackgroundProcess\WP_Background_Process;
13
14
15 defined( 'ABSPATH' ) || exit;
16
17 /**
18 * WC_Background_Process class.
19 */
20 abstract class Background_Task extends WP_Background_Process {
21 protected $current_item;
22
23 /**
24 * Dispatch updater.
25 *
26 * Updater will still run via cron job if this fails for any reason.
27 */
28 public function dispatch() {
29 $dispatched = parent::dispatch();
30
31 if ( is_wp_error( $dispatched ) ) {
32 wp_die( esc_html( $dispatched ) );
33 }
34 }
35
36 public function query_col( $sql ) {
37 global $wpdb;
38
39 // Add Calc.
40 $item = $this->get_current_item();
41 if ( empty( $item['total'] ) ) {
42 $sql = preg_replace( '/^SELECT/', 'SELECT SQL_CALC_FOUND_ROWS', $sql );
43 }
44
45 // Add offset & limit.
46 $sql = preg_replace( '/;$/', '', $sql );
47 $sql .= ' LIMIT %d, %d;';
48
49 $results = $wpdb->get_col( $wpdb->prepare( $sql, $this->get_current_offset(), $this->get_limit() ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
50
51 if ( ! empty( $results ) ) {
52 $this->set_total();
53 }
54
55 return $results;
56 }
57
58 public function should_run_again( $updated_rows ) {
59 return count( $updated_rows ) === $this->get_limit();
60 }
61
62 public function get_current_offset() {
63 $limit = $this->get_limit();
64 return ( $this->current_item['iterate_num'] - 1 ) * $limit;
65 }
66
67 public function get_limit() {
68 return $this->manager->get_query_limit();
69 }
70
71 public function set_total() {
72 global $wpdb;
73
74 if ( empty( $this->current_item['total'] ) ) {
75 $total_rows = $wpdb->get_var( 'SELECT FOUND_ROWS();' );
76 $total_iterates = ceil( $total_rows / $this->get_limit() );
77 $this->current_item['total'] = $total_iterates;
78 }
79 }
80
81 /**
82 * Complete
83 *
84 * Override if applicable, but ensure that the below actions are
85 * performed, or, call parent::complete().
86 */
87 protected function complete() {
88 $this->manager->on_runner_complete( true );
89
90 parent::complete();
91 }
92
93 public function continue_run() {
94 // Used to fire an action added in WP_Background_Process::_construct() that calls WP_Background_Process::handle_cron_healthcheck().
95 // This method will make sure the database updates are executed even if cron is disabled. Nothing will happen if the updates are already running.
96 do_action( $this->cron_hook_identifier );
97 }
98
99 /**
100 * @return mixed
101 */
102 public function get_current_item() {
103 return $this->current_item;
104 }
105
106 /**
107 * Get batch.
108 *
109 * @return \stdClass Return the first batch from the queue.
110 */
111 protected function get_batch() {
112 $batch = parent::get_batch();
113 $batch->data = array_filter( (array) $batch->data );
114
115 return $batch;
116 }
117
118 /**
119 * Handle cron healthcheck
120 *
121 * Restart the background process if not already running
122 * and data exists in the queue.
123 */
124 public function handle_cron_healthcheck() {
125 if ( $this->is_process_running() ) {
126 // Background process already running.
127 return;
128 }
129
130 if ( $this->is_queue_empty() ) {
131 // No data to process.
132 $this->clear_scheduled_event();
133 return;
134 }
135
136 $this->handle();
137 }
138
139 /**
140 * Schedule fallback event.
141 */
142 protected function schedule_event() {
143 if ( ! wp_next_scheduled( $this->cron_hook_identifier ) ) {
144 wp_schedule_event( time() + 10, $this->cron_interval_identifier, $this->cron_hook_identifier );
145 }
146 }
147
148 /**
149 * Is the updater running?
150 *
151 * @return boolean
152 */
153 public function is_running() {
154 return false === $this->is_queue_empty();
155 }
156
157 /**
158 * See if the batch limit has been exceeded.
159 *
160 * @return bool
161 */
162 protected function batch_limit_exceeded() {
163 return $this->time_exceeded() || $this->memory_exceeded();
164 }
165
166 /**
167 * Handle.
168 *
169 * Pass each queue item to the task handler, while remaining
170 * within server memory and time limit constraints.
171 */
172 protected function handle() {
173 $this->manager->on_runner_start();
174
175 $this->lock_process();
176
177 do {
178 $batch = $this->get_batch();
179
180 foreach ( $batch->data as $key => $value ) {
181 $task = $this->task( $value );
182
183 if ( false !== $task ) {
184 $batch->data[ $key ] = $task;
185 } else {
186 unset( $batch->data[ $key ] );
187 }
188
189 if ( $this->batch_limit_exceeded() ) {
190 // Batch limits reached.
191 break;
192 }
193 }
194
195 // Update or delete current batch.
196 if ( ! empty( $batch->data ) ) {
197 $this->update( $batch->key, $batch->data );
198 } else {
199 $this->delete( $batch->key );
200 }
201 } while ( ! $this->batch_limit_exceeded() && ! $this->is_queue_empty() );
202
203 $this->unlock_process();
204
205 // Start next batch or complete process.
206 if ( ! $this->is_queue_empty() ) {
207 $this->dispatch();
208 } else {
209 $this->complete();
210 }
211 }
212
213 /**
214 * Use the protected `is_process_running` method as a public method.
215 *
216 * @return bool
217 */
218 public function is_process_locked() {
219 return $this->is_process_running();
220 }
221
222 public function handle_immediately( $callbacks ) {
223 $this->manager->on_runner_start();
224
225 $this->lock_process();
226
227 foreach ( $callbacks as $callback ) {
228 $item = [
229 'callback' => $callback,
230 ];
231
232 do {
233 $item = $this->task( $item );
234 } while ( $item );
235 }
236
237 $this->unlock_process();
238 }
239
240 /**
241 * Task
242 *
243 * Override this method to perform any actions required on each
244 * queue item. Return the modified item for further processing
245 * in the next pass through. Or, return false to remove the
246 * item from the queue.
247 *
248 * @param array $item
249 *
250 * @return array|bool
251 */
252 protected function task( $item ) {
253 $result = false;
254
255 if ( ! isset( $item['iterate_num'] ) ) {
256 $item['iterate_num'] = 1;
257 }
258
259 $logger = Plugin::$instance->logger->get_logger();
260 $callback = $this->format_callback_log( $item );
261
262 if ( is_callable( $item['callback'] ) ) {
263 $progress = '';
264
265 if ( 1 < $item['iterate_num'] ) {
266 if ( empty( $item['total'] ) ) {
267 $progress = sprintf( '(x%s)', $item['iterate_num'] );
268 } else {
269 $percent = ceil( $item['iterate_num'] / ( $item['total'] / 100 ) );
270 $progress = sprintf( '(%s of %s, %s%%)', $item['iterate_num'], $item['total'], $percent );
271 }
272 }
273
274 $logger->info( sprintf( '%s Start %s', $callback, $progress ) );
275
276 $this->current_item = $item;
277
278 $result = (bool) call_user_func( $item['callback'], $this );
279
280 // get back the updated item.
281 $item = $this->current_item;
282 $this->current_item = null;
283
284 if ( $result ) {
285 if ( empty( $item['total'] ) ) {
286 $logger->info( sprintf( '%s callback needs to run again', $callback ) );
287 } elseif ( 1 === $item['iterate_num'] ) {
288 $logger->info( sprintf( '%s callback needs to run more %d times', $callback, $item['total'] - $item['iterate_num'] ) );
289 }
290
291 ++$item['iterate_num'];
292 } else {
293 $logger->info( sprintf( '%s Finished', $callback ) );
294 }
295 } else {
296 $logger->notice( sprintf( 'Could not find %s callback', $callback ) );
297 }
298
299 return $result ? $item : false;
300 }
301
302 /**
303 * Schedule cron healthcheck.
304 *
305 * @param array $schedules Schedules.
306 * @return array
307 */
308 public function schedule_cron_healthcheck( $schedules ) {
309 $interval = apply_filters( $this->identifier . '_cron_interval', 5 );
310
311 // Adds every 5 minutes to the existing schedules.
312 $schedules[ $this->identifier . '_cron_interval' ] = [
313 'interval' => MINUTE_IN_SECONDS * $interval,
314 'display' => sprintf(
315 /* translators: %d: Interval in minutes. */
316 esc_html__( 'Every %d minutes', 'elementor' ),
317 $interval
318 ),
319 ];
320
321 return $schedules;
322 }
323
324 /**
325 * See if the batch limit has been exceeded.
326 *
327 * @return bool
328 */
329 public function is_memory_exceeded() {
330 return $this->memory_exceeded();
331 }
332
333 /**
334 * Delete all batches.
335 *
336 * @return self
337 */
338 public function delete_all_batches() {
339 global $wpdb;
340
341 $table = $wpdb->options;
342 $column = 'option_name';
343
344 if ( is_multisite() ) {
345 $table = $wpdb->sitemeta;
346 $column = 'meta_key';
347 }
348
349 $key = $wpdb->esc_like( $this->identifier . '_batch_' ) . '%';
350
351 $wpdb->query( $wpdb->prepare( "DELETE FROM {$table} WHERE {$column} LIKE %s", $key ) ); // @codingStandardsIgnoreLine.
352
353 return $this;
354 }
355
356 /**
357 * Kill process.
358 *
359 * Stop processing queue items, clear cronjob and delete all batches.
360 */
361 public function kill_process() {
362 if ( ! $this->is_queue_empty() ) {
363 $this->delete_all_batches();
364 wp_clear_scheduled_hook( $this->cron_hook_identifier );
365 }
366 }
367
368 public function set_current_item( $item ) {
369 $this->current_item = $item;
370 }
371
372 protected function format_callback_log( $item ) {
373 return implode( '::', (array) $item['callback'] );
374 }
375
376 /**
377 * @var \Elementor\Core\Base\Background_Task_Manager
378 */
379 protected $manager;
380
381 public function __construct( $manager ) {
382 $this->manager = $manager;
383 // Uses unique prefix per blog so each blog has separate queue.
384 $this->prefix = 'elementor_' . get_current_blog_id();
385 $this->action = $this->manager->get_action();
386
387 parent::__construct();
388 }
389 }
390