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

386 lines 9.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Elementor\Core\Base;
4
5 use Elementor\Plugin;
6
7 /**
8 * Based on https://github.com/woocommerce/woocommerce/blob/master/includes/abstracts/class-wc-background-process.php
9 * & https://github.com/woocommerce/woocommerce/blob/master/includes/class-wc-background-updater.php
10 */
11
12 defined( 'ABSPATH' ) || exit;
13
14 include_once ELEMENTOR_PATH . '/includes/libraries/wp-background-process/wp-async-request.php';
15 include_once ELEMENTOR_PATH . '/includes/libraries/wp-background-process/wp-background-process.php';
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( $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 * @return bool
216 */
217 public function is_process_locked() {
218 return $this->is_process_running();
219 }
220
221 public function handle_immediately( $callbacks ) {
222 $this->manager->on_runner_start();
223
224 $this->lock_process();
225
226 foreach ( $callbacks as $callback ) {
227 $item = [
228 'callback' => $callback,
229 ];
230
231 do {
232 $item = $this->task( $item );
233 } while ( $item );
234 }
235
236 $this->unlock_process();
237 }
238
239 /**
240 * Task
241 *
242 * Override this method to perform any actions required on each
243 * queue item. Return the modified item for further processing
244 * in the next pass through. Or, return false to remove the
245 * item from the queue.
246 *
247 * @param array $item
248 *
249 * @return array|bool
250 */
251 protected function task( $item ) {
252 $result = false;
253
254 if ( ! isset( $item['iterate_num'] ) ) {
255 $item['iterate_num'] = 1;
256 }
257
258 $logger = Plugin::$instance->logger->get_logger();
259 $callback = $this->format_callback_log( $item );
260
261 if ( is_callable( $item['callback'] ) ) {
262 $progress = '';
263
264 if ( 1 < $item['iterate_num'] ) {
265 if ( empty( $item['total'] ) ) {
266 $progress = sprintf( '(x%s)', $item['iterate_num'] );
267 } else {
268 $percent = ceil( $item['iterate_num'] / ( $item['total'] / 100 ) );
269 $progress = sprintf( '(%s of %s, %s%%)', $item['iterate_num'], $item['total'], $percent );
270 }
271 }
272
273 $logger->info( sprintf( '%s Start %s', $callback, $progress ) );
274
275 $this->current_item = $item;
276
277 $result = (bool) call_user_func( $item['callback'], $this );
278
279 // get back the updated item.
280 $item = $this->current_item;
281 $this->current_item = null;
282
283 if ( $result ) {
284 if ( empty( $item['total'] ) ) {
285 $logger->info( sprintf( '%s callback needs to run again', $callback ) );
286 } elseif ( 1 === $item['iterate_num'] ) {
287 $logger->info( sprintf( '%s callback needs to run more %d times', $callback, $item['total'] - $item['iterate_num'] ) );
288 }
289
290 $item['iterate_num']++;
291 } else {
292 $logger->info( sprintf( '%s Finished', $callback ) );
293 }
294 } else {
295 $logger->notice( sprintf( 'Could not find %s callback', $callback ) );
296 }
297
298 return $result ? $item : false;
299 }
300
301 /**
302 * Schedule cron healthcheck.
303 *
304 * @param array $schedules Schedules.
305 * @return array
306 */
307 public function schedule_cron_healthcheck( $schedules ) {
308 $interval = apply_filters( $this->identifier . '_cron_interval', 5 );
309
310 // Adds every 5 minutes to the existing schedules.
311 $schedules[ $this->identifier . '_cron_interval' ] = array(
312 'interval' => MINUTE_IN_SECONDS * $interval,
313 /* translators: %d: interval */
314 'display' => sprintf( __( 'Every %d minutes', 'elementor' ), $interval ),
315 );
316
317 return $schedules;
318 }
319
320 /**
321 * See if the batch limit has been exceeded.
322 *
323 * @return bool
324 */
325 public function is_memory_exceeded() {
326 return $this->memory_exceeded();
327 }
328
329 /**
330 * Delete all batches.
331 *
332 * @return self
333 */
334 public function delete_all_batches() {
335 global $wpdb;
336
337 $table = $wpdb->options;
338 $column = 'option_name';
339
340 if ( is_multisite() ) {
341 $table = $wpdb->sitemeta;
342 $column = 'meta_key';
343 }
344
345 $key = $wpdb->esc_like( $this->identifier . '_batch_' ) . '%';
346
347 $wpdb->query( $wpdb->prepare( "DELETE FROM {$table} WHERE {$column} LIKE %s", $key ) ); // @codingStandardsIgnoreLine.
348
349 return $this;
350 }
351
352 /**
353 * Kill process.
354 *
355 * Stop processing queue items, clear cronjob and delete all batches.
356 */
357 public function kill_process() {
358 if ( ! $this->is_queue_empty() ) {
359 $this->delete_all_batches();
360 wp_clear_scheduled_hook( $this->cron_hook_identifier );
361 }
362 }
363
364 public function set_current_item( $item ) {
365 $this->current_item = $item;
366 }
367
368 protected function format_callback_log( $item ) {
369 return implode( '::', (array) $item['callback'] );
370 }
371
372 /**
373 * @var \Elementor\Core\Base\Background_Task_Manager
374 */
375 protected $manager;
376
377 public function __construct( $manager ) {
378 $this->manager = $manager;
379 // Uses unique prefix per blog so each blog has separate queue.
380 $this->prefix = 'elementor_' . get_current_blog_id();
381 $this->action = $this->manager->get_action();
382
383 parent::__construct();
384 }
385 }
386