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
← All changes | classes/class-cron.php +7 -483 157111 View file →
@@ -6,17 +6,9 @@
6 6 */
7 7
8 8 namespace LassoLite\Classes;
9 9
10 -use LassoLite\Classes\Estimate_Earning;
11 -use LassoLite\Classes\License;
12 -use LassoLite\Classes\Processes\Amazon;
13 -use LassoLite\Classes\Processes\Amazon_Shortlink;
14 10 use LassoLite\Classes\Processes\Import_All;
15 -use LassoLite\Classes\Processes\Revert_All;
16 -use LassoLite\Classes\Setting;
17 -use LassoLite\Classes\Enum;
18 -use LassoLite\Admin\Constant;
19 11
20 12 /**
21 13 * Config
22 14 */
@@ -22,37 +14,20 @@
22 14 */
23 15 class Cron {
24 16
25 17 const CRONS = array(
26 - 'lasso_lite_amazon_shortlink' => 'lasso_lite_15_minutes',
27 - 'lasso_lite_update_amazon' => 'lasso_lite_15_minutes',
18 + 'lasso_lite_tracking_support_status' => 'daily',
28 19 'lasso_lite_import_all' => 'lasso_lite_15_minutes',
29 - 'lasso_lite_revert_all' => 'lasso_lite_15_minutes',
30 - 'lasso_lite_tracking_support_status' => 'daily',
31 - 'lasso_lite_update_license_status' => 'daily',
32 - 'lasso_lite_cron_get_snippet' => 'daily',
33 - 'lasso_lite_cron_get_js_domain' => 'daily',
34 - 'lasso_lite_cron_get_info' => 'daily',
35 - 'lasso_lite_check_lite_user' => 'daily',
36 - Estimate_Earning::CRON_HOOK => 'weekly',
37 20 );
38 21
39 22 /**
40 23 * Cron constructor.
41 24 */
42 - public function register_hooks() {
25 + public function __construct() {
43 26 add_filter( 'cron_schedules', array( $this, 'add_lasso_cron' ) );
44 27 add_action( 'lasso_lite_tracking_support_status', array( $this, 'lasso_lite_tracking_support_status' ) );
45 28 add_action( 'lasso_lite_import_all', array( $this, 'lasso_import_all' ) );
46 - add_action( 'lasso_lite_revert_all', array( $this, 'lasso_revert_all' ) );
47 - add_action( 'lasso_lite_update_amazon', array( $this, 'lasso_lite_update_amazon' ) );
48 - add_action( 'lasso_lite_amazon_shortlink', array( $this, 'lasso_lite_amazon_shortlink' ) );
49 - add_action( 'lasso_lite_update_license_status', array( $this, 'lasso_lite_update_license_status' ) );
50 - add_action( 'lasso_lite_cron_get_snippet', array( $this, 'lasso_lite_cron_get_snippet' ) );
51 - add_action( 'lasso_lite_cron_get_js_domain', array( $this, 'lasso_lite_cron_get_js_domain' ) );
52 - add_action( 'lasso_lite_cron_get_info', array( $this, 'lasso_lite_cron_get_info' ) );
53 - add_action( 'lasso_lite_check_lite_user', array( $this, 'lasso_lite_check_lite_user' ) );
54 - add_action( Estimate_Earning::CRON_HOOK, array( $this, 'lasso_lite_weekly_estimate_earning' ) );
29 +
55 30 $this->lasso_create_schedule_hook();
56 31 }
57 32
58 33 /**
@@ -65,76 +40,18 @@
65 40 'interval' => 15 * MINUTE_IN_SECONDS, // ? 15 minutes in seconds
66 41 'display' => __( '15 minutes' ),
67 42 );
68 43
69 - if ( ! isset( $schedules['weekly'] ) ) {
70 - $schedules['weekly'] = array(
71 - 'interval' => WEEK_IN_SECONDS,
72 - 'display' => __( 'Once Weekly' ),
73 - );
74 - }
75 -
76 44 return $schedules;
77 45 }
78 46
79 47 /**
80 - * Upgrade the stored cron option structure to version 2 (hash-keyed args).
81 - *
82 - * Mirrors WordPress core cron array shape so iteration in this class matches
83 - * `wp_next_scheduled` / `wp_schedule_event` expectations.
84 - *
85 - * @param array $cron Cron info array from lasso_get_stored_cron_array().
86 - * @return array Upgraded cron info array.
87 - */
88 - private function lasso_upgrade_stored_cron_array( $cron ) {
89 - if ( isset( $cron['version'] ) && 2 === $cron['version'] ) {
90 - return $cron;
91 - }
92 -
93 - $new_cron = array();
94 -
95 - foreach ( (array) $cron as $timestamp => $hooks ) {
96 - foreach ( (array) $hooks as $hook => $args ) {
97 - $key = md5( serialize( $args['args'] ) );
98 -
99 - $new_cron[ $timestamp ][ $hook ][ $key ] = $args;
100 - }
101 - }
102 -
103 - $new_cron['version'] = 2;
104 -
105 - update_option( 'cron', $new_cron );
106 -
107 - return $new_cron;
108 - }
109 -
110 - /**
111 - * Load the `cron` option as an array, upgrading legacy shape when needed.
112 - *
113 - * @return array Cron events (no `version` key in the returned array).
114 - */
115 - private function lasso_get_stored_cron_array() {
116 - $cron = get_option( 'cron' );
117 - if ( ! is_array( $cron ) ) {
118 - return array();
119 - }
120 -
121 - if ( ! isset( $cron['version'] ) ) {
122 - $cron = $this->lasso_upgrade_stored_cron_array( $cron );
123 - }
124 -
125 - unset( $cron['version'] );
126 -
127 - return $cron;
128 - }
129 -
130 - /**
131 48 * Create hook for the new cron
132 49 */
133 50 public function lasso_create_schedule_hook() {
134 51 $crons = self::CRONS;
135 52 $events = array();
136 - $crons_array = $this->lasso_get_stored_cron_array();
53 + $crons_array = _get_cron_array();
137 54
138 55 if ( ! is_array( $crons_array ) ) {
139 56 return;
140 57 }
@@ -139,11 +56,8 @@
139 56 return;
140 57 }
141 58
142 59 foreach ( $crons_array as $time => $cron ) {
143 - if ( ! is_array( $cron ) ) {
144 - continue;
145 - }
146 60 foreach ( $cron as $hook => $dings ) {
147 61 if ( strpos( $hook, 'lasso_lite_' ) === false ) {
148 62 continue;
149 63 }
@@ -161,270 +75,21 @@
161 75 'time' => $time, // ? UTC
162 76 'schedule' => $data['schedule'],
163 77 'interval' => $interval,
164 78 );
79 +
165 80 }
166 81 }
167 82 }
168 83
169 - $load_slot = self::site_load_slot( self::site_schedule_key() );
170 -
171 84 foreach ( $crons as $cron_name => $interval ) {
172 - $next_scheduled = wp_next_scheduled( $cron_name );
173 - $is_daily = ( 'daily' === $interval );
174 - $is_15m = ( 'lasso_lite_15_minutes' === $interval );
175 - $is_weekly = ( 'weekly' === $interval );
176 -
177 - if ( $next_scheduled && $is_daily && ! self::timestamp_matches_daily_slot( $next_scheduled, $load_slot ) ) {
178 - wp_clear_scheduled_hook( $cron_name );
179 - $next_scheduled = false;
180 - } elseif ( $next_scheduled && $is_15m && ! self::timestamp_matches_15m_slot( $next_scheduled, $load_slot ) ) {
181 - wp_clear_scheduled_hook( $cron_name );
182 - $next_scheduled = false;
183 - } elseif ( $next_scheduled && $is_weekly && ! self::timestamp_matches_daily_slot( $next_scheduled, $load_slot ) ) {
184 - wp_clear_scheduled_hook( $cron_name );
185 - $next_scheduled = false;
85 + if ( ! wp_next_scheduled( $cron_name ) ) {
86 + wp_schedule_event( time(), $interval, $cron_name );
186 87 }
187 -
188 - if ( ! $next_scheduled ) {
189 - $current_time = time();
190 - $next_run_time = $current_time;
191 - if ( $is_daily || $is_weekly ) {
192 - $next_run_time = self::next_daily_run_ts( $load_slot, $current_time );
193 - } elseif ( $is_15m ) {
194 - $next_run_time = self::next_15m_run_ts( $load_slot, $current_time );
195 - }
196 - wp_schedule_event( $next_run_time, $interval, $cron_name );
197 - }
198 88 }
199 89 }
200 90
201 91 /**
202 - * Stable key for load-slot hashing. Prefer site URL.
203 - *
204 - * @return string
205 - */
206 - public static function site_schedule_key() {
207 - $url = '';
208 - if ( function_exists( 'home_url' ) ) {
209 - $url = (string) home_url();
210 - }
211 - if ( function_exists( 'untrailingslashit' ) ) {
212 - $url = untrailingslashit( strtolower( trim( $url ) ) );
213 - } else {
214 - $url = strtolower( trim( $url ) );
215 - }
216 - if ( '' !== $url ) {
217 - return $url;
218 - }
219 -
220 - return 'lasso-missing-site-key';
221 - }
222 -
223 - /**
224 - * Deterministic minute-of-day slot from a unique site key.
225 - *
226 - * @param string $site_key home_url.
227 - * @return array
228 - */
229 - public static function site_load_slot( $site_key ) {
230 - $key = (string) $site_key;
231 - if ( '' === $key ) {
232 - $key = 'lasso-missing-site-key';
233 - }
234 -
235 - $minute_of_day = abs( crc32( $key ) ) % 1440;
236 -
237 - return array(
238 - 'minute_of_day' => $minute_of_day,
239 - 'hour' => (int) floor( $minute_of_day / 60 ),
240 - 'minute' => $minute_of_day % 60,
241 - );
242 - }
243 -
244 - /**
245 - * Next daily UTC timestamp at the hashed hour:minute.
246 - *
247 - * @param array $slot site_load_slot().
248 - * @param int $current_time Unix timestamp.
249 - * @return int
250 - */
251 - public static function next_daily_run_ts( $slot, $current_time ) {
252 - $hour = isset( $slot['hour'] ) ? max( 0, min( 23, (int) $slot['hour'] ) ) : 0;
253 - $minute = isset( $slot['minute'] ) ? max( 0, min( 59, (int) $slot['minute'] ) ) : 0;
254 - $today = gmmktime( $hour, $minute, 0 );
255 - if ( $today > $current_time ) {
256 - return $today;
257 - }
258 -
259 - return $today + DAY_IN_SECONDS;
260 - }
261 -
262 - /**
263 - * Next 15-minute UTC timestamp at hashed offset inside the window.
264 - *
265 - * @param array $slot site_load_slot().
266 - * @param int $current_time Unix timestamp.
267 - * @return int
268 - */
269 - public static function next_15m_run_ts( $slot, $current_time ) {
270 - $offset = isset( $slot['minute'] ) ? ( (int) $slot['minute'] % 15 ) : 0;
271 - $cur_min = (int) gmdate( 'i', $current_time );
272 - $hour = (int) gmdate( 'G', $current_time );
273 - $window = $cur_min - ( $cur_min % 15 );
274 - $candidate = gmmktime( $hour, $window + $offset, 0 );
275 - if ( $candidate > $current_time ) {
276 - return $candidate;
277 - }
278 -
279 - return $candidate + ( 15 * MINUTE_IN_SECONDS );
280 - }
281 -
282 - /**
283 - * Whether a scheduled timestamp sits on the daily slot.
284 - *
285 - * @param int $timestamp Unix timestamp.
286 - * @param array $slot site_load_slot().
287 - * @return bool
288 - */
289 - public static function timestamp_matches_daily_slot( $timestamp, $slot ) {
290 - return (int) gmdate( 'G', $timestamp ) === (int) $slot['hour']
291 - && (int) gmdate( 'i', $timestamp ) === (int) $slot['minute'];
292 - }
293 -
294 - /**
295 - * Whether a 15-minute event sits on the hashed offset.
296 - *
297 - * @param int $timestamp Unix timestamp.
298 - * @param array $slot site_load_slot().
299 - * @return bool
300 - */
301 - public static function timestamp_matches_15m_slot( $timestamp, $slot ) {
302 - $offset = isset( $slot['minute'] ) ? ( (int) $slot['minute'] % 15 ) : 0;
303 - return ( (int) gmdate( 'i', $timestamp ) % 15 ) === $offset;
304 - }
305 -
306 - /**
307 - * 0–499ms delay so one site's URL list does not hit lasso.link as one burst.
308 - *
309 - * @param string $site_key Site URL.
310 - * @param string $item_key Request URL.
311 - * @return int
312 - */
313 - public static function request_spread_delay_ms( $site_key, $item_key ) {
314 - return abs( crc32( (string) $site_key . "\0" . (string) $item_key ) ) % 500;
315 - }
316 -
317 - /**
318 - * Whether the current request should spread lasso.link API calls.
319 - *
320 - * Cron handlers enqueue background-process work that runs via admin-ajax.php
321 - * where DOING_CRON is false; LASSO_LITE_BACKGROUND_PROCESS marks those workers.
322 - *
323 - * @return bool
324 - */
325 - public static function is_paced_background_context() {
326 - if ( defined( 'DOING_CRON' ) && DOING_CRON ) {
327 - return true;
328 - }
329 -
330 - return defined( 'LASSO_LITE_BACKGROUND_PROCESS' ) && LASSO_LITE_BACKGROUND_PROCESS;
331 - }
332 -
333 - /**
334 - * Sleep only on cron/background API calls. Admin save stays instant.
335 - *
336 - * @param string $item_key Request URL.
337 - * @param bool $is_lasso_save Interactive save.
338 - * @return int Milliseconds slept (0 when skipped).
339 - */
340 - public static function maybe_pace_background_request( $item_key, $is_lasso_save = false ) {
341 - if ( $is_lasso_save ) {
342 - return 0;
343 - }
344 - if ( ! self::is_paced_background_context() ) {
345 - return 0;
346 - }
347 -
348 - $ms = self::request_spread_delay_ms( self::site_schedule_key(), $item_key );
349 - $ms = (int) apply_filters( 'lasso_lite_request_spread_delay_ms', $ms, $item_key );
350 - if ( $ms > 0 && $ms < 500 ) {
351 - usleep( $ms * 1000 );
352 - }
353 -
354 - return $ms;
355 - }
356 -
357 - const OPTION_BLS_LAST_TICK = 'lasso_lite_bls_last_tick';
358 -
359 - /**
360 - * Per-URL minute-of-day due slot (site + url).
361 - *
362 - * @param string $site_key Site URL.
363 - * @param string $item_key Request URL.
364 - * @return int
365 - */
366 - public static function item_due_minute( $site_key, $item_key ) {
367 - return abs( crc32( (string) $site_key . "\0" . (string) $item_key ) ) % 1440;
368 - }
369 -
370 - /**
371 - * Late = due more than an hour ago (skip the line vs current-hour items).
372 - *
373 - * @param int $due_minute Minute of day.
374 - * @param int $now Unix timestamp.
375 - * @return bool
376 - */
377 - public static function is_late_data_request( $due_minute, $now ) {
378 - $now_minute = ( (int) gmdate( 'G', $now ) * 60 ) + (int) gmdate( 'i', $now );
379 - $age = ( $now_minute - (int) $due_minute + 1440 ) % 1440;
380 - return $age > 60;
381 - }
382 -
383 - /**
384 - * Hourly due window: current hour plus up to 2h of late catch-up.
385 - *
386 - * @param string $item_key Request URL.
387 - * @param int|null $now Unix timestamp.
388 - * @param int|null $last_tick Previous tick (0 = first run).
389 - * @param int|null $due_minute Injected slot for tests.
390 - * @return bool
391 - */
392 - public static function should_send_scheduled_data_request( $item_key, $now = null, $last_tick = null, $due_minute = null ) {
393 - $now = null === $now ? time() : (int) $now;
394 - if ( null === $last_tick ) {
395 - $last_tick = (int) get_option( self::OPTION_BLS_LAST_TICK, 0 );
396 - }
397 - if ( $last_tick <= 0 ) {
398 - $last_tick = $now - HOUR_IN_SECONDS;
399 - }
400 -
401 - $window_start = max( $last_tick - 300, $now - ( 2 * HOUR_IN_SECONDS ) );
402 - if ( null === $due_minute ) {
403 - $due_minute = self::item_due_minute( self::site_schedule_key(), $item_key );
404 - }
405 -
406 - $midnight = gmmktime( 0, 0, 0, (int) gmdate( 'n', $now ), (int) gmdate( 'j', $now ), (int) gmdate( 'Y', $now ) );
407 - $due_today = $midnight + ( (int) $due_minute * 60 );
408 - if ( $due_today <= $now ) {
409 - return $due_today > $window_start;
410 - }
411 -
412 - // Due later today: still catch yesterday's occurrence inside the 2h window
413 - // (WP-Cron often fires after UTC midnight and would otherwise skip 22:00–23:59).
414 - return ( $due_today - DAY_IN_SECONDS ) > $window_start;
415 - }
416 -
417 - /**
418 - * Close this hour's window so the next tick only opens new + late-capped work.
419 - *
420 - * @param int|null $now Unix timestamp.
421 - */
422 - public static function advance_data_request_tick( $now = null ) {
423 - update_option( self::OPTION_BLS_LAST_TICK, null === $now ? time() : (int) $now, false );
424 - }
425 -
426 - /**
427 92 * Tracking support status
428 93 */
429 94 public function lasso_lite_tracking_support_status() {
430 95 $settings = Setting::get_settings();
@@ -441,147 +106,6 @@
441 106 if ( 1 === intval( $allow_import_all ) ) {
442 107 $lasso_import_all = new Import_All();
443 108 $lasso_import_all->import();
444 109 }
445 - }
446 -
447 - /**
448 - * Revert all
449 - */
450 - public function lasso_revert_all() {
451 - $allow_revert_all = get_option( Revert_All::OPTION, '0' );
452 - if ( 1 === intval( $allow_revert_all ) ) {
453 - $lasso_import_all = new Revert_All();
454 - $lasso_import_all->revert();
455 - }
456 - }
457 -
458 - /**
459 - * Revert all
460 - */
461 - public function lasso_lite_update_amazon() {
462 - $settings = Setting::get_settings();
463 - if ( boolval( $settings['amazon_pricing_daily'] ) ) {
464 - $lasso_amazon = new Amazon();
465 - $lasso_amazon->run();
466 - }
467 - }
468 -
469 - /**
470 - * Revert all
471 - */
472 - public function lasso_lite_amazon_shortlink() {
473 - $settings = Setting::get_settings();
474 - if ( boolval( $settings['amazon_pricing_daily'] ) ) {
475 - $lasso_amazon = new Amazon_Shortlink();
476 - $lasso_amazon->run();
477 - }
478 - }
479 -
480 - /**
481 - * Update license status.
482 - */
483 - public function lasso_lite_update_license_status() {
484 - License::check_user_license();
485 - }
486 -
487 - /**
488 - * Daily update snippet: Fetch snippet performance and write to connect-snippet.min.js
489 - */
490 - public function lasso_lite_cron_get_snippet() {
491 - try {
492 - $url = Constant::LASSO_LINK . '/api/snippet/performance?ver=' . time();
493 - $res = Helper::send_request( 'get', $url );
494 -
495 - $status_code = isset( $res['status_code'] ) ? intval( $res['status_code'] ) : 0;
496 - $body = isset( $res['response'] ) ? ( $res['response']->content ?? '' ) : '';
497 - $body_str = is_string( $body ) ? $body : wp_json_encode( $body );
498 -
499 - if ( 200 === $status_code && ! empty( $body_str ) && strpos( $body_str, 'LASSO_REDIRECT_AMAZON_URL,' ) !== false ) {
500 - $file_path = LASSO_CONNECT_SNIPPET_FILE_LITE;
501 - $result = file_put_contents( $file_path, (string) $body_str );
502 - if ( false === $result ) {
503 - return false;
504 - }
505 - }
506 - return true;
507 - } catch ( \Exception $e ) {
508 - return false;
509 - }
510 - }
511 -
512 - /**
513 - * Fetches the JS domain URL from the remote API and updates the 'js_domain' option if valid.
514 - *
515 - * @return string Returns an empty string. Returns early on exception or after processing.
516 - * @throws \Exception This method catches all exceptions internally and does not throw.
517 - */
518 - public function lasso_lite_cron_get_js_domain() {
519 - try {
520 - $url = Constant::LASSO_LINK . '/api/js-domain?ver=' . time();
521 - $res = Helper::send_request( 'get', $url );
522 - $status_code = intval( $res['status_code'] ?? 500 );
523 - $response = $res['response'] ?? '';
524 -
525 - if ( 200 === $status_code && $response ) {
526 - // API returns JSON object that contains a URL to the JS file
527 - $file_url = $response->url ?? '';
528 - // Only return the URL. Do not fetch or write files here.
529 - if ( ! empty( $file_url ) && Helper::validate_url( $file_url ) ) {
530 - Helper::update_option( 'js_domain', $file_url );
531 - }
532 -
533 - $full_file_url = $response->full_url ?? '';
534 - if ( ! empty( $full_file_url ) && Helper::validate_url( $full_file_url ) ) {
535 - Helper::update_option( 'full_js_domain', $full_file_url );
536 - }
537 - }
538 - } catch ( \Exception $e ) {
539 - return '';
540 - }
541 -
542 - return '';
543 - }
544 -
545 - /**
546 - * Get info
547 - */
548 - public function lasso_lite_cron_get_info() {
549 - try {
550 - License::lasso_getinfo(['license_key']);
551 - } catch ( \Exception $e ) {
552 - return false;
553 - }
554 -
555 - return true;
556 - }
557 -
558 - /**
559 - * Daily: request lite user record for this site's admin email.
560 - *
561 - * @return bool True when the HTTP request completes with 200.
562 - */
563 - public function lasso_lite_check_lite_user() {
564 - try {
565 - $admin_email = get_option( 'admin_email' );
566 - if ( empty( $admin_email ) || ! is_email( $admin_email ) ) {
567 - return false;
568 - }
569 -
570 - $url = Constant::LASSO_LINK . '/plugin/lite/users/' . rawurlencode( $admin_email );
571 - $headers = Helper::get_headers();
572 - $res = Helper::send_request( 'get', $url, array(), $headers );
573 -
574 - $status_code = isset( $res['status_code'] ) ? intval( $res['status_code'] ) : 0;
575 - return 200 === $status_code;
576 - } catch ( \Exception $e ) {
577 - return false;
578 - }
579 - }
580 -
581 - /**
582 - * Weekly: refresh cached orphan Affiliate+ payout estimate for the weekly banner.
583 - */
584 - public function lasso_lite_weekly_estimate_earning() {
585 - Estimate_Earning::fetch_and_cache();
586 110 }
587 111 }