PluginProbe ʕ •ᴥ•ʔ
GiveWP – Donation Plugin and Fundraising Platform / 2.5.11
GiveWP – Donation Plugin and Fundraising Platform v2.5.11
4.16.2 4.16.1 4.16.0 4.15.5 4.15.4 4.15.3 4.15.2 4.15.1 4.15.0 2.3.0 2.3.1 2.3.2 2.30.0 2.31.0 2.31.1 2.32.0 2.33.0 2.33.1 2.33.2 2.33.3 2.33.4 2.33.5 2.4.0 2.4.1 2.4.2 2.4.3 2.4.4 2.4.5 2.4.6 2.4.7 2.5.0 2.5.1 2.5.10 2.5.11 2.5.12 2.5.13 2.5.2 2.5.3 2.5.4 2.5.5 2.5.6 2.5.7 2.5.8 2.5.9 2.6.0 2.6.1 2.6.2 2.6.3 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.8.0 2.8.1 2.9.0 2.9.1 2.9.2 2.9.3 2.9.4 2.9.5 2.9.6 2.9.7 3.0.0 3.0.1 3.0.2 3.0.3 3.0.4 3.1.0 3.1.1 3.1.2 3.10.0 3.11.0 3.12.0 3.12.1 3.12.2 3.12.3 3.13.0 3.14.0 3.14.1 3.14.2 3.15.0 3.15.1 3.16.0 3.16.1 3.16.2 3.16.3 3.16.4 3.16.5 3.17.0 3.17.1 3.17.2 3.18.0 3.19.0 3.19.1 3.19.2 3.19.3 3.19.4 3.2.0 3.2.1 3.2.2 3.20.0 3.21.0 3.21.1 3.22.0 3.22.1 3.22.2 3.3.0 3.3.1 3.4.0 3.4.1 3.4.2 3.5.0 3.5.1 3.6.0 3.6.1 3.6.2 3.7.0 3.8.0 3.9.0 4.0.0 4.1.0 4.1.1 4.10.0 4.10.1 4.11.0 4.12.0 4.13.0 4.13.1 4.13.2 4.14.0 4.14.1 4.14.2 4.14.3 4.14.4 4.14.5 4.14.6 4.2.0 4.2.1 4.3.0 4.3.1 4.3.2 4.4.0 4.5.0 4.6.1 4.7.0 4.7.1 4.8.0 4.8.1 4.9.0 trunk 1.9.0 2.0.0 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.1.0 2.1.1 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.10.0 2.10.1 2.10.2 2.10.3 2.10.4 2.11.0 2.11.1 2.11.2 2.11.3 2.12.0 2.12.1 2.12.2 2.12.3 2.13.0 2.13.1 2.13.2 2.13.3 2.13.4 2.14.0 2.15.0 2.16.0 2.16.1 2.17.0 2.17.1 2.17.3 2.18.0 2.18.1 2.19.1 2.19.2 2.19.3 2.19.4 2.19.5 2.19.6 2.19.7 2.19.8 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.2.6 2.20.0 2.20.1 2.20.2 2.21.0 2.21.1 2.21.2 2.21.3 2.21.4 2.22.0 2.22.1 2.22.2 2.22.3 2.23.0 2.23.1 2.23.2 2.24.0 2.24.1 2.24.2 2.25.0 2.25.1 2.25.2 2.25.3 2.26.0 2.27.0 2.27.1 2.27.2 2.27.3 2.28.0 2.29.0 2.29.1 2.29.2
give / includes / libraries / wp-background-process.php
give / includes / libraries Last commit date
googlechartlib 8 years ago tcpdf 7 years ago array2xml.php 8 years ago browser.php 8 years ago give-pdf.php 7 years ago wp-async-request.php 8 years ago wp-background-process.php 8 years ago
wp-background-process.php
507 lines
1 <?php
2 /**
3 * WP Background Process
4 *
5 * @package WP-Background-Processing
6 */
7
8 if ( ! class_exists( 'WP_Background_Process' ) ) {
9
10 /**
11 * Abstract WP_Background_Process class.
12 *
13 * @abstract
14 * @extends WP_Async_Request
15 */
16 abstract class WP_Background_Process extends WP_Async_Request {
17
18 /**
19 * Action
20 *
21 * (default value: 'background_process')
22 *
23 * @var string
24 * @access protected
25 */
26 protected $action = 'background_process';
27
28 /**
29 * Start time of current process.
30 *
31 * (default value: 0)
32 *
33 * @var int
34 * @access protected
35 */
36 protected $start_time = 0;
37
38 /**
39 * Cron_hook_identifier
40 *
41 * @var mixed
42 * @access protected
43 */
44 protected $cron_hook_identifier;
45
46 /**
47 * Cron_interval_identifier
48 *
49 * @var mixed
50 * @access protected
51 */
52 protected $cron_interval_identifier;
53
54 /**
55 * Initiate new background process
56 */
57 public function __construct() {
58 parent::__construct();
59
60 $this->cron_hook_identifier = $this->identifier . '_cron';
61 $this->cron_interval_identifier = $this->identifier . '_cron_interval';
62
63 add_action( $this->cron_hook_identifier, array( $this, 'handle_cron_healthcheck' ) );
64 add_filter( 'cron_schedules', array( $this, 'schedule_cron_healthcheck' ) );
65 }
66
67 /**
68 * Dispatch
69 *
70 * @access public
71 * @return void
72 */
73 public function dispatch() {
74 // Schedule the cron healthcheck.
75 $this->schedule_event();
76
77 // Perform remote post.
78 return parent::dispatch();
79 }
80
81 /**
82 * Push to queue
83 *
84 * @param mixed $data Data.
85 *
86 * @return $this
87 */
88 public function push_to_queue( $data ) {
89 $this->data[] = $data;
90
91 return $this;
92 }
93
94 /**
95 * Save queue
96 *
97 * @return $this
98 */
99 public function save() {
100 $key = $this->generate_key();
101
102 if ( ! empty( $this->data ) ) {
103 update_site_option( $key, $this->data );
104 }
105
106 return $this;
107 }
108
109 /**
110 * Update queue
111 *
112 * @param string $key Key.
113 * @param array $data Data.
114 *
115 * @return $this
116 */
117 public function update( $key, $data ) {
118 if ( ! empty( $data ) ) {
119 update_site_option( $key, $data );
120 }
121
122 return $this;
123 }
124
125 /**
126 * Delete queue
127 *
128 * @param string $key Key.
129 *
130 * @return $this
131 */
132 public function delete( $key ) {
133 delete_site_option( $key );
134
135 return $this;
136 }
137
138 /**
139 * Generate key
140 *
141 * Generates a unique key based on microtime. Queue items are
142 * given a unique key so that they can be merged upon save.
143 *
144 * @param int $length Length.
145 *
146 * @return string
147 */
148 protected function generate_key( $length = 64 ) {
149 $unique = md5( microtime() . rand() );
150 $prepend = $this->identifier . '_batch_';
151
152 return substr( $prepend . $unique, 0, $length );
153 }
154
155 /**
156 * Maybe process queue
157 *
158 * Checks whether data exists within the queue and that
159 * the process is not already running.
160 */
161 public function maybe_handle() {
162 // Don't lock up other requests while processing
163 session_write_close();
164
165 if ( $this->is_process_running() ) {
166 // Background process already running.
167 wp_die();
168 }
169
170 if ( $this->is_queue_empty() ) {
171 // No data to process.
172 wp_die();
173 }
174
175 check_ajax_referer( $this->identifier, 'nonce' );
176
177 $this->handle();
178
179 wp_die();
180 }
181
182 /**
183 * Is queue empty
184 *
185 * @return bool
186 */
187 protected function is_queue_empty() {
188 global $wpdb;
189
190 $table = $wpdb->options;
191 $column = 'option_name';
192
193 if ( is_multisite() ) {
194 $table = $wpdb->sitemeta;
195 $column = 'meta_key';
196 }
197
198 $key = $this->identifier . '_batch_%';
199
200 $count = $wpdb->get_var( $wpdb->prepare( "
201 SELECT COUNT(*)
202 FROM {$table}
203 WHERE {$column} LIKE %s
204 ", $key ) );
205
206 return ( $count > 0 ) ? false : true;
207 }
208
209 /**
210 * Is process running
211 *
212 * Check whether the current process is already running
213 * in a background process.
214 */
215 public function is_process_running() {
216 if ( get_site_transient( $this->identifier . '_process_lock' ) ) {
217 // Process already running.
218 return true;
219 }
220
221 return false;
222 }
223
224 /**
225 * Lock process
226 *
227 * Lock the process so that multiple instances can't run simultaneously.
228 * Override if applicable, but the duration should be greater than that
229 * defined in the time_exceeded() method.
230 */
231 protected function lock_process() {
232 $this->start_time = time(); // Set start time of current process.
233
234 $lock_duration = ( property_exists( $this, 'queue_lock_time' ) ) ? $this->queue_lock_time : 60; // 1 minute
235 $lock_duration = apply_filters( $this->identifier . '_queue_lock_time', $lock_duration );
236
237 set_site_transient( $this->identifier . '_process_lock', microtime(), $lock_duration );
238 }
239
240 /**
241 * Unlock process
242 *
243 * Unlock the process so that other instances can spawn.
244 *
245 * @return $this
246 */
247 protected function unlock_process() {
248 delete_site_transient( $this->identifier . '_process_lock' );
249
250 return $this;
251 }
252
253 /**
254 * Get batch
255 *
256 * @return stdClass Return the first batch from the queue
257 */
258 protected function get_batch() {
259 global $wpdb;
260
261 $table = $wpdb->options;
262 $column = 'option_name';
263 $key_column = 'option_id';
264 $value_column = 'option_value';
265
266 if ( is_multisite() ) {
267 $table = $wpdb->sitemeta;
268 $column = 'meta_key';
269 $key_column = 'meta_id';
270 $value_column = 'meta_value';
271 }
272
273 $key = $this->identifier . '_batch_%';
274
275 $query = $wpdb->get_row( $wpdb->prepare( "
276 SELECT *
277 FROM {$table}
278 WHERE {$column} LIKE %s
279 ORDER BY {$key_column} ASC
280 LIMIT 1
281 ", $key ) );
282
283 $batch = new stdClass();
284 $batch->key = $query->$column;
285 $batch->data = maybe_unserialize( $query->$value_column );
286
287 return $batch;
288 }
289
290 /**
291 * Handle
292 *
293 * Pass each queue item to the task handler, while remaining
294 * within server memory and time limit constraints.
295 */
296 protected function handle() {
297 $this->lock_process();
298
299 do {
300 $batch = $this->get_batch();
301
302 foreach ( $batch->data as $key => $value ) {
303 $task = $this->task( $value );
304
305 if ( false !== $task ) {
306 $batch->data[ $key ] = $task;
307 } else {
308 unset( $batch->data[ $key ] );
309 }
310
311 if ( $this->time_exceeded() || $this->memory_exceeded() ) {
312 // Batch limits reached.
313 break;
314 }
315 }
316
317 // Update or delete current batch.
318 if ( ! empty( $batch->data ) ) {
319 $this->update( $batch->key, $batch->data );
320 } else {
321 $this->delete( $batch->key );
322 }
323 } while ( ! $this->time_exceeded() && ! $this->memory_exceeded() && ! $this->is_queue_empty() );
324
325 $this->unlock_process();
326
327 // Start next batch or complete process.
328 if ( ! $this->is_queue_empty() ) {
329 $this->dispatch();
330 } else {
331 $this->complete();
332 }
333
334 wp_die();
335 }
336
337 /**
338 * Memory exceeded
339 *
340 * Ensures the batch process never exceeds 90%
341 * of the maximum WordPress memory.
342 *
343 * @return bool
344 */
345 protected function memory_exceeded() {
346 $memory_limit = $this->get_memory_limit() * 0.9; // 90% of max memory
347 $current_memory = memory_get_usage( true );
348 $return = false;
349
350 if ( $current_memory >= $memory_limit ) {
351 $return = true;
352 }
353
354 return apply_filters( $this->identifier . '_memory_exceeded', $return );
355 }
356
357 /**
358 * Get memory limit
359 *
360 * @return int
361 */
362 protected function get_memory_limit() {
363 if ( function_exists( 'ini_get' ) ) {
364 $memory_limit = ini_get( 'memory_limit' );
365 } else {
366 // Sensible default.
367 $memory_limit = '128M';
368 }
369
370 if ( ! $memory_limit || -1 === $memory_limit ) {
371 // Unlimited, set to 32GB.
372 $memory_limit = '32000M';
373 }
374
375 return intval( $memory_limit ) * 1024 * 1024;
376 }
377
378 /**
379 * Time exceeded.
380 *
381 * Ensures the batch never exceeds a sensible time limit.
382 * A timeout limit of 30s is common on shared hosting.
383 *
384 * @return bool
385 */
386 protected function time_exceeded() {
387 $finish = $this->start_time + apply_filters( $this->identifier . '_default_time_limit', 20 ); // 20 seconds
388 $return = false;
389
390 if ( time() >= $finish ) {
391 $return = true;
392 }
393
394 return apply_filters( $this->identifier . '_time_exceeded', $return );
395 }
396
397 /**
398 * Complete.
399 *
400 * Override if applicable, but ensure that the below actions are
401 * performed, or, call parent::complete().
402 */
403 protected function complete() {
404 // Unschedule the cron healthcheck.
405 $this->clear_scheduled_event();
406 }
407
408 /**
409 * Schedule cron healthcheck
410 *
411 * @access public
412 * @param mixed $schedules Schedules.
413 * @return mixed
414 */
415 public function schedule_cron_healthcheck( $schedules ) {
416 $interval = apply_filters( $this->identifier . '_cron_interval', 5 );
417
418 if ( property_exists( $this, 'cron_interval' ) ) {
419 $interval = apply_filters( $this->identifier . '_cron_interval', $this->cron_interval_identifier );
420 }
421
422 // Adds every 5 minutes to the existing schedules.
423 $schedules[ $this->identifier . '_cron_interval' ] = array(
424 'interval' => MINUTE_IN_SECONDS * $interval,
425 'display' => sprintf( __( 'Every %d Minutes' ), $interval ),
426 );
427
428 return $schedules;
429 }
430
431 /**
432 * Handle cron healthcheck
433 *
434 * Restart the background process if not already running
435 * and data exists in the queue.
436 */
437 public function handle_cron_healthcheck() {
438 if ( $this->is_process_running() ) {
439 // Background process already running.
440 exit;
441 }
442
443 if ( $this->is_queue_empty() ) {
444 // No data to process.
445 $this->clear_scheduled_event();
446 exit;
447 }
448
449 $this->handle();
450
451 exit;
452 }
453
454 /**
455 * Schedule event
456 */
457 protected function schedule_event() {
458 if ( ! wp_next_scheduled( $this->cron_hook_identifier ) ) {
459 wp_schedule_event( time(), $this->cron_interval_identifier, $this->cron_hook_identifier );
460 }
461 }
462
463 /**
464 * Clear scheduled event
465 */
466 protected function clear_scheduled_event() {
467 $timestamp = wp_next_scheduled( $this->cron_hook_identifier );
468
469 if ( $timestamp ) {
470 wp_unschedule_event( $timestamp, $this->cron_hook_identifier );
471 }
472 }
473
474 /**
475 * Cancel Process
476 *
477 * Stop processing queue items, clear cronjob and delete batch.
478 *
479 */
480 public function cancel_process() {
481 if ( ! $this->is_queue_empty() ) {
482 $batch = $this->get_batch();
483
484 $this->delete( $batch->key );
485
486 wp_clear_scheduled_hook( $this->cron_hook_identifier );
487 }
488
489 }
490
491 /**
492 * Task
493 *
494 * Override this method to perform any actions required on each
495 * queue item. Return the modified item for further processing
496 * in the next pass through. Or, return false to remove the
497 * item from the queue.
498 *
499 * @param mixed $item Queue item to iterate over.
500 *
501 * @return mixed
502 */
503 abstract protected function task( $item );
504
505 }
506 }
507