PluginProbe
Lasso Lite – Affiliate Link Manager & Product Displays / 111
Lasso Lite – Affiliate Link Manager & Product Displays v111
158 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 All 57 releases
simple-urls / classes / processes / class-process.php

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

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