PluginProbe
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance / 3.2.22
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance v3.2.22
4.7.0 4.6.1 4.6.0 4.5.5 4.5.4 4.5.3 4.5.2 3.2.20 3.2.21 3.2.22 3.2.3 3.2.5 3.2.6 3.2.7 3.2.9 3.3.0 3.3.1 3.3.2 3.4.0 3.4.1 3.4.2 3.5.0 3.6.0 3.7.0 3.7.1 All 111 releases
wp-optimize / vendor / team-updraft / common-libs / src / updraft-tasks / class-updraft-task.php

class-updraft-task.php in WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance 3.2.22, at vendor/team-updraft/common-libs/src/updraft-tasks/class-updraft-task.php

747 lines 18.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * The base class which must be extended to use the tasks library
4 */
5
6 if (!defined('ABSPATH')) die('Access denied.');
7
8 if (!class_exists('Updraft_Task_1_2')) :
9
10 if (!class_exists('Updraft_Task_Options')) require_once('class-updraft-task-options.php');
11 if (!class_exists('Updraft_Task_Meta')) require_once('class-updraft-task-meta.php');
12
13 abstract class Updraft_Task_1_2 {
14
15 /**
16 * A unique ID for the specific task
17 *
18 * @var int
19 */
20 private $id;
21
22 /**
23 * The user id of the creator of this task
24 *
25 * @var string
26 */
27 private $user_id;
28
29 /**
30 * A text description for the task
31 *
32 * @var string
33 */
34 private $description;
35
36 /**
37 * A type for the task
38 *
39 * @var string
40 */
41 private $type;
42
43 /**
44 * A timestamp indicating the time the task was created
45 *
46 * @var string
47 */
48 private $time_created;
49
50 /**
51 * The number of times this task was attempted
52 *
53 * @var int
54 */
55 private $attempts;
56
57 /**
58 * A text description describing the status of the task
59 *
60 * @var string
61 */
62 private $status;
63
64 /**
65 * An identifier indicating which child class created this instance
66 *
67 * @var string
68 */
69 private $class_identifier;
70
71 /**
72 * A logger object that can be used to capture interesting events / messages
73 *
74 * @var Object
75 */
76 protected $_loggers;
77
78 /**
79 * Additional dynamic properties, usually copied from the UpdraftPlus_Task object.
80 *
81 * @var array<string,mixed>
82 */
83 protected $extra_properties = array();
84
85 /**
86 * The Task constructor
87 *
88 * @param UpdraftPlus_Task|object $task UpdraftPlus_Task object.
89 */
90 public function __construct($task) {
91 foreach (get_object_vars($task) as $key => $value)
92 $this->$key = $value;
93 }
94
95 /**
96 * Sets the instance ID.
97 *
98 * @param String $instance_id - the instance ID
99 */
100 public function set_id($instance_id) {
101 $this->id = $instance_id;
102 }
103
104 /**
105 * Gets the instance ID.
106 *
107 * @return String the instance ID
108 */
109 public function get_id() {
110 return $this->id;
111 }
112
113 /**
114 * Sets the description.
115 *
116 * @param String $description - the description of the task
117 */
118 public function set_description($description) {
119 $this->description = $description;
120 }
121
122 /**
123 * Gets the task description
124 *
125 * @return String $description - the description of the task
126 */
127 public function get_description() {
128 return $this->description;
129 }
130
131 /**
132 * Sets the type.
133 *
134 * @param String $type - the type of the task
135 */
136 public function set_type($type) {
137 $this->type = $type;
138 }
139
140 /**
141 * Gets the number of times this task was attempted
142 *
143 * @return int $attempts - the count
144 */
145 public function get_attempts() {
146 return $this->attempts;
147 }
148
149 /**
150 * Sets the number of times this task was attempted
151 *
152 * @param String $attempts - the count
153 */
154 public function set_attempts($attempts) {
155 if (is_numeric($attempts))
156 $this->attempts = $attempts;
157 else return false;
158
159 return $this->update_attempts($this->id, $this->attempts);
160 }
161
162 /**
163 * Gets the task type
164 *
165 * @return String $type - the type of the task
166 */
167 public function get_type() {
168 return $this->type;
169 }
170
171 /**
172 * Sets the task status.
173 *
174 * @param String $status - the status of the task
175 *
176 * @return Boolean - the result of the status update
177 */
178 public function set_status($status) {
179
180 if (array_key_exists($status, self::get_allowed_statuses()))
181 $this->status = $status;
182 else return false;
183
184 return $this->update_status($this->id, $this->status);
185 }
186
187 /**
188 * Gets the task status
189 *
190 * @return String $status - the status of the task
191 */
192 public function get_status() {
193 return $this->status;
194 }
195
196 /**
197 * Sets the logger for this task.
198 *
199 * @param array $loggers - the loggers for this task
200 */
201 public function set_loggers($loggers) {
202 if (is_array($loggers)) {
203 foreach ($loggers as $logger) {
204 $this->add_logger($logger);
205 }
206 }
207 }
208
209 /**
210 * Add a logger to loggers list
211 *
212 * @param Object $logger - a logger for the task
213 */
214 public function add_logger($logger) {
215 $this->_loggers[] = $logger;
216 }
217
218 /**
219 * Return list of loggers
220 *
221 * @return array
222 */
223 public function get_loggers() {
224 return $this->_loggers;
225 }
226
227 /**
228 * The initialisation function that accepts and processes any parameters needed before the task starts
229 *
230 * @param Array $options - array of options
231 *
232 * @uses update_option
233 */
234 public function initialise($options = array()) {
235
236 do_action('ud_task_before_initialise', $this, $options);
237
238 /**
239 * Parse incoming $options into an array and merge it with defaults
240 */
241 $defaults = $this->get_default_options();
242 $options = wp_parse_args($options, $defaults);
243
244 foreach ($options as $option => $value) {
245 $this->update_option($option, $value);
246 }
247
248 do_action('ud_task_initialise_complete', $this, $options);
249
250 }
251
252 /**
253 * Attempts to perform the task
254 *
255 * @param integer $lock_for - if greater than zero, then lock the task, and don't break until this number of seconds has passed
256 *
257 * @return boolean Status of the attempt
258 */
259 public function attempt($lock_for = 0) {
260
261 $_task = $this->get_task_from_db($this->get_id());
262
263 if (!$_task) {
264 $this->log("The task with id : {$this->get_id()}, and type '{$this->get_type()}' seems to have been deleted from the database.");
265 return false;
266 }
267
268 if ('complete' == $this->get_status()) {
269 $this->log("Attempting already complete task with ID : {$this->get_id()}, and type '{$this->get_type()}'. Aborting !");
270 return true;
271 }
272
273 if ($lock_for) {
274 $try = 1;
275 $locked = false;
276 while ($try < 4) {
277 if ($locked = $this->lock($this->get_id(), true, $lock_for)) break;
278 $try ++;
279 sleep(1);
280 }
281 if (!$locked) {
282 $this->fail('could_not_lock', 'The task could not be locked');
283 return false;
284 }
285 }
286
287 $attempts = $this->get_attempts();
288
289 if ($attempts >= $this->get_max_attempts()) {
290 $this->fail("max_attempts_exceeded", "Maximum attempts ($attempts) exceeded for task");
291 return false;
292 }
293
294 $this->log("Processing task with ID : {$this->get_id()}, and type '{$this->get_type()}'");
295 $this->set_attempts(++$attempts);
296 $status = $this->run();
297
298 if ($status) {
299 $this->complete();
300 $this->log("Completed processing task with ID : {$this->get_id()}, and type '{$this->get_type()}'");
301 }
302
303 if ($lock_for) $this->lock($this->get_id(), false);
304
305 return $status;
306 }
307
308 /**
309 * Lock or unlock a task
310 *
311 * @param Integer - $task_id - task identifier
312 * @param Boolean - $lock - whether to lock or unlock
313 * @param Integer - $lock_for - if already locked, how long after which to break the lock
314 *
315 * @return Boolean - whether the operation was successful
316 */
317 public function lock($task_id, $lock = true, $lock_for = 60) {
318
319 global $wpdb;
320
321 if (!$lock) {
322 return $wpdb->update($wpdb->base_prefix.'tm_tasks', array('last_locked_at' => 0), array('id' => $task_id)) ? true : false;
323 }
324
325 // Mode: lock. Attempt to set the lock
326 $affected = $wpdb->update($wpdb->base_prefix.'tm_tasks', array('last_locked_at' => time()), array('id' => $task_id, 'last_locked_at' => 0));
327
328 // Success.
329 if (1 == $affected) return true;
330
331 // Failed - something else already had it locked. Grab the lock if it had expired.
332 $affected = $wpdb->update($wpdb->base_prefix.'tm_tasks', array('last_locked_at' => time()), array('id' => $task_id, 'last_locked_at' => 0));
333
334 $expires_at = time() - $lock_for;
335
336 $affected = $wpdb->query($wpdb->prepare("
337 UPDATE {$wpdb->base_prefix}tm_tasks
338 SET last_locked_at = %d
339 WHERE id = %d
340 AND last_locked_at <= %s
341 ", time(), $task_id, $expires_at));
342
343 return $affected ? true : false;
344 }
345
346 /**
347 * This function is called to allow for the task to perform a small chunk of work.
348 * It should be written in a way that anticipates it being killed off at any time.
349 */
350 abstract public function run();
351
352 /**
353 * Any clean up code goes here.
354 */
355 public function complete() {
356
357 do_action('ud_task_before_complete', $this);
358
359 $this->set_status('complete');
360
361 do_action('ud_task_completed', $this);
362
363 return true;
364 }
365
366 /**
367 * Fires if the task fails, any clean up code and logging should go here
368 *
369 * @param String $error_code - A code for the failure
370 * @param String $error_message - A description for the failure
371 */
372 public function fail($error_code = "Unknown", $error_message = "Unknown") {
373
374 do_action('ud_task_before_failed', $this);
375
376 $this->set_status('failed');
377 $this->log(sprintf("Task with ID %d and type (%s) failed with error code %s - %s", $this->id, $this->type, $error_code, $error_message));
378
379 $this->update_option("error_code", $error_code);
380 $this->update_option("error_message", $error_message);
381
382 do_action('ud_task_failed', $this);
383
384 return true;
385 }
386
387 /**
388 * Prints any information about the task that the UI can use on the front end for debugging
389 * @param String $title the header to use in the report
390 *
391 * @return String The task report HTML
392 */
393 public function print_task_report_widget($title = 'Task Summary') {
394
395 $ret = "";
396
397 $status = $this->get_status();
398 $stage = $this->get_option('stage') ? $this->get_option('stage') : 'Unknown';
399 $description = $this->get_status_description($status);
400
401
402 $ret .= "<div class='task task-report task-{$this->type}' id='task-id-{$this->id}'>";
403 $ret .= "<h4>Task Summary</h4>";
404 $ret .= "<ul class='properties-list task-{$this->type}'>";
405
406 foreach ($this as $key => $value) {
407 $ret .= sprintf("<li> %s : %s </li>", $key, $value);
408 }
409 $ret .='</ul>';
410
411 $ret .= "<h4> $title </h4>";
412 $ret .= "<ul class='data-list task-{$this->type}'>";
413
414 foreach ($this->get_all_options() as $key => $value) {
415 if (is_array(maybe_unserialize($value))) {
416 $ret .= sprintf("<li> %s</li>", $key);
417 $ret .= "<ul class='sub-list'>";
418 foreach (maybe_unserialize($value) as $k => $v) {
419 $ret .= sprintf("<li> %s => %s </li>", $k, $v);
420 }
421 $ret .= "</ul>";
422 } else {
423 $ret .= sprintf("<li> %s : %s </li>", $key, $value);
424 }
425 }
426 $ret .='</ul>';
427 $ret .='</div>';
428
429 return apply_filters('ud_print_task_report_widget', $ret, $this);
430
431 }
432
433 /**
434 * This method gets an option from the task options in the WordPress database if available,
435 * otherwise returns the default for this task type
436 *
437 * @param String $option the name of the option to get
438 * @param Mixed $default a value to return if the option is not currently set
439 *
440 * @return Mixed The option from the database
441 */
442 public function get_option($option = null, $default = null) {
443 return Updraft_Task_Options::get_task_option($this->id, $option, $default);
444 }
445
446 /**
447 * This method is used to add a task option stored in the WordPress database
448 *
449 * @param String $option the name of the option to update
450 * @param Mixed $value the value to save to the option
451 *
452 * @return Mixed the status of the add operation
453 */
454 public function add_option($option, $value) {
455 return Updraft_Task_Options::update_task_option($this->id, $option, $value);
456 }
457
458 /**
459 * This method is used to update a task option stored in the WordPress database
460 *
461 * @param String $option the name of the option to update
462 * @param Mixed $value the value to save to the option
463 *
464 * @return Mixed the status of the update operation
465 */
466 public function update_option($option, $value) {
467 return Updraft_Task_Options::update_task_option($this->id, $option, $value);
468 }
469
470 /**
471 * This method is used to delete a task option stored in the WordPress database
472 *
473 * @param String $option the option to delete
474 *
475 * @return Boolean the result of the delete operation
476 */
477 public function delete_option($option) {
478 return Updraft_Task_Options::delete_task_option($this->id, $option);
479 }
480
481 /**
482 * This method gets all options assoicated with a task
483 */
484 public function get_all_options() {
485 return Updraft_Task_Options::get_all_task_options($this->id);
486 }
487
488 /**
489 * Retrieve default options for this task.
490 * This method should normally be over-ridden by the child.
491 *
492 * @return Array - an array of options
493 */
494 public function get_default_options() {
495
496 $this->log(sprintf('The get_default_options() method was not over-ridden for the class : %s', $this->get_description()));
497
498 return array();
499 }
500
501 /**
502 * Returns a unique label for this instance that can be used as an identifier
503 *
504 * @return String - a unique label for this instance
505 */
506 protected function get_unique_label() {
507 return apply_filters('ud_task_unique_label', $this->id."-".$this->type, $this);
508 }
509
510 /**
511 * Updates the status of the given task in the DB
512 *
513 * @param String $id - the id of the task
514 * @param String $status - the status of the task
515 *
516 * @return Boolean - the stauts of the update operation
517 */
518 public function update_status($id, $status) {
519
520 if (!array_key_exists($status, self::get_allowed_statuses()))
521 return false;
522
523 global $wpdb;
524 $sql = $wpdb->prepare("UPDATE {$wpdb->base_prefix}tm_tasks SET status = %s WHERE id = %d", $status, $id);
525
526 return $wpdb->query($sql);
527 }
528
529 /**
530 * Updates the number of attempts made for the given task in the DB
531 *
532 * @param String $id - the id of the task
533 * @param int $attempts - the status of the task
534 *
535 * @return Boolean - the stauts of the update operation
536 */
537 public function update_attempts($id, $attempts) {
538
539 if (!is_numeric($attempts))
540 return false;
541
542 global $wpdb;
543 $sql = $wpdb->prepare("UPDATE {$wpdb->base_prefix}tm_tasks SET attempts = %s WHERE id = %d", $attempts, $id);
544
545 return $wpdb->query($sql);
546 }
547
548 /**
549 * Cleans out the given task from the DB
550 *
551 * @return Boolean - the status of the delete operation
552 */
553 public function delete() {
554 global $wpdb;
555
556 $sql = $wpdb->prepare("DELETE FROM {$wpdb->base_prefix}tm_tasks WHERE id = %d", $this->id);
557 return $wpdb->query($sql);
558 }
559
560 /**
561 * Cleans out the given task meta from the DB
562 *
563 * @return Boolean - the status of the delete operation
564 */
565 public function delete_meta() {
566 return Updraft_Task_Meta::bulk_delete_task_meta($this->id);
567 }
568
569 /**
570 * Helper function to convert object to array.
571 *
572 * @return array Object as array.
573 */
574 public function to_array() {
575 $task = get_object_vars($this);
576
577 foreach (array( 'task_options', 'task_data', 'task_logs', 'task_extras' ) as $key) {
578 if ($this->__isset($key))
579 $task[$key] = $this->__get($key);
580 }
581
582 return $task;
583 }
584
585 /**
586 * Captures and logs any interesting messages
587 *
588 * @param String $message - the error message
589 * @param String $error_type - the error type
590 */
591 public function log($message, $error_type = 'info') {
592
593 if (isset($this->_loggers)) {
594 foreach ($this->_loggers as $logger) {
595 $logger->log($message, $error_type);
596 }
597 }
598 }
599
600 /**
601 * Retrieve all the supported task statuses.
602 *
603 * Tasks should have a limited set of valid status values, this method provides a
604 * list of values and descriptions.
605 *
606 * @return array List of task statuses.
607 */
608 public static function get_allowed_statuses() {
609 $status = array(
610 'initialised' => __('Initialised'),
611 'active' => __('Active'),
612 'paused' => __('Paused'),
613 'complete' => __('Completed'),
614 'failed' => __('Failed')
615 );
616
617 return apply_filters('ud_allowed_task_statuses', $status);
618 }
619
620
621 /**
622 * Retrieve the max attempts permitted for task type
623 *
624 * @return int Max attempts permitted for task type
625 */
626 private function get_max_attempts() {
627 return apply_filters('ud_max_attempts', 5, $this);
628 }
629
630 /**
631 * Retrieve the text description of the task status.
632 *
633 * @param String $status - The task status
634 *
635 * @return String Description of the task status.
636 */
637 public static function get_status_description($status) {
638 $list = self::get_allowed_statuses();
639
640 if (!array_key_exists($status, self::get_allowed_statuses()))
641 return __('Unknown');
642
643 return apply_filters("ud_task_status_description_{$status}", $list[$status], $status, $list);
644 }
645
646
647 /**
648 * Creates a new task instance and returns it
649 *
650 * @access public
651 *
652 * @global wpdb $wpdb WordPress database abstraction object.
653 *
654 * @param String $type A identifier for the task
655 * @param String $description A description of the task
656 * @param Mixed $options A list of options to initialise the task
657 * @param String $task_class Class name of task; only needed/used on PHP 5.2 (due to lack of late static binding)
658 *
659 * @return Updraft_Task|false Task object, false otherwise.
660 */
661 public static function create_task($type, $description, $options = array(), $task_class = '') {
662 global $wpdb;
663
664 $user_id = get_current_user_id();
665 $class_identifier = function_exists('get_called_class') ? get_called_class() : $task_class;// phpcs:ignore PHPCompatibility.FunctionUse.NewFunctions.get_called_classFound
666
667 $is_anonymous_user_allowed = isset($options['anonymous_user_allowed']) && $options['anonymous_user_allowed'];
668 if (!$user_id && !$is_anonymous_user_allowed) return false;
669
670 $sql = $wpdb->prepare("INSERT INTO {$wpdb->base_prefix}tm_tasks (type, user_id, description, class_identifier, status) VALUES (%s, %d, %s, %s, %s)", $type, $user_id, $description, $class_identifier, 'active');
671
672 $wpdb->query($sql);
673
674 $task_id = $wpdb->insert_id;
675
676 if (!$task_id) return false;
677
678 $_task = $wpdb->get_row("SELECT * FROM {$wpdb->base_prefix}tm_tasks WHERE id = {$task_id} LIMIT 1");
679
680 $task = new $class_identifier($_task);
681
682 if (!$task) return false;
683
684 $task->initialise($options);
685
686 return $task;
687 }
688
689 /**
690 * Select the current task from the database
691 *
692 * @param int $task_id - The task ID
693 * @return object|false
694 */
695 private function get_task_from_db($task_id) {
696 global $wpdb;
697 $sql = $wpdb->prepare("SELECT * FROM {$wpdb->base_prefix}tm_tasks WHERE id = %d LIMIT 1", $task_id);
698 return $wpdb->get_row($sql);
699 }
700
701 /**
702 * Writing data to inaccessible/non-existing property
703 *
704 * @param string $name
705 * @param mixed $value
706 *
707 * @return void
708 */
709 public function __set($name, $value) {
710 $this->extra_properties[$name] = $value;
711 }
712
713 /**
714 * Reading data from inaccessible/non-existing property
715 *
716 * @param string $name
717 *
718 * @return mixed|null
719 */
720 public function __get($name) {
721 return isset($this->extra_properties[$name]) ? $this->extra_properties[$name] : null;
722 }
723
724 /**
725 * Invoked when `isset` or `empty` are called on inaccessible/non-existing property
726 *
727 * @param string $name
728 *
729 * @return bool
730 */
731 public function __isset($name) {
732 return isset($this->extra_properties[$name]);
733 }
734
735 /**
736 * Invoked when `unset` is called on inaccessible/non-existing property
737 *
738 * @param string $name
739 *
740 * @return void
741 */
742 public function __unset($name) {
743 unset($this->extra_properties[$name]);
744 }
745 }
746 endif;
747