PluginProbe
Lasso Lite – Affiliate Link Manager & Product Displays / 151
Lasso Lite – Affiliate Link Manager & Product Displays v151
157 155 156 154 153 152 151 150 149 148 trunk 0.9.9 104 105 106 107 108 109 110 111 112 113 114 115 116 All 56 releases
simple-urls / classes / processes / class-process.php

class-process.php in Lasso Lite – Affiliate Link Manager & Product Displays 151, at classes/processes/class-process.php

564 lines 13.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Declare class Process
4 * Scan lasso link in posts/pages
5 *
6 * @package Process
7 */
8
9 namespace LassoLite\Classes\Processes;
10
11 use LassoLite\Classes\Helper as Lasso_Helper;
12 use LassoLite\Classes\Lasso_DB;
13 use LassoLite\Classes\Setting as Lasso_Setting;
14 use LassoLite\Classes\Verbiage as Lasso_Verbiage;
15
16 use LassoLite\Libs\Background_Processes\Lasso_WP_Background_Process;
17
18 use stdClass;
19
20 /**
21 * Load vendor-prefix for cron requests
22 */
23 require_once SIMPLE_URLS_DIR . '/vendor-prefix/vendor/autoload.php';
24
25 /**
26 * $key generated when save() event get fired
27 * OR
28 * $this->identifier = $this->prefix . '_' . $this->action;
29 * $key = $wpdb->esc_like( $this->identifier . '_batch_' ) . '%';
30 *
31 * Reference URL
32 * https://deliciousbrains.com/background-processing-wordpress/
33 * https://github.com/A5hleyRich/wp-background-processing
34 * https://github.com/A5hleyRich/wp-background-processing-example
35 */
36 abstract class Process extends Lasso_WP_Background_Process {
37 const EXCEEDED_TIME_LIMIT = 10; // ? Exceeded time limit in seconds
38 const AGE_OUT = 6; // ? Age out time after n hours
39 const RESTART_ATTEMPTED_LIMIT = 3;
40 const OPTION_RESTART_ATTEMPTED = 'lasso_lite_process_restart_attempted'; // ? Age out time after n hours
41 const COMPLETE_ACTION_KEY = 'lasso_lite_process_complete';
42
43 /**
44 * Action name
45 *
46 * @var string $action
47 */
48 protected $action = 'lassolite_background_process';
49
50 /**
51 * Log name
52 *
53 * @var string $log_name
54 */
55 protected $log_name = 'lasso_lite_background_process';
56
57 /**
58 * Process name
59 *
60 * @var string $process_name
61 */
62 protected $process_name = 'Lasso Lite Background Process';
63
64 /**
65 * Process constructor.
66 */
67 public function __construct() {
68 parent::__construct();
69
70 add_action( 'wp_ajax_lasso_lite_get_list_background_processing', array( $this, 'get_list_background_processing' ) );
71 add_filter( $this->identifier . '_default_time_limit', array( $this, 'custom_exceeded_time_limit' ), 10, 1 );
72 }
73
74 /**
75 * Custom exceeded time limit.
76 *
77 * @param int $time_limit Exceeded time limit.
78 */
79 public function custom_exceeded_time_limit( $time_limit ) {
80 return self::EXCEEDED_TIME_LIMIT;
81 }
82
83 /**
84 * Set log name
85 *
86 * @param string $log_name Log name.
87 */
88 public function set_log_file_name( $log_name ) {
89 $this->log_name = $log_name;
90 $this->process_name = ucwords( preg_replace( '/[\-_]/', ' ', $log_name ) );
91 }
92
93 /**
94 * Write logs when task starts
95 */
96 public function task_start_log() {
97 // ? set process start time
98 $this->set_process_start_time();
99 }
100
101 /**
102 * Write logs when task completed
103 */
104 public function task_end_log() {
105
106 }
107
108 /**
109 * Set total idexes of process
110 *
111 * @param int $total_count Total.
112 */
113 public function set_total( $total_count ) {
114 $key = $this->get_key();
115
116 if ( false === get_option( $key . '_total_count' ) ) {
117 update_option( $key . '_total_count', $total_count, false );
118 }
119 }
120
121 /**
122 * Get total idexes of process
123 */
124 public function get_total() {
125 $key = $this->get_key();
126 return (int) get_option( $key . '_total_count' );
127 }
128
129 /**
130 * Get key of process
131 */
132 public function get_key() {
133 return $this->identifier;
134 }
135
136 /**
137 * Set start time of process
138 */
139 public function set_process_start_time() {
140 // ? SHOULD CONSIDER LATER
141 // ? Only set "start time" for the first process of each background process type. The "start time" will auto delete when the process completed.
142 /**
143 // if ( ! $this->get_process_start_time() ) {
144 // $key = $this->get_key();
145 // update_option( $key . '_start_time', microtime( true ), false );
146 // }
147 */
148
149 $key = $this->get_key();
150 update_option( $key . '_start_time', microtime( true ), false );
151 }
152
153 /**
154 * Get start time of process
155 */
156 public function get_process_start_time() {
157 $key = $this->get_key();
158 return get_option( $key . '_start_time', 0 );
159 }
160
161 /**
162 * Set running time of process
163 */
164 public function set_processing_runtime() {
165 $key = $this->get_key();
166 $diff = microtime( true ) - $this->get_process_start_time();
167 $diff = round( $diff, 2 );
168
169 update_option( $key . '_process_running_time', $diff, false );
170 }
171
172 /**
173 * Get running time of process
174 */
175 public function get_processing_runtime() {
176 $key = $this->get_key();
177 return get_option( $key . '_process_running_time', 0 );
178 }
179
180 /**
181 * Get ETA
182 */
183 public function get_eta() {
184 $total = $this->get_total();
185 $completed = $this->get_total_completed();
186 $completed = 0 !== $completed ? $completed : 1;
187 $unit_execution_time = $this->get_processing_runtime() / $completed;
188 $eta = $unit_execution_time * ( $total - $completed );
189
190 return $eta;
191 }
192
193 /**
194 * Check whether process is running or not
195 *
196 * @param bool $default Use default or custom logic. Default to false.
197 */
198 public function is_process_running( $default = false ) {
199 $this->remove_duplicated_processes();
200 if ( $default ) {
201 return parent::is_process_running();
202 }
203
204 $lasso_db = new Lasso_DB();
205 $lasso_db->check_empty_process();
206 $next_schedule = $this->next_schedule();
207
208 return parent::is_process_running() && $this->get_total_remaining() > 0 && $next_schedule;
209 }
210
211 /**
212 * Remove duplicated processes
213 */
214 private function remove_duplicated_processes() {
215 $lasso_db = new Lasso_DB();
216
217 $query = '
218 DELETE
219 FROM ' . $lasso_db->options . '
220 WHERE option_id not in (
221 SELECT option_id
222 FROM (
223 SELECT min(option_id) AS option_id
224 FROM ' . $lasso_db->options . "
225 WHERE option_name LIKE '%" . $this->action . "%batch%'
226 ) AS wpo
227 ) AND option_name LIKE '%" . $this->action . "%batch%'
228 ";
229
230 $lasso_db->query( $query );
231 }
232
233 /**
234 * Remove: remove a process
235 */
236 public function remove_process() {
237 $lasso_db = new Lasso_DB();
238
239 $sql = '
240 DELETE FROM ' . $lasso_db->options . "
241 WHERE option_name LIKE '%" . $this->action . "%'
242 AND option_name NOT LIKE '%" . $this->action . "_start_time%'
243 ";
244
245 $lasso_db->query( $sql );
246 }
247
248 /**
249 * Check whether the process is age out
250 */
251 public function is_process_age_out() {
252 $start_time = $this->get_process_start_time();
253 $diff = microtime( true ) - $start_time;
254 $failed_times = self::RESTART_ATTEMPTED_LIMIT;
255 $option_name = self::OPTION_RESTART_ATTEMPTED;
256 $current_attempted = intval( get_option( $option_name, 0 ) );
257 $hour_in_seconds = self::AGE_OUT * HOUR_IN_SECONDS;
258
259 if ( $current_attempted >= $failed_times && ! $this->is_queue_empty() ) {
260 return true;
261 }
262
263 if ( $start_time > 0 && $diff > $hour_in_seconds ) { // ? the process is age out
264 // ? restart_attempted reach the limit, remove the process
265 $this->remove_process();
266
267 // ? increase restart_attempted
268 update_option( $option_name, ++$current_attempted );
269 }
270
271 return false;
272 }
273
274 /**
275 * Get next schedule
276 */
277 public function next_schedule() {
278 return wp_next_scheduled( $this->cron_hook_identifier );
279 }
280
281 /**
282 * Get total remaining items
283 */
284 public function get_total_remaining() {
285 global $wpdb;
286
287 $lasso_db = new Lasso_DB();
288
289 $table = $wpdb->options;
290 $column = 'option_name';
291 $key_column = 'option_id';
292 $value_column = 'option_value';
293
294 if ( is_multisite() ) {
295 $table = $wpdb->sitemeta;
296 $column = 'meta_key';
297 $key_column = 'meta_id';
298 $value_column = 'meta_value';
299 }
300
301 $key = $wpdb->esc_like( $this->identifier . '_batch_' ) . '%';
302
303 // @codingStandardsIgnoreStart
304 $sql = $wpdb->prepare(
305 "
306 SELECT {$value_column}
307 FROM {$table}
308 WHERE {$column} LIKE %s
309 ORDER BY {$key_column} ASC
310 LIMIT 1
311 ",
312 $key
313 );
314 // @codingStandardsIgnoreEnd
315
316 $queue = $lasso_db->get_var( $sql ); // ? We want to track SQL errors in Sentry
317 $count = 0;
318 $unserialized_queue = maybe_unserialize( $queue );
319 if ( is_array( $unserialized_queue ) ) {
320 $count = count( $unserialized_queue );
321 }
322
323 return $count;
324 }
325
326 /**
327 * Get total items are completed
328 */
329 public function get_total_completed() {
330 $get_total = $this->get_total();
331 $total_remainig = $this->get_total_remaining();
332 if ( $total_remainig && $total_remainig <= $get_total ) {
333 return $get_total - $total_remainig;
334 }
335
336 return $get_total;
337 }
338
339 /**
340 * Do something when the process is completed
341 */
342 public function set_completed() {
343 $key = $this->get_key();
344 delete_option( $key . '_total_count' );
345 delete_option( $key . '_start_time' );
346 delete_option( $key . '_process_running_time' );
347 }
348
349 /**
350 * Check whether the process is empty or not
351 */
352 public function is_queue_empty() { // phpcs:ignore
353 return parent::is_queue_empty();
354 }
355
356 /**
357 * Check whether the process is completed or not
358 */
359 public function is_completed() {
360 return $this->is_queue_empty();
361 }
362
363 /**
364 * Do something when an index completes
365 */
366 public function complete() {
367 parent::complete();
368 $this->set_completed();
369 $this->task_end_log();
370 do_action( self::COMPLETE_ACTION_KEY, $this->action );
371 }
372
373 /**
374 * Check whether CPU exceeded or not
375 */
376 public function cpu_exceeded() {
377 $max_cpu_allow = Lasso_Setting::get_setting( 'cpu_threshold', 80 ); // ? percent
378 $cpu_load = Lasso_Helper::get_cpu_load();
379
380 return $cpu_load >= $max_cpu_allow;
381 }
382
383 /**
384 * Handle
385 *
386 * Pass each queue item to the task handler, while remaining
387 * within server memory and time limit constraints.
388 */
389 protected function handle() {
390 $this->lock_process();
391
392 do {
393 $batch = $this->get_batch();
394
395 $batch_data = $batch->data ?? array();
396 foreach ( $batch_data as $key => $value ) {
397 $task = $this->cpu_exceeded() ? true : $this->task( $value );
398
399 if ( false !== $task ) {
400 $batch->data[ $key ] = $task;
401 } else {
402 unset( $batch->data[ $key ] );
403 }
404
405 if ( $this->time_exceeded() || $this->memory_exceeded() ) {
406 // ? Batch limits reached.
407 break;
408 }
409 }
410
411 // ? Update or delete current batch.
412 $batch_key = $batch->key ?? '';
413 if ( ! empty( $batch->data ) ) {
414 $this->update( $batch_key, $batch->data );
415 } else {
416 $this->delete( $batch_key );
417 }
418 } while ( ! $this->time_exceeded() && ! $this->memory_exceeded() && ! $this->is_queue_empty() );
419
420 $this->unlock_process();
421
422 // ? Start next batch or complete process.
423 if ( ! $this->is_queue_empty() ) {
424 $this->dispatch();
425 } else {
426 $this->complete();
427 }
428
429 wp_die();
430 }
431
432 /**
433 * Handle manually
434 *
435 * Pass each queue item to the task handler, while remaining
436 * within server memory and time limit constraints.
437 */
438 public function handle_manually() {
439 $this->handle();
440 }
441
442 /**
443 * Get batch
444 *
445 * @return stdClass Return the first batch from the queue
446 */
447 public function get_batch() {
448 global $wpdb;
449
450 $table = $wpdb->options;
451 $column = 'option_name';
452 $key_column = 'option_id';
453 $value_column = 'option_value';
454
455 if ( is_multisite() ) {
456 $table = $wpdb->sitemeta;
457 $column = 'meta_key';
458 $key_column = 'meta_id';
459 $value_column = 'meta_value';
460 }
461
462 $key = $wpdb->esc_like( $this->identifier . '_batch_' ) . '%';
463
464 // @codingStandardsIgnoreStart
465 $query = $wpdb->get_row(
466 $wpdb->prepare(
467 "
468 SELECT *
469 FROM {$table}
470 WHERE {$column} LIKE %s
471 ORDER BY {$key_column} ASC
472 LIMIT 1
473 ",
474 $key
475 )
476 );
477 // @codingStandardsIgnoreEnd
478
479 if ( is_null( $query ) ) {
480 return array();
481 }
482
483 $batch = new stdClass();
484 $batch->key = $query->$column;
485 $batch->data = maybe_unserialize( $query->$value_column );
486
487 return $batch;
488 }
489
490 /**
491 * Disable all processes exclude remove attribute process
492 */
493 public static function are_all_processes_disabled() {
494 $disable = get_option( 'lasso_lite_disable_processes', 0 );
495 $disable = boolval( intval( $disable ) );
496
497 return $disable;
498 }
499
500 /**
501 * Remove all processes (WP schedule)
502 */
503 public function remove_all_processes() {
504 $process_classes = Lasso_Verbiage::PROCESS_DESCRIPTION;
505 foreach ( $process_classes as $process_class => $process_name ) {
506 /** @var Process $process_class_obj */ // phpcs:ignore
507 $process_class_obj = new $process_class();
508 $process_class_obj->clear_scheduled_event();
509 $process_class_obj->set_completed();
510 wp_clear_scheduled_hook( $process_class_obj->cron_hook_identifier );
511 }
512
513 $lasso_db = new Lasso_DB();
514 $lasso_db->remove_all_lasso_processes();
515 }
516
517 /**
518 * Get Background Process is running
519 */
520 public function get_list_background_processing() {
521 $result = array(
522 'running_total' => 0,
523 'items' => array(),
524 );
525 $restart_attempted = intval( get_option( self::OPTION_RESTART_ATTEMPTED, 0 ) );
526
527 $process_classes = Lasso_Verbiage::PROCESS_DESCRIPTION;
528 $process_classes = apply_filters( 'lasso_lite_all_processes', $process_classes );
529
530 foreach ( $process_classes as $process_class => $process_name ) {
531 /** @var Process $process_class_obj */ // phpcs:ignore
532 $process_class_obj = new $process_class();
533 $process_next_schedule = $process_class_obj->next_schedule();
534
535 $process_total_item = $process_class_obj->get_total();
536 $process_completed = $process_class_obj->get_total_completed();
537 $process_remaining = $process_class_obj->get_total_remaining();
538
539 $trigger_manually = $restart_attempted >= self::RESTART_ATTEMPTED_LIMIT && ! $process_class_obj->is_process_running();
540 $data = array(
541 'name' => $process_name,
542 'class' => $process_class,
543 'completed' => $process_completed,
544 'total' => $process_total_item,
545 'trigger_manually' => $trigger_manually,
546 );
547
548 if ( $process_next_schedule ) {
549 if ( ! $process_total_item || ! $process_remaining ) {
550 continue;
551 }
552
553 $result['running_total'] += 1;
554
555 $result['items'][] = $data;
556 } elseif ( ! $process_class_obj->is_queue_empty() && $trigger_manually ) {
557 $result['items'][] = $data;
558 }
559 }
560
561 wp_send_json_success( $result );
562 }
563 }
564