Plugin Directory

source: litespeed-cache/trunk/src/task.cls.php

Last change on this file was 3421352, checked in by LiteSpeedTech, 3 months ago

Release v7.7

File size: 7.0 KB
Line 
1<?php
2/**
3 * The cron task class.
4 *
5 * @since   1.1.3
6 * @package LiteSpeed
7 */
8
9namespace LiteSpeed;
10
11defined( 'WPINC' ) || exit();
12
13/**
14 * Schedules and runs LiteSpeed Cache background tasks.
15 */
16class Task extends Root {
17
18        /**
19         * Tag for debug logs.
20         *
21         * @var string
22         */
23        const LOG_TAG = '⏰';
24
25        /**
26         * Map of option id => cron hook registration.
27         *
28         * @var array<string,array{name:string,hook:callable|string}>
29         */
30        private static $_triggers = [
31                Base::O_IMG_OPTM_CRON => [
32                        'name' => 'litespeed_task_imgoptm_pull',
33                        'hook' => 'LiteSpeed\Img_Optm::start_async_cron',
34                ], // always fetch immediately
35                Base::O_OPTM_CSS_ASYNC => [
36                        'name' => 'litespeed_task_ccss',
37                        'hook' => 'LiteSpeed\CSS::cron_ccss',
38                ],
39                Base::O_OPTM_UCSS => [
40                        'name' => 'litespeed_task_ucss',
41                        'hook' => 'LiteSpeed\UCSS::cron',
42                ],
43                Base::O_MEDIA_VPI_CRON => [
44                        'name' => 'litespeed_task_vpi',
45                        'hook' => 'LiteSpeed\VPI::cron',
46                ],
47                Base::O_MEDIA_PLACEHOLDER_RESP_ASYNC => [
48                        'name' => 'litespeed_task_lqip',
49                        'hook' => 'LiteSpeed\Placeholder::cron',
50                ],
51                Base::O_DISCUSS_AVATAR_CRON => [
52                        'name' => 'litespeed_task_avatar',
53                        'hook' => 'LiteSpeed\Avatar::cron',
54                ],
55                Base::O_IMG_OPTM_AUTO => [
56                        'name' => 'litespeed_task_imgoptm_req',
57                        'hook' => 'LiteSpeed\Img_Optm::cron_auto_request',
58                ],
59                Base::O_GUEST => [
60                        'name' => 'litespeed_task_guest_sync',
61                        'hook' => 'LiteSpeed\Guest::cron',
62                ], // Daily sync Guest Mode IP/UA lists
63                Base::O_CRAWLER => [
64                        'name' => 'litespeed_task_crawler',
65                        'hook' => 'LiteSpeed\Crawler::start_async_cron',
66                ], // Set crawler to last one to use above results
67        ];
68
69        /**
70         * Options allowed to run for guest optimization.
71         *
72         * @var array<int,string>
73         */
74        private static $_guest_options = [ Base::O_OPTM_CSS_ASYNC, Base::O_OPTM_UCSS, Base::O_MEDIA_VPI ];
75
76        /**
77         * Schedule id for crawler.
78         *
79         * @var string
80         */
81        const FILTER_CRAWLER = 'litespeed_crawl_filter';
82
83        /**
84         * Schedule id for general tasks.
85         *
86         * @var string
87         */
88        const FILTER = 'litespeed_filter';
89
90        /**
91         * Keep all tasks in cron.
92         *
93         * @since 3.0
94         * @access public
95         * @return void
96         */
97        public function init() {
98                self::debug2( 'Init' );
99                add_filter( 'cron_schedules', [ $this, 'lscache_cron_filter' ] );
100
101                $guest_optm = $this->conf( Base::O_GUEST ) && $this->conf( Base::O_GUEST_OPTM );
102
103                foreach ( self::$_triggers as $id => $trigger ) {
104                        if ( Base::O_IMG_OPTM_CRON === $id ) {
105                                if ( ! Img_Optm::need_pull() ) {
106                                        continue;
107                                }
108                        } elseif ( ! $this->conf( $id ) ) {
109                                if ( ! $guest_optm || ! in_array( $id, self::$_guest_options, true ) ) {
110                                        continue;
111                                }
112                        }
113
114                        // Special check for crawler.
115                        if ( Base::O_CRAWLER === $id ) {
116                                if ( ! Router::can_crawl() ) {
117                                        continue;
118                                }
119
120                                add_filter( 'cron_schedules', [ $this, 'lscache_cron_filter_crawler' ] ); // phpcs:ignore WordPress.WP.CronInterval.ChangeDetected
121                        }
122
123                        if ( ! wp_next_scheduled( $trigger['name'] ) ) {
124                                self::debug( 'Cron hook register [name] ' . $trigger['name'] );
125
126                                // Determine schedule: crawler uses its own, guest uses daily, others use 15min
127                                if ( Base::O_CRAWLER === $id ) {
128                                        $schedule = self::FILTER_CRAWLER;
129                                } elseif ( Base::O_GUEST === $id ) {
130                                        $schedule = 'daily';
131                                } else {
132                                        $schedule = self::FILTER;
133                                }
134
135                                wp_schedule_event( time(), $schedule, $trigger['name'] );
136                        }
137
138                        add_action( $trigger['name'], $trigger['hook'] );
139                }
140        }
141
142        /**
143         * Handle all async noabort requests.
144         *
145         * @since 5.5
146         * @return void
147         */
148        public static function async_litespeed_handler() {
149                $hash_data = self::get_option( 'async_call-hash', [] );
150                if ( ! $hash_data || ! is_array( $hash_data ) || empty( $hash_data['hash'] ) || empty( $hash_data['ts'] ) ) {
151                        self::debug( 'async_litespeed_handler no hash data', $hash_data );
152                        return;
153                }
154
155                $nonce = isset( $_GET['nonce'] ) ? sanitize_text_field( wp_unslash( $_GET['nonce'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
156                if ( 120 < time() - (int) $hash_data['ts'] || '' === $nonce || $nonce !== $hash_data['hash'] ) {
157                        self::debug( 'async_litespeed_handler nonce mismatch' );
158                        return;
159                }
160                self::delete_option( 'async_call-hash' );
161
162                $type = Router::verify_type();
163                self::debug( 'type=' . $type );
164
165                // Don't lock up other requests while processing.
166                session_write_close();
167
168                switch ( $type ) {
169                        case 'crawler':
170                                Crawler::async_handler();
171                                break;
172                        case 'crawler_force':
173                                Crawler::async_handler( true );
174                                break;
175                        case 'imgoptm':
176                                Img_Optm::async_handler();
177                                break;
178                        case 'imgoptm_force':
179                                Img_Optm::async_handler( true );
180                                break;
181                        default:
182                                break;
183                }
184        }
185
186        /**
187         * Async caller wrapper func.
188         *
189         * @since 5.5
190         *
191         * @param string $type Async operation type.
192         * @return void
193         */
194        public static function async_call( $type ) {
195                $hash = Str::rrand( 32 );
196                self::update_option(
197                        'async_call-hash',
198                        [
199                                'hash' => $hash,
200                                'ts'   => time(),
201                        ]
202                );
203
204                $args = [
205                        'timeout'   => 0.01,
206                        'blocking'  => false,
207                        'sslverify' => false,
208                        // 'cookies' => $_COOKIE,
209                ];
210
211                $qs = [
212                        'action'      => 'async_litespeed',
213                        'nonce'       => $hash,
214                        Router::TYPE  => $type,
215                ];
216
217                $url = add_query_arg( $qs, admin_url( 'admin-ajax.php' ) );
218                self::debug( 'async call to ' . $url );
219                wp_safe_remote_post( esc_url_raw( $url ), $args );
220        }
221
222        /**
223         * Clean all potential existing crons.
224         *
225         * @since 3.0
226         * @access public
227         * @return void
228         */
229        public static function destroy() {
230                Utility::compatibility();
231                array_map( 'wp_clear_scheduled_hook', array_column( self::$_triggers, 'name' ) );
232        }
233
234        /**
235         * Try to clean the crons if disabled.
236         *
237         * @since 3.0
238         * @access public
239         *
240         * @param string $id Option id of cron trigger.
241         * @return void
242         */
243        public function try_clean( $id ) {
244                if ( $id && ! empty( self::$_triggers[ $id ] ) ) {
245                        if ( ! $this->conf( $id ) || ( Base::O_CRAWLER === $id && ! Router::can_crawl() ) ) {
246                                self::debug( 'Cron clear [id] ' . $id . ' [hook] ' . self::$_triggers[ $id ]['name'] );
247                                wp_clear_scheduled_hook( self::$_triggers[ $id ]['name'] );
248                        }
249                        return;
250                }
251
252                self::debug( '❌ Unknown cron [id] ' . $id );
253        }
254
255        /**
256         * Register cron interval for general tasks.
257         *
258         * @since 1.6.1
259         * @access public
260         *
261         * @param array $schedules Existing schedules.
262         * @return array
263         */
264        public function lscache_cron_filter( $schedules ) {
265                if ( ! array_key_exists( self::FILTER, $schedules ) ) {
266                        $schedules[ self::FILTER ] = [
267                                'interval' => 900,
268                                'display'  => __( 'Every 15 Minutes', 'litespeed-cache' ),
269                        ];
270                }
271                return $schedules;
272        }
273
274        /**
275         * Register cron interval for crawler.
276         *
277         * @since 1.1.0
278         * @access public
279         *
280         * @param array $schedules Existing schedules.
281         * @return array
282         */
283        public function lscache_cron_filter_crawler( $schedules ) {
284                $crawler_run_interval = defined( 'LITESPEED_CRAWLER_RUN_INTERVAL' ) ? (int) constant( 'LITESPEED_CRAWLER_RUN_INTERVAL' ) : 600;
285
286                if ( ! array_key_exists( self::FILTER_CRAWLER, $schedules ) ) {
287                        $schedules[ self::FILTER_CRAWLER ] = [
288                                'interval' => $crawler_run_interval,
289                                'display'  => __( 'LiteSpeed Crawler Cron', 'litespeed-cache' ),
290                        ];
291                }
292                return $schedules;
293        }
294}
Note: See TracBrowser for help on using the repository browser.