LiteSpeed Cache - Version 3.4.1

Version Description

  • Sep 2 2020 =
  • CCSS Fixed an issue where dynamically generated CSS failed with TypeError: Cannot read property type of undefined.
  • Page Optimize Fixed CSS optimization compatibility for CSS dynamically generated with PHP.
  • Page Optimize Added the ability to defer JS even when the resource is excluded from other JS optimizations. (@slr1979)
  • ESI Added support for ESI last paramater inline value.
  • 3rd YITH Wishlist, when cached for the first time, will no longer send sub requests.
Download this release

Release Info

Developer LiteSpeedTech
Plugin Icon 128x128 LiteSpeed Cache
Version 3.4.1
Comparing to
See all releases

Code changes from version 3.4 to 3.4.1

litespeed-cache.php CHANGED
@@ -3,7 +3,7 @@
3
  * Plugin Name: LiteSpeed Cache
4
  * Plugin URI: https://www.litespeedtech.com/products/cache-plugins/wordpress-acceleration
5
  * Description: High-performance page caching and site optimization from LiteSpeed
6
- * Version: 3.4
7
  * Author: LiteSpeed Technologies
8
  * Author URI: https://www.litespeedtech.com
9
  * License: GPLv3
@@ -33,7 +33,7 @@ if ( class_exists( 'LiteSpeed\Core' ) || defined( 'LSCWP_DIR' ) ) {
33
  return;
34
  }
35
 
36
- ! defined( 'LSCWP_V' ) && define( 'LSCWP_V', '3.4' );
37
 
38
  ! defined( 'LSCWP_CONTENT_DIR' ) && define( 'LSCWP_CONTENT_DIR', WP_CONTENT_DIR ) ;
39
  ! defined( 'LSCWP_DIR' ) && define( 'LSCWP_DIR', __DIR__ . '/' ) ;// Full absolute path '/var/www/html/***/wp-content/plugins/litespeed-cache/' or MU
3
  * Plugin Name: LiteSpeed Cache
4
  * Plugin URI: https://www.litespeedtech.com/products/cache-plugins/wordpress-acceleration
5
  * Description: High-performance page caching and site optimization from LiteSpeed
6
+ * Version: 3.4.1
7
  * Author: LiteSpeed Technologies
8
  * Author URI: https://www.litespeedtech.com
9
  * License: GPLv3
33
  return;
34
  }
35
 
36
+ ! defined( 'LSCWP_V' ) && define( 'LSCWP_V', '3.4.1' );
37
 
38
  ! defined( 'LSCWP_CONTENT_DIR' ) && define( 'LSCWP_CONTENT_DIR', WP_CONTENT_DIR ) ;
39
  ! defined( 'LSCWP_DIR' ) && define( 'LSCWP_DIR', __DIR__ . '/' ) ;// Full absolute path '/var/www/html/***/wp-content/plugins/litespeed-cache/' or MU
readme.txt CHANGED
@@ -3,7 +3,7 @@ Contributors: LiteSpeedTech
3
  Tags: caching, optimize, performance, pagespeed, seo, speed, image optimize, compress, object cache, redis, memcached, database cleaner
4
  Requires at least: 4.0
5
  Tested up to: 5.5
6
- Stable tag: 3.4
7
  License: GPLv3
8
  License URI: http://www.gnu.org/licenses/gpl.html
9
 
@@ -245,6 +245,13 @@ The vast majority of plugins and themes are compatible with LiteSpeed Cache. The
245
 
246
  == Changelog ==
247
 
 
 
 
 
 
 
 
248
  = 3.4 - Aug 26 2020 =
249
  * 🌱**LQIP** New setting **LQIP Excludes**.
250
  * 🌱**LQIP** Added a Clear LQIP Queue button.
3
  Tags: caching, optimize, performance, pagespeed, seo, speed, image optimize, compress, object cache, redis, memcached, database cleaner
4
  Requires at least: 4.0
5
  Tested up to: 5.5
6
+ Stable tag: 3.4.1
7
  License: GPLv3
8
  License URI: http://www.gnu.org/licenses/gpl.html
9
 
245
 
246
  == Changelog ==
247
 
248
+ = 3.4.1 - Sep 2 2020 =
249
+ * 🐞**CCSS** Fixed an issue where dynamically generated CSS failed with `TypeError: Cannot read property type of undefined`.
250
+ * 🐞**Page Optimize** Fixed CSS optimization compatibility for CSS dynamically generated with PHP.
251
+ * **Page Optimize** Added the ability to defer JS even when the resource is excluded from other JS optimizations. (@slr1979)
252
+ * **ESI** Added support for ESI last paramater inline value.
253
+ * **3rd** YITH Wishlist, when cached for the first time, will no longer send sub requests.
254
+
255
  = 3.4 - Aug 26 2020 =
256
  * 🌱**LQIP** New setting **LQIP Excludes**.
257
  * 🌱**LQIP** Added a Clear LQIP Queue button.
src/admin.cls.php CHANGED
@@ -8,17 +8,16 @@
8
  * @subpackage LiteSpeed_Cache/admin
9
  * @author LiteSpeed Technologies <info@litespeedtech.com>
10
  */
11
- namespace LiteSpeed ;
12
 
13
- defined( 'WPINC' ) || exit ;
14
 
15
- class Admin extends Instance
16
- {
17
  const PAGE_EDIT_HTACCESS = 'litespeed-edit-htaccess';
18
 
19
- protected static $_instance ;
20
- private $__cfg ;// cfg instance
21
- private $display ;
22
 
23
  /**
24
  * Initialize the class and set its properties.
@@ -26,41 +25,40 @@ class Admin extends Instance
26
  *
27
  * @since 1.0.0
28
  */
29
- protected function __construct()
30
- {
31
  // Define LSCWP_MU_PLUGIN if is mu-plugins
32
  if ( defined( 'WPMU_PLUGIN_DIR' ) && dirname( LSCWP_DIR ) == WPMU_PLUGIN_DIR ) {
33
- define( 'LSCWP_MU_PLUGIN', true ) ;
34
  }
35
 
36
  // Additional litespeed assets on admin display
37
  // Also register menu
38
- $this->display = Admin_Display::get_instance() ;
39
 
40
- $this->__cfg = Conf::get_instance() ;
41
 
42
  // initialize admin actions
43
- add_action( 'admin_init', array( $this, 'admin_init' ) ) ;
44
  // add link to plugin list page
45
- add_filter( 'plugin_action_links_' . LSCWP_BASENAME, array( $this->display, 'add_plugin_links' ) ) ;
46
 
47
  if ( defined( 'LITESPEED_ON' ) ) {
48
  // register purge_all actions
49
- $purge_all_events = Conf::val( Base::O_PURGE_HOOK_ALL ) ;
50
 
51
  // purge all on upgrade
52
  if ( Conf::val( Base::O_PURGE_ON_UPGRADE ) ) {
53
- $purge_all_events[] = 'upgrader_process_complete' ;
54
- $purge_all_events[] = 'admin_action_do-plugin-upgrade' ;
55
  }
56
  foreach ( $purge_all_events as $event ) {
57
  // Don't allow hook to update_option bcos purge_all will cause infinite loop of update_option
58
  if ( in_array( $event, array( 'update_option' ) ) ) {
59
- continue ;
60
  }
61
- add_action( $event, __NAMESPACE__ . '\Purge::purge_all' ) ;
62
  }
63
- // add_filter( 'upgrader_pre_download', 'Purge::filter_with_purge_all' ) ;
64
  }
65
  }
66
 
@@ -77,13 +75,13 @@ class Admin extends Instance
77
 
78
  // Terminate if user doesn't have the access to settings
79
  if( is_network_admin() ) {
80
- $capability = 'manage_network_options' ;
81
  }
82
  else {
83
- $capability = 'manage_options' ;
84
  }
85
  if ( ! current_user_can($capability) ) {
86
- return ;
87
  }
88
 
89
  // Save setting from admin settings page
@@ -92,14 +90,14 @@ class Admin extends Instance
92
  // Add privacy policy
93
  // @since 2.2.6
94
  if ( function_exists( 'wp_add_privacy_policy_content' ) ) {
95
- wp_add_privacy_policy_content( Core::PLUGIN_NAME, Doc::privacy_policy() ) ;
96
  }
97
 
98
- do_action( 'litspeed_after_admin_init' ) ;
99
 
100
  if ( Router::esi_enabled() ) {
101
- add_action( 'in_widget_form', array( $this->display, 'show_widget_edit' ), 100, 3 ) ;
102
- add_filter( 'widget_update_callback', __NAMESPACE__ . '\Admin_Settings::validate_widget_save', 10, 4 ) ;
103
  }
104
  }
105
 
@@ -108,8 +106,7 @@ class Admin extends Instance
108
  *
109
  * @since 1.1.0
110
  */
111
- private function _proceed_admin_action()
112
- {
113
  // handle actions
114
  switch ( Router::get_action() ) {
115
 
@@ -142,13 +139,12 @@ class Admin extends Instance
142
  * @param string $input The input string to clean.
143
  * @return string The cleaned up input.
144
  */
145
- public static function cleanup_text( $input )
146
- {
147
  if ( is_array( $input ) ) {
148
- return array_map( __CLASS__ . '::cleanup_text', $input ) ;
149
  }
150
 
151
- return stripslashes( trim( $input ) ) ;
152
  }
153
 
154
  /**
@@ -159,8 +155,7 @@ class Admin extends Instance
159
  * @access public
160
  * @global string $pagenow
161
  */
162
- public static function redirect( $url = false )
163
- {
164
  global $pagenow;
165
 
166
  if ( ! empty( $_GET[ '_litespeed_ori' ] ) ) {
8
  * @subpackage LiteSpeed_Cache/admin
9
  * @author LiteSpeed Technologies <info@litespeedtech.com>
10
  */
11
+ namespace LiteSpeed;
12
 
13
+ defined( 'WPINC' ) || exit;
14
 
15
+ class Admin extends Instance {
 
16
  const PAGE_EDIT_HTACCESS = 'litespeed-edit-htaccess';
17
 
18
+ protected static $_instance;
19
+ private $__cfg;// cfg instance
20
+ private $display;
21
 
22
  /**
23
  * Initialize the class and set its properties.
25
  *
26
  * @since 1.0.0
27
  */
28
+ protected function __construct() {
 
29
  // Define LSCWP_MU_PLUGIN if is mu-plugins
30
  if ( defined( 'WPMU_PLUGIN_DIR' ) && dirname( LSCWP_DIR ) == WPMU_PLUGIN_DIR ) {
31
+ define( 'LSCWP_MU_PLUGIN', true );
32
  }
33
 
34
  // Additional litespeed assets on admin display
35
  // Also register menu
36
+ $this->display = Admin_Display::get_instance();
37
 
38
+ $this->__cfg = Conf::get_instance();
39
 
40
  // initialize admin actions
41
+ add_action( 'admin_init', array( $this, 'admin_init' ) );
42
  // add link to plugin list page
43
+ add_filter( 'plugin_action_links_' . LSCWP_BASENAME, array( $this->display, 'add_plugin_links' ) );
44
 
45
  if ( defined( 'LITESPEED_ON' ) ) {
46
  // register purge_all actions
47
+ $purge_all_events = Conf::val( Base::O_PURGE_HOOK_ALL );
48
 
49
  // purge all on upgrade
50
  if ( Conf::val( Base::O_PURGE_ON_UPGRADE ) ) {
51
+ $purge_all_events[] = 'upgrader_process_complete';
52
+ $purge_all_events[] = 'admin_action_do-plugin-upgrade';
53
  }
54
  foreach ( $purge_all_events as $event ) {
55
  // Don't allow hook to update_option bcos purge_all will cause infinite loop of update_option
56
  if ( in_array( $event, array( 'update_option' ) ) ) {
57
+ continue;
58
  }
59
+ add_action( $event, __NAMESPACE__ . '\Purge::purge_all' );
60
  }
61
+ // add_filter( 'upgrader_pre_download', 'Purge::filter_with_purge_all' );
62
  }
63
  }
64
 
75
 
76
  // Terminate if user doesn't have the access to settings
77
  if( is_network_admin() ) {
78
+ $capability = 'manage_network_options';
79
  }
80
  else {
81
+ $capability = 'manage_options';
82
  }
83
  if ( ! current_user_can($capability) ) {
84
+ return;
85
  }
86
 
87
  // Save setting from admin settings page
90
  // Add privacy policy
91
  // @since 2.2.6
92
  if ( function_exists( 'wp_add_privacy_policy_content' ) ) {
93
+ wp_add_privacy_policy_content( Core::PLUGIN_NAME, Doc::privacy_policy() );
94
  }
95
 
96
+ do_action( 'litspeed_after_admin_init' );
97
 
98
  if ( Router::esi_enabled() ) {
99
+ add_action( 'in_widget_form', array( $this->display, 'show_widget_edit' ), 100, 3 );
100
+ add_filter( 'widget_update_callback', __NAMESPACE__ . '\Admin_Settings::validate_widget_save', 10, 4 );
101
  }
102
  }
103
 
106
  *
107
  * @since 1.1.0
108
  */
109
+ private function _proceed_admin_action() {
 
110
  // handle actions
111
  switch ( Router::get_action() ) {
112
 
139
  * @param string $input The input string to clean.
140
  * @return string The cleaned up input.
141
  */
142
+ public static function cleanup_text( $input ) {
 
143
  if ( is_array( $input ) ) {
144
+ return array_map( __CLASS__ . '::cleanup_text', $input );
145
  }
146
 
147
+ return stripslashes( trim( $input ) );
148
  }
149
 
150
  /**
155
  * @access public
156
  * @global string $pagenow
157
  */
158
+ public static function redirect( $url = false ) {
 
159
  global $pagenow;
160
 
161
  if ( ! empty( $_GET[ '_litespeed_ori' ] ) ) {
src/avatar.cls.php CHANGED
@@ -7,20 +7,19 @@
7
  * @subpackage LiteSpeed/inc
8
  * @author LiteSpeed Technologies <info@litespeedtech.com>
9
  */
10
- namespace LiteSpeed ;
11
 
12
- defined( 'WPINC' ) || exit ;
13
 
14
- class Avatar extends Base
15
- {
16
- protected static $_instance ;
17
 
18
- const TYPE_GENERATE = 'generate' ;
19
 
20
- private $_conf_cache_ttl ;
21
- private $_tb ;
22
 
23
- private $_avatar_realtime_gen_dict = array() ;
24
  protected $_summary;
25
 
26
  /**
@@ -29,19 +28,18 @@ class Avatar extends Base
29
  * @since 1.4
30
  * @access protected
31
  */
32
- protected function __construct()
33
- {
34
  if ( ! Conf::val( Base::O_DISCUSS_AVATAR_CACHE ) ) {
35
- return ;
36
  }
37
 
38
- Debug2::debug2( '[Avatar] init' ) ;
39
 
40
- $this->_tb = Data::get_instance()->tb( 'avatar' ) ;
41
 
42
- $this->_conf_cache_ttl = Conf::val( Base::O_DISCUSS_AVATAR_CACHE_TTL ) ;
43
 
44
- add_filter( 'get_avatar_url', array( $this, 'crawl_avatar' ) ) ;
45
 
46
  $this->_summary = self::get_summary();
47
  }
@@ -52,13 +50,12 @@ class Avatar extends Base
52
  * @since 3.0
53
  * @access public
54
  */
55
- public static function need_db()
56
- {
57
  if ( Conf::val( Base::O_DISCUSS_AVATAR_CACHE ) ) {
58
- return true ;
59
  }
60
 
61
- return false ;
62
  }
63
  /**
64
  * Get gravatar URL from DB and regenarate
@@ -66,29 +63,28 @@ class Avatar extends Base
66
  * @since 3.0
67
  * @access public
68
  */
69
- public function serve_satic( $md5 )
70
- {
71
- global $wpdb ;
72
 
73
- Debug2::debug( '[Avatar] is avatar request' ) ;
74
 
75
  if ( strlen( $md5 ) !== 32 ) {
76
- Debug2::debug( '[Avatar] wrong md5 ' . $md5 ) ;
77
- return ;
78
  }
79
 
80
- $q = "SELECT url FROM `$this->_tb` WHERE md5=%s" ;
81
- $url = $wpdb->get_var( $wpdb->prepare( $q, $md5 ) ) ;
82
 
83
  if ( ! $url ) {
84
- Debug2::debug( '[Avatar] no matched url for md5 ' . $md5 ) ;
85
- return ;
86
  }
87
 
88
- $url = $this->_generate( $url ) ;
89
 
90
- wp_redirect( $url ) ;
91
- exit ;
92
  }
93
 
94
  /**
@@ -97,39 +93,38 @@ class Avatar extends Base
97
  * @since 3.0
98
  * @access public
99
  */
100
- public function crawl_avatar( $url )
101
- {
102
  if ( ! $url ) {
103
- return $url ;
104
  }
105
 
106
  // Check if its already in dict or not
107
  if ( ! empty( $this->_avatar_realtime_gen_dict[ $url ] ) ) {
108
- Debug2::debug2( '[Avatar] already in dict [url] ' . $url ) ;
109
 
110
- return $this->_avatar_realtime_gen_dict[ $url ] ;
111
  }
112
 
113
- $realpath = $this->_realpath( $url ) ;
114
  if ( file_exists( $realpath ) && time() - filemtime( $realpath ) <= $this->_conf_cache_ttl ) {
115
- Debug2::debug2( '[Avatar] cache file exists [url] ' . $url ) ;
116
- return $this->_rewrite( $url ) ;
117
  }
118
 
119
  if ( ! strpos( $url, 'gravatar.com' ) ) {
120
- return $url ;
121
  }
122
 
123
  // Send request
124
  if ( ! empty( $this->_summary[ 'curr_request' ] ) && time() - $this->_summary[ 'curr_request' ] < 300 ) {
125
- Debug2::debug2( '[Avatar] Bypass generating due to interval limit [url] ' . $url ) ;
126
- return $url ;
127
  }
128
 
129
  // Generate immediately
130
- $this->_avatar_realtime_gen_dict[ $url ] = $this->_generate( $url ) ;
131
 
132
- return $this->_avatar_realtime_gen_dict[ $url ] ;
133
  }
134
 
135
  /**
@@ -138,9 +133,8 @@ class Avatar extends Base
138
  * @since 3.0
139
  * @access public
140
  */
141
- public static function has_cache()
142
- {
143
- return is_dir( LITESPEED_STATIC_DIR . '/avatar' ) ;
144
  }
145
 
146
  /**
@@ -149,9 +143,8 @@ class Avatar extends Base
149
  * @since 3.0
150
  * @access private
151
  */
152
- private function _mkdir()
153
- {
154
- mkdir( LITESPEED_STATIC_DIR . '/avatar', 0755, true ) ;
155
  }
156
 
157
  /**
@@ -160,8 +153,7 @@ class Avatar extends Base
160
  * @since 3.0
161
  * @access public
162
  */
163
- public function queue_count()
164
- {
165
  global $wpdb;
166
 
167
  // If var not exists, mean table not exists // todo: not true
@@ -180,8 +172,7 @@ class Avatar extends Base
180
  *
181
  * @since 3.0
182
  */
183
- private function _rewrite( $url )
184
- {
185
  return LITESPEED_STATIC_URL . '/avatar/' . md5( $url ) . '.jpg';
186
  }
187
 
@@ -191,8 +182,7 @@ class Avatar extends Base
191
  * @since 3.0
192
  * @access private
193
  */
194
- private function _realpath( $url )
195
- {
196
  return LITESPEED_STATIC_DIR . '/avatar/' . md5( $url ) . '.jpg';
197
  }
198
 
@@ -202,16 +192,15 @@ class Avatar extends Base
202
  * @since 3.0
203
  * @access public
204
  */
205
- public function rm_cache_folder()
206
- {
207
  if ( file_exists( LITESPEED_STATIC_DIR . '/avatar' ) ) {
208
- File::rrmdir( LITESPEED_STATIC_DIR . '/avatar' ) ;
209
  }
210
 
211
  // Clear avatar summary
212
- self::save_summary( array() ) ;
213
 
214
- Debug2::debug2( '[Avatar] Cleared avatar queue' ) ;
215
  }
216
 
217
  /**
@@ -220,32 +209,31 @@ class Avatar extends Base
220
  * @since 3.0
221
  * @access public
222
  */
223
- public static function cron( $force = false )
224
- {
225
- global $wpdb ;
226
 
227
  $_instance = self::get_instance();
228
  if ( ! $_instance->queue_count() ) {
229
- Debug2::debug( '[Avatar] no queue' ) ;
230
- return ;
231
  }
232
 
233
  // For cron, need to check request interval too
234
  if ( ! $force ) {
235
  if ( ! empty( $_instance->_summary[ 'curr_request' ] ) && time() - $_instance->_summary[ 'curr_request' ] < 300 ) {
236
- Debug2::debug( '[Avatar] curr_request too close' ) ;
237
- return ;
238
  }
239
  }
240
 
241
- $q = "SELECT url FROM `$_instance->_tb` WHERE dateline < %d ORDER BY id DESC LIMIT %d" ;
242
- $q = $wpdb->prepare( $q, array( time() - $_instance->_conf_cache_ttl, apply_filters( 'litespeed_avatar_limit', 30 ) ) ) ;
243
 
244
- $list = $wpdb->get_results( $q ) ;
245
- Debug2::debug( '[Avatar] cron job [count] ' . count( $list ) ) ;
246
 
247
  foreach ( $list as $v ) {
248
- Debug2::debug( '[Avatar] cron job [url] ' . $v->url ) ;
249
 
250
  $_instance->_generate( $v->url );
251
  }
@@ -257,52 +245,51 @@ class Avatar extends Base
257
  * @since 3.0
258
  * @access private
259
  */
260
- private function _generate( $url )
261
- {
262
- global $wpdb ;
263
 
264
  // Record the data
265
 
266
- $file = $this->_realpath( $url ) ;
267
 
268
  // Update request status
269
- $this->_summary[ 'curr_request' ] = time() ;
270
- self::save_summary() ;
271
 
272
  // Generate
273
  if ( ! self::has_cache() ) {
274
- $this->_mkdir() ;
275
  }
276
- $response = wp_remote_get( $url, array( 'timeout' => 180, 'stream' => true, 'filename' => $file ) ) ;
277
 
278
- Debug2::debug( '[Avatar] _generate [url] ' . $url ) ;
279
 
280
  // Parse response data
281
  if ( is_wp_error( $response ) ) {
282
- $error_message = $response->get_error_message() ;
283
- file_exists( $file ) && unlink( $file ) ;
284
- Debug2::debug( '[Avatar] failed to get: ' . $error_message ) ;
285
- return $url ;
286
  }
287
 
288
  // Save summary data
289
- $this->_summary[ 'last_spent' ] = time() - $this->_summary[ 'curr_request' ] ;
290
- $this->_summary[ 'last_request' ] = $this->_summary[ 'curr_request' ] ;
291
- $this->_summary[ 'curr_request' ] = 0 ;
292
- self::save_summary() ;
293
 
294
  // Update DB
295
- $md5 = md5( $url ) ;
296
- $q = "UPDATE `$this->_tb` SET dateline=%d WHERE md5=%s" ;
297
- $existed = $wpdb->query( $wpdb->prepare( $q, array( time(), $md5 ) ) ) ;
298
  if ( ! $existed ) {
299
- $q = "INSERT INTO `$this->_tb` SET url=%s, md5=%s, dateline=%d" ;
300
- $wpdb->query( $wpdb->prepare( $q, array( $url, $md5, time() ) ) ) ;
301
  }
302
 
303
- Debug2::debug( '[Avatar] saved avatar ' . $file ) ;
304
 
305
- return $this->_rewrite( $url ) ;
306
  }
307
 
308
  /**
@@ -311,22 +298,21 @@ class Avatar extends Base
311
  * @since 3.0
312
  * @access public
313
  */
314
- public static function handler()
315
- {
316
- $instance = self::get_instance() ;
317
 
318
- $type = Router::verify_type() ;
319
 
320
  switch ( $type ) {
321
  case self::TYPE_GENERATE :
322
- self::cron( true ) ;
323
- break ;
324
 
325
  default:
326
- break ;
327
  }
328
 
329
- Admin::redirect() ;
330
  }
331
 
332
  }
7
  * @subpackage LiteSpeed/inc
8
  * @author LiteSpeed Technologies <info@litespeedtech.com>
9
  */
10
+ namespace LiteSpeed;
11
 
12
+ defined( 'WPINC' ) || exit;
13
 
14
+ class Avatar extends Base {
15
+ protected static $_instance;
 
16
 
17
+ const TYPE_GENERATE = 'generate';
18
 
19
+ private $_conf_cache_ttl;
20
+ private $_tb;
21
 
22
+ private $_avatar_realtime_gen_dict = array();
23
  protected $_summary;
24
 
25
  /**
28
  * @since 1.4
29
  * @access protected
30
  */
31
+ protected function __construct() {
 
32
  if ( ! Conf::val( Base::O_DISCUSS_AVATAR_CACHE ) ) {
33
+ return;
34
  }
35
 
36
+ Debug2::debug2( '[Avatar] init' );
37
 
38
+ $this->_tb = Data::get_instance()->tb( 'avatar' );
39
 
40
+ $this->_conf_cache_ttl = Conf::val( Base::O_DISCUSS_AVATAR_CACHE_TTL );
41
 
42
+ add_filter( 'get_avatar_url', array( $this, 'crawl_avatar' ) );
43
 
44
  $this->_summary = self::get_summary();
45
  }
50
  * @since 3.0
51
  * @access public
52
  */
53
+ public static function need_db() {
 
54
  if ( Conf::val( Base::O_DISCUSS_AVATAR_CACHE ) ) {
55
+ return true;
56
  }
57
 
58
+ return false;
59
  }
60
  /**
61
  * Get gravatar URL from DB and regenarate
63
  * @since 3.0
64
  * @access public
65
  */
66
+ public function serve_satic( $md5 ) {
67
+ global $wpdb;
 
68
 
69
+ Debug2::debug( '[Avatar] is avatar request' );
70
 
71
  if ( strlen( $md5 ) !== 32 ) {
72
+ Debug2::debug( '[Avatar] wrong md5 ' . $md5 );
73
+ return;
74
  }
75
 
76
+ $q = "SELECT url FROM `$this->_tb` WHERE md5=%s";
77
+ $url = $wpdb->get_var( $wpdb->prepare( $q, $md5 ) );
78
 
79
  if ( ! $url ) {
80
+ Debug2::debug( '[Avatar] no matched url for md5 ' . $md5 );
81
+ return;
82
  }
83
 
84
+ $url = $this->_generate( $url );
85
 
86
+ wp_redirect( $url );
87
+ exit;
88
  }
89
 
90
  /**
93
  * @since 3.0
94
  * @access public
95
  */
96
+ public function crawl_avatar( $url ) {
 
97
  if ( ! $url ) {
98
+ return $url;
99
  }
100
 
101
  // Check if its already in dict or not
102
  if ( ! empty( $this->_avatar_realtime_gen_dict[ $url ] ) ) {
103
+ Debug2::debug2( '[Avatar] already in dict [url] ' . $url );
104
 
105
+ return $this->_avatar_realtime_gen_dict[ $url ];
106
  }
107
 
108
+ $realpath = $this->_realpath( $url );
109
  if ( file_exists( $realpath ) && time() - filemtime( $realpath ) <= $this->_conf_cache_ttl ) {
110
+ Debug2::debug2( '[Avatar] cache file exists [url] ' . $url );
111
+ return $this->_rewrite( $url );
112
  }
113
 
114
  if ( ! strpos( $url, 'gravatar.com' ) ) {
115
+ return $url;
116
  }
117
 
118
  // Send request
119
  if ( ! empty( $this->_summary[ 'curr_request' ] ) && time() - $this->_summary[ 'curr_request' ] < 300 ) {
120
+ Debug2::debug2( '[Avatar] Bypass generating due to interval limit [url] ' . $url );
121
+ return $url;
122
  }
123
 
124
  // Generate immediately
125
+ $this->_avatar_realtime_gen_dict[ $url ] = $this->_generate( $url );
126
 
127
+ return $this->_avatar_realtime_gen_dict[ $url ];
128
  }
129
 
130
  /**
133
  * @since 3.0
134
  * @access public
135
  */
136
+ public static function has_cache() {
137
+ return is_dir( LITESPEED_STATIC_DIR . '/avatar' );
 
138
  }
139
 
140
  /**
143
  * @since 3.0
144
  * @access private
145
  */
146
+ private function _mkdir() {
147
+ mkdir( LITESPEED_STATIC_DIR . '/avatar', 0755, true );
 
148
  }
149
 
150
  /**
153
  * @since 3.0
154
  * @access public
155
  */
156
+ public function queue_count() {
 
157
  global $wpdb;
158
 
159
  // If var not exists, mean table not exists // todo: not true
172
  *
173
  * @since 3.0
174
  */
175
+ private function _rewrite( $url ) {
 
176
  return LITESPEED_STATIC_URL . '/avatar/' . md5( $url ) . '.jpg';
177
  }
178
 
182
  * @since 3.0
183
  * @access private
184
  */
185
+ private function _realpath( $url ) {
 
186
  return LITESPEED_STATIC_DIR . '/avatar/' . md5( $url ) . '.jpg';
187
  }
188
 
192
  * @since 3.0
193
  * @access public
194
  */
195
+ public function rm_cache_folder() {
 
196
  if ( file_exists( LITESPEED_STATIC_DIR . '/avatar' ) ) {
197
+ File::rrmdir( LITESPEED_STATIC_DIR . '/avatar' );
198
  }
199
 
200
  // Clear avatar summary
201
+ self::save_summary( array() );
202
 
203
+ Debug2::debug2( '[Avatar] Cleared avatar queue' );
204
  }
205
 
206
  /**
209
  * @since 3.0
210
  * @access public
211
  */
212
+ public static function cron( $force = false ) {
213
+ global $wpdb;
 
214
 
215
  $_instance = self::get_instance();
216
  if ( ! $_instance->queue_count() ) {
217
+ Debug2::debug( '[Avatar] no queue' );
218
+ return;
219
  }
220
 
221
  // For cron, need to check request interval too
222
  if ( ! $force ) {
223
  if ( ! empty( $_instance->_summary[ 'curr_request' ] ) && time() - $_instance->_summary[ 'curr_request' ] < 300 ) {
224
+ Debug2::debug( '[Avatar] curr_request too close' );
225
+ return;
226
  }
227
  }
228
 
229
+ $q = "SELECT url FROM `$_instance->_tb` WHERE dateline < %d ORDER BY id DESC LIMIT %d";
230
+ $q = $wpdb->prepare( $q, array( time() - $_instance->_conf_cache_ttl, apply_filters( 'litespeed_avatar_limit', 30 ) ) );
231
 
232
+ $list = $wpdb->get_results( $q );
233
+ Debug2::debug( '[Avatar] cron job [count] ' . count( $list ) );
234
 
235
  foreach ( $list as $v ) {
236
+ Debug2::debug( '[Avatar] cron job [url] ' . $v->url );
237
 
238
  $_instance->_generate( $v->url );
239
  }
245
  * @since 3.0
246
  * @access private
247
  */
248
+ private function _generate( $url ) {
249
+ global $wpdb;
 
250
 
251
  // Record the data
252
 
253
+ $file = $this->_realpath( $url );
254
 
255
  // Update request status
256
+ $this->_summary[ 'curr_request' ] = time();
257
+ self::save_summary();
258
 
259
  // Generate
260
  if ( ! self::has_cache() ) {
261
+ $this->_mkdir();
262
  }
263
+ $response = wp_remote_get( $url, array( 'timeout' => 180, 'stream' => true, 'filename' => $file ) );
264
 
265
+ Debug2::debug( '[Avatar] _generate [url] ' . $url );
266
 
267
  // Parse response data
268
  if ( is_wp_error( $response ) ) {
269
+ $error_message = $response->get_error_message();
270
+ file_exists( $file ) && unlink( $file );
271
+ Debug2::debug( '[Avatar] failed to get: ' . $error_message );
272
+ return $url;
273
  }
274
 
275
  // Save summary data
276
+ $this->_summary[ 'last_spent' ] = time() - $this->_summary[ 'curr_request' ];
277
+ $this->_summary[ 'last_request' ] = $this->_summary[ 'curr_request' ];
278
+ $this->_summary[ 'curr_request' ] = 0;
279
+ self::save_summary();
280
 
281
  // Update DB
282
+ $md5 = md5( $url );
283
+ $q = "UPDATE `$this->_tb` SET dateline=%d WHERE md5=%s";
284
+ $existed = $wpdb->query( $wpdb->prepare( $q, array( time(), $md5 ) ) );
285
  if ( ! $existed ) {
286
+ $q = "INSERT INTO `$this->_tb` SET url=%s, md5=%s, dateline=%d";
287
+ $wpdb->query( $wpdb->prepare( $q, array( $url, $md5, time() ) ) );
288
  }
289
 
290
+ Debug2::debug( '[Avatar] saved avatar ' . $file );
291
 
292
+ return $this->_rewrite( $url );
293
  }
294
 
295
  /**
298
  * @since 3.0
299
  * @access public
300
  */
301
+ public static function handler() {
302
+ $instance = self::get_instance();
 
303
 
304
+ $type = Router::verify_type();
305
 
306
  switch ( $type ) {
307
  case self::TYPE_GENERATE :
308
+ self::cron( true );
309
+ break;
310
 
311
  default:
312
+ break;
313
  }
314
 
315
+ Admin::redirect();
316
  }
317
 
318
  }
src/cdn.cls.php CHANGED
@@ -408,8 +408,7 @@ class CDN extends Instance {
408
  * @since 1.7
409
  * @access public
410
  */
411
- public function url_css( $url )
412
- {
413
  if ( $url && $url2 = $this->rewrite( $url, Base::CDN_MAPPING_INC_CSS ) ) {
414
  $url = $url2;
415
  }
408
  * @since 1.7
409
  * @access public
410
  */
411
+ public function url_css( $url ) {
 
412
  if ( $url && $url2 = $this->rewrite( $url, Base::CDN_MAPPING_INC_CSS ) ) {
413
  $url = $url2;
414
  }
src/css.cls.php CHANGED
@@ -240,7 +240,8 @@ class CSS extends Base {
240
 
241
  // Load CSS content
242
  $real_file = Utility::is_internal_file( $attrs[ 'href' ] );
243
- if ( ! $real_file ) {
 
244
  Debug2::debug2( '[CCSS] Load Remote CSS ' . $attrs[ 'href' ] );
245
  $con = wp_remote_retrieve_body( wp_remote_get( $attrs[ 'href' ] ) );
246
  if ( ! $con ) {
240
 
241
  // Load CSS content
242
  $real_file = Utility::is_internal_file( $attrs[ 'href' ] );
243
+ $postfix = pathinfo( parse_url( $attrs[ 'href' ], PHP_URL_PATH ), PATHINFO_EXTENSION );
244
+ if ( ! $real_file || $postfix != 'css' ) {
245
  Debug2::debug2( '[CCSS] Load Remote CSS ' . $attrs[ 'href' ] );
246
  $con = wp_remote_retrieve_body( wp_remote_get( $attrs[ 'href' ] ) );
247
  if ( ! $con ) {
src/esi.cls.php CHANGED
@@ -331,7 +331,7 @@ class ESI extends Instance {
331
  return ;
332
  }
333
 
334
- add_filter('widget_display_callback', array($this, 'sub_widget_block'), 0, 3) ;
335
 
336
  // Add admin_bar esi
337
  if ( Router::is_logged_in() ) {
@@ -360,11 +360,9 @@ class ESI extends Instance {
360
  * @param bool $silence If generate wrapper comment or not
361
  * @param bool $preserved If this ESI block is used in any filter, need to temporarily convert it to a string to avoid the HTML tag being removed/filtered.
362
  * @param bool $svar If store the value in memory or not, in memory wil be faster
363
- * @param bool $inline_val If show the current value for current request( this can avoid multiple esi requests in first time cache generating process ) -- Not used yet
364
- * @return bool|string False on error, the output otherwise.
365
  */
366
- public static function sub_esi_block( $block_id, $wrapper, $params = array(), $control = 'private,no-vary', $silence = false, $preserved = false, $svar = false, $inline_val = false )
367
- {
368
  if ( empty($block_id) || ! is_array($params) || preg_match('/[^\w-]/', $block_id) ) {
369
  return false ;
370
  }
@@ -411,7 +409,20 @@ class ESI extends Instance {
411
  // Generate ESI URL
412
  $url = add_query_arg( $appended_params, trailingslashit( wp_make_link_relative( home_url() ) ) ) ;
413
 
414
- $output = "<esi:include src='$url'" ;
 
 
 
 
 
 
 
 
 
 
 
 
 
415
  if ( ! empty( $control ) ) {
416
  $output .= " cache-control='$control'" ;
417
  }
@@ -555,7 +566,6 @@ class ESI extends Instance {
555
  do_action('litespeed_esi_load-' . LSCACHE_IS_ESI, $params) ;
556
  }
557
 
558
- // BEGIN helper functions
559
  // The *_sub_* functions are helpers for the sub_* functions.
560
  // The *_load_* functions are helpers for the load_* functions.
561
 
@@ -584,8 +594,6 @@ class ESI extends Instance {
584
  return $options ;
585
  }
586
 
587
- // END helper functions.
588
-
589
  /**
590
  * Hooked to the widget_display_callback filter.
591
  * If the admin configured the widget to display via esi, this function
@@ -598,8 +606,7 @@ class ESI extends Instance {
598
  * @param array $args Parameter used to build the widget.
599
  * @return mixed Return false if display through esi, instance otherwise.
600
  */
601
- public function sub_widget_block( $instance, $widget, $args )
602
- {
603
  // #210407
604
  if ( ! is_array( $instance ) ) {
605
  return $instance ;
@@ -625,8 +632,9 @@ class ESI extends Instance {
625
  self::PARAM_ARGS => $args
626
  ) ;
627
 
628
- echo self::sub_esi_block( 'widget', 'widget ' . $name, $params, $esi_private . 'no-vary' ) ;
629
- return false ;
 
630
  }
631
 
632
  /**
331
  return ;
332
  }
333
 
334
+ add_filter('widget_display_callback', array( $this, 'sub_widget_block' ), 0, 3);
335
 
336
  // Add admin_bar esi
337
  if ( Router::is_logged_in() ) {
360
  * @param bool $silence If generate wrapper comment or not
361
  * @param bool $preserved If this ESI block is used in any filter, need to temporarily convert it to a string to avoid the HTML tag being removed/filtered.
362
  * @param bool $svar If store the value in memory or not, in memory wil be faster
363
+ * @param array $inline_val If show the current value for current request( this can avoid multiple esi requests in first time cache generating process )
 
364
  */
365
+ public static function sub_esi_block( $block_id, $wrapper, $params = array(), $control = 'private,no-vary', $silence = false, $preserved = false, $svar = false, $inline_param = array() ) {
 
366
  if ( empty($block_id) || ! is_array($params) || preg_match('/[^\w-]/', $block_id) ) {
367
  return false ;
368
  }
409
  // Generate ESI URL
410
  $url = add_query_arg( $appended_params, trailingslashit( wp_make_link_relative( home_url() ) ) ) ;
411
 
412
+ $output = '';
413
+
414
+ if ( ! empty( $inline_param[ 'val' ] ) ) {
415
+ $output .= "<esi:inline name='$url'";
416
+ if ( ! empty( $inline_param[ 'control' ] ) ) {
417
+ $output .= " cache-control='" . $inline_param[ 'control' ] . "'";
418
+ }
419
+ if ( ! empty( $inline_param[ 'tag' ] ) ) {
420
+ $output .= " cache-tag='" . $inline_param[ 'tag' ] . "'";
421
+ }
422
+ $output .= '>' . $inline_param[ 'val' ] . '</esi:inline>';
423
+ }
424
+
425
+ $output .= "<esi:include src='$url'" ;
426
  if ( ! empty( $control ) ) {
427
  $output .= " cache-control='$control'" ;
428
  }
566
  do_action('litespeed_esi_load-' . LSCACHE_IS_ESI, $params) ;
567
  }
568
 
 
569
  // The *_sub_* functions are helpers for the sub_* functions.
570
  // The *_load_* functions are helpers for the load_* functions.
571
 
594
  return $options ;
595
  }
596
 
 
 
597
  /**
598
  * Hooked to the widget_display_callback filter.
599
  * If the admin configured the widget to display via esi, this function
606
  * @param array $args Parameter used to build the widget.
607
  * @return mixed Return false if display through esi, instance otherwise.
608
  */
609
+ public function sub_widget_block( $instance, $widget, $args ) {
 
610
  // #210407
611
  if ( ! is_array( $instance ) ) {
612
  return $instance ;
632
  self::PARAM_ARGS => $args
633
  ) ;
634
 
635
+ echo self::sub_esi_block( 'widget', 'widget ' . $name, $params, $esi_private . 'no-vary' );
636
+
637
+ return false;
638
  }
639
 
640
  /**
src/htaccess.cls.php CHANGED
@@ -8,52 +8,51 @@
8
  * @subpackage LiteSpeed/inc
9
  * @author LiteSpeed Technologies <info@litespeedtech.com>
10
  */
11
- namespace LiteSpeed ;
12
-
13
- defined( 'WPINC' ) || exit ;
14
-
15
- class Htaccess extends Instance
16
- {
17
- protected static $_instance ;
18
-
19
- const EDITOR_TEXTAREA_NAME = 'lscwp_ht_editor' ;
20
-
21
- private $frontend_htaccess = null ;
22
- private $_default_frontend_htaccess = null ;
23
- private $backend_htaccess = null ;
24
- private $_default_backend_htaccess = null ;
25
- private $theme_htaccess = null ;// Not used yet
26
- private $frontend_htaccess_readable = false ;
27
- private $frontend_htaccess_writable = false ;
28
- private $backend_htaccess_readable = false ;
29
- private $backend_htaccess_writable = false ;
30
- private $theme_htaccess_readable = false ;
31
- private $theme_htaccess_writable = false ;
32
- private $__rewrite_on ;
33
-
34
- const LS_MODULE_START = '<IfModule LiteSpeed>' ;
35
- const EXPIRES_MODULE_START = '<IfModule mod_expires.c>' ;
36
- const LS_MODULE_END = '</IfModule>' ;
37
- const LS_MODULE_REWRITE_START = '<IfModule mod_rewrite.c>' ;
38
- const REWRITE_ON = 'RewriteEngine on' ;
39
- const LS_MODULE_DONOTEDIT = "## LITESPEED WP CACHE PLUGIN - Do not edit the contents of this block! ##" ;
40
- const MARKER = 'LSCACHE' ;
41
- const MARKER_NONLS = 'NON_LSCACHE' ;
42
- const MARKER_LOGIN_COOKIE = '### marker LOGIN COOKIE' ;
43
- const MARKER_MOBILE = '### marker MOBILE' ;
44
- const MARKER_NOCACHE_COOKIES = '### marker NOCACHE COOKIES' ;
45
- const MARKER_NOCACHE_USER_AGENTS = '### marker NOCACHE USER AGENTS' ;
46
- const MARKER_CACHE_RESOURCE = '### marker CACHE RESOURCE' ;
47
- const MARKER_FAVICON = '### marker FAVICON' ;
48
- const MARKER_BROWSER_CACHE = '### marker BROWSER CACHE' ;
49
- const MARKER_MINIFY = '### marker MINIFY' ;
50
- const MARKER_CORS = '### marker CORS' ;
51
- const MARKER_WEBP = '### marker WEBP' ;
52
- const MARKER_DROPQS = '### marker DROPQS' ;
53
- const MARKER_START = ' start ###' ;
54
- const MARKER_END = ' end ###' ;
55
-
56
- const RW_PATTERN_RES = '/.*/[^/]*(responsive|css|js|dynamic|loader|fonts)\.php' ;
57
 
58
  /**
59
  * Initialize the class and set its properties.
@@ -61,11 +60,10 @@ class Htaccess extends Instance
61
  * @since 1.0.7
62
  * @access protected
63
  */
64
- protected function __construct()
65
- {
66
- $this->_path_set() ;
67
- $this->_default_frontend_htaccess = $this->frontend_htaccess ;
68
- $this->_default_backend_htaccess = $this->backend_htaccess ;
69
 
70
  $frontend_htaccess = Conf::val( Base::O_MISC_HTACCESS_FRONT );
71
  if ( $frontend_htaccess && substr( $frontend_htaccess, -10 ) === '/.htaccess' ) {
@@ -77,38 +75,38 @@ class Htaccess extends Instance
77
  }
78
 
79
  // Filter for frontend&backend htaccess path
80
- $this->frontend_htaccess = apply_filters( 'litespeed_frontend_htaccess', $this->frontend_htaccess ) ;
81
- $this->backend_htaccess = apply_filters( 'litespeed_backend_htaccess', $this->backend_htaccess ) ;
82
 
83
- clearstatcache() ;
84
 
85
  // frontend .htaccess privilege
86
- $test_permissions = file_exists( $this->frontend_htaccess ) ? $this->frontend_htaccess : dirname( $this->frontend_htaccess ) ;
87
  if ( is_readable( $test_permissions ) ) {
88
- $this->frontend_htaccess_readable = true ;
89
  }
90
  if ( is_writable( $test_permissions ) ) {
91
- $this->frontend_htaccess_writable = true ;
92
  }
93
 
94
  $this->__rewrite_on = array(
95
  self::REWRITE_ON,
96
  "CacheLookup on",
97
  "RewriteRule .* - [E=Cache-Control:no-autoflush]",
98
- ) ;
99
 
100
  // backend .htaccess privilege
101
  if ( $this->frontend_htaccess === $this->backend_htaccess ) {
102
- $this->backend_htaccess_readable = $this->frontend_htaccess_readable ;
103
- $this->backend_htaccess_writable = $this->frontend_htaccess_writable ;
104
  }
105
  else {
106
- $test_permissions = file_exists( $this->backend_htaccess ) ? $this->backend_htaccess : dirname( $this->backend_htaccess ) ;
107
  if ( is_readable( $test_permissions ) ) {
108
- $this->backend_htaccess_readable = true ;
109
  }
110
  if ( is_writable( $test_permissions ) ) {
111
- $this->backend_htaccess_writable = true ;
112
  }
113
  }
114
  }
@@ -119,13 +117,12 @@ class Htaccess extends Instance
119
  * @since 1.1.0
120
  * @return string
121
  */
122
- private function _readable( $kind = 'frontend' )
123
- {
124
  if( $kind === 'frontend' ) {
125
- return $this->frontend_htaccess_readable ;
126
  }
127
  if( $kind === 'backend' ) {
128
- return $this->backend_htaccess_readable ;
129
  }
130
  }
131
 
@@ -135,13 +132,12 @@ class Htaccess extends Instance
135
  * @since 1.1.0
136
  * @return string
137
  */
138
- public function writable( $kind = 'frontend' )
139
- {
140
  if( $kind === 'frontend' ) {
141
- return $this->frontend_htaccess_writable ;
142
  }
143
  if( $kind === 'backend' ) {
144
- return $this->backend_htaccess_writable ;
145
  }
146
  }
147
 
@@ -151,12 +147,11 @@ class Htaccess extends Instance
151
  * @since 1.1.0
152
  * @return string
153
  */
154
- public static function get_frontend_htaccess( $show_default = false )
155
- {
156
  if ( $show_default ) {
157
- return self::get_instance()->_default_frontend_htaccess ;
158
  }
159
- return self::get_instance()->frontend_htaccess ;
160
  }
161
 
162
  /**
@@ -165,12 +160,11 @@ class Htaccess extends Instance
165
  * @since 1.1.0
166
  * @return string
167
  */
168
- public static function get_backend_htaccess( $show_default = false )
169
- {
170
  if ( $show_default ) {
171
- return self::get_instance()->_default_backend_htaccess ;
172
  }
173
- return self::get_instance()->backend_htaccess ;
174
  }
175
 
176
  /**
@@ -207,34 +201,33 @@ class Htaccess extends Instance
207
  * @since 1.0.11
208
  * @access private
209
  */
210
- private function _path_set()
211
- {
212
- $frontend = Router::frontend_path() ;
213
- $frontend_htaccess_search = $this->_htaccess_search( $frontend ) ;// The existing .htaccess path to be used for frontend .htaccess
214
- $this->frontend_htaccess = ( $frontend_htaccess_search ?: $frontend ) . '/.htaccess' ;
215
 
216
- $backend = realpath( ABSPATH ) ; // /home/user/public_html/backend/
217
  if ( $frontend == $backend ) {
218
- $this->backend_htaccess = $this->frontend_htaccess ;
219
- return ;
220
  }
221
 
222
  // Backend is a different path
223
- $backend_htaccess_search = $this->_htaccess_search( $backend ) ;
224
  // Found affected .htaccess
225
  if ( $backend_htaccess_search ) {
226
- $this->backend_htaccess = $backend_htaccess_search . '/.htaccess' ;
227
- return ;
228
  }
229
 
230
  // Frontend path is the parent of backend path
231
  if ( stripos( $backend, $frontend . '/' ) === 0 ) {
232
  // backend use frontend htaccess
233
- $this->backend_htaccess = $this->frontend_htaccess ;
234
- return ;
235
  }
236
 
237
- $this->backend_htaccess = $backend . '/.htaccess' ;
238
  }
239
 
240
  /**
@@ -244,19 +237,18 @@ class Htaccess extends Instance
244
  * @param string $kind Frontend or backend
245
  * @return string Path
246
  */
247
- public function htaccess_path( $kind = 'frontend' )
248
- {
249
  switch ( $kind ) {
250
  case 'backend' :
251
- $path = $this->backend_htaccess ;
252
- break ;
253
 
254
  case 'frontend' :
255
  default :
256
- $path = $this->frontend_htaccess ;
257
- break ;
258
  }
259
- return $path ;
260
  }
261
 
262
  /**
@@ -268,26 +260,25 @@ class Htaccess extends Instance
268
  * @since 2.9 Used exception for failed reading
269
  * @access public
270
  */
271
- public function htaccess_read( $kind = 'frontend' )
272
- {
273
- $path = $this->htaccess_path( $kind ) ;
274
 
275
  if( ! $path || ! file_exists( $path ) ) {
276
- return "\n" ;
277
  }
278
 
279
  if ( ! $this->_readable( $kind ) ) {
280
- Error::t( 'HTA_R' ) ;
281
  }
282
 
283
- $content = File::read( $path ) ;
284
  if ( $content === false ) {
285
- Error::t( 'HTA_GET' ) ;
286
  }
287
 
288
  // Remove ^M characters.
289
- $content = str_ireplace( "\x0D", "", $content ) ;
290
- return $content ;
291
  }
292
 
293
  /**
@@ -298,20 +289,19 @@ class Htaccess extends Instance
298
  * @since 1.0.4
299
  * @access public
300
  */
301
- public function htaccess_save( $content, $kind = 'frontend' )
302
- {
303
- $path = $this->htaccess_path( $kind ) ;
304
 
305
  if ( ! $this->writable( $kind ) ) {
306
- Error::t( 'HTA_W' ) ;
307
  }
308
 
309
- $this->_htaccess_backup( $kind ) ;
310
 
311
  // File put contents will truncate by default. Will create file if doesn't exist.
312
- $res = File::save( $path, $content, false, false, false ) ;
313
  if ( $res !== true ) {
314
- throw new \Exception( $res ) ;
315
  }
316
  }
317
 
@@ -323,23 +313,22 @@ class Htaccess extends Instance
323
  * @since 1.0.10
324
  * @access private
325
  */
326
- private function _htaccess_backup( $kind = 'frontend' )
327
- {
328
- $path = $this->htaccess_path( $kind ) ;
329
 
330
  if ( ! file_exists( $path ) ) {
331
- return ;
332
  }
333
 
334
  if ( file_exists( $path . '.bk' ) ) {
335
- return ;
336
  }
337
 
338
- $res = copy( $path, $path . '.bk' ) ;
339
 
340
  // Failed to backup, abort
341
  if ( ! $res ) {
342
- Error::t( 'HTA_BK' ) ;
343
  }
344
  }
345
 
@@ -350,22 +339,21 @@ class Htaccess extends Instance
350
  *
351
  * @since 1.1.0
352
  */
353
- public function current_mobile_agents()
354
- {
355
- $rules = $this->_get_rule_by( self::MARKER_MOBILE ) ;
356
  if( ! isset( $rules[ 0 ] ) ) {
357
- Error::t( 'HTA_DNF', self::MARKER_MOBILE ) ;
358
  }
359
 
360
- $rule = trim( $rules[ 0 ] ) ;
361
- // 'RewriteCond %{HTTP_USER_AGENT} ' . Utility::arr2regex( $cfg[ $id ], true ) . ' [NC]' ;
362
- $match = substr( $rule, strlen( 'RewriteCond %{HTTP_USER_AGENT} ' ), -strlen( ' [NC]' ) ) ;
363
 
364
  if ( ! $match ) {
365
- Error::t( 'HTA_DNF', __( 'Mobile Agent Rules', 'litespeed-cache' ) ) ;
366
  }
367
 
368
- return $match ;
369
  }
370
 
371
  /**
@@ -376,28 +364,27 @@ class Htaccess extends Instance
376
  * @since 1.1.0
377
  * @access public
378
  */
379
- public function current_login_cookie( $kind = 'frontend' )
380
- {
381
- $rule = $this->_get_rule_by( self::MARKER_LOGIN_COOKIE, $kind ) ;
382
 
383
  if( ! $rule ) {
384
- Error::t( 'HTA_DNF', self::MARKER_LOGIN_COOKIE ) ;
385
  }
386
 
387
  if( strpos( $rule, 'RewriteRule .? - [E=' ) !== 0 ) {
388
- Error::t( 'HTA_LOGIN_COOKIE_INVALID' ) ;
389
  }
390
 
391
- $rule_cookie = substr( $rule, strlen( 'RewriteRule .? - [E=' ), -1 ) ;
392
 
393
  if ( LITESPEED_SERVER_TYPE === 'LITESPEED_SERVER_OLS' ) {
394
- $rule_cookie = trim( $rule_cookie, '"' ) ;
395
  }
396
 
397
  // Drop `Cache-Vary:`
398
- $rule_cookie = substr( $rule_cookie, strlen( 'Cache-Vary:' ) ) ;
399
 
400
- return $rule_cookie ;
401
  }
402
 
403
  /**
@@ -406,35 +393,34 @@ class Htaccess extends Instance
406
  * @since 2.0
407
  * @access private
408
  */
409
- private function _get_rule_by( $cond, $kind = 'frontend' )
410
- {
411
- clearstatcache() ;
412
- $path = $this->htaccess_path( $kind ) ;
413
  if ( ! $this->_readable( $kind ) ) {
414
- return false ;
415
  }
416
 
417
- $rules = File::extract_from_markers( $path, self::MARKER ) ;
418
  if( ! in_array( $cond . self::MARKER_START, $rules ) || ! in_array( $cond . self::MARKER_END, $rules ) ) {
419
- return false ;
420
  }
421
 
422
- $key_start = array_search( $cond . self::MARKER_START, $rules ) ;
423
- $key_end = array_search( $cond . self::MARKER_END, $rules ) ;
424
  if( $key_start === false || $key_end === false ) {
425
- return false ;
426
  }
427
 
428
- $results = array_slice( $rules, $key_start + 1, $key_end - $key_start - 1 ) ;
429
  if( ! $results ) {
430
- return false ;
431
  }
432
 
433
  if( count( $results ) == 1 ) {
434
- return trim( $results[ 0 ] ) ;
435
  }
436
 
437
- return array_filter( $results ) ;
438
  }
439
 
440
  /**
@@ -444,14 +430,13 @@ class Htaccess extends Instance
444
  * @access private
445
  * @return array Rules set
446
  */
447
- private function _browser_cache_rules( $cfg )
448
- {
449
  /**
450
  * Add ttl setting
451
  * @since 1.6.3
452
  */
453
- $id = Base::O_CACHE_TTL_BROWSER ;
454
- $ttl = $cfg[ $id ] ;
455
  $rules = array(
456
  self::EXPIRES_MODULE_START,
457
  // '<FilesMatch "\.(pdf|ico|svg|xml|jpg|jpeg|png|gif|webp|ogg|mp4|webm|js|css|woff|woff2|ttf|eot)(\.gz)?$">',
@@ -488,8 +473,8 @@ class Htaccess extends Instance
488
  '',
489
  // '</FilesMatch>',
490
  self::LS_MODULE_END,
491
- ) ;
492
- return $rules ;
493
  }
494
 
495
  /**
@@ -499,15 +484,14 @@ class Htaccess extends Instance
499
  * @access private
500
  * @return array Rules set
501
  */
502
- private function _cors_rules()
503
- {
504
  return array(
505
  '<FilesMatch "\.(ttf|ttc|otf|eot|woff|woff2|font\.css)$">',
506
  '<IfModule mod_headers.c>',
507
  'Header set Access-Control-Allow-Origin "*"',
508
  '</IfModule>',
509
  '</FilesMatch>',
510
- ) ;
511
  }
512
 
513
  /**
@@ -518,147 +502,146 @@ class Htaccess extends Instance
518
  * @param array $cfg The settings to be used for rewrite rule
519
  * @return array Rules array
520
  */
521
- private function _generate_rules( $cfg )
522
- {
523
- $new_rules = array() ;
524
- $new_rules_nonls = array() ;
525
- $new_rules_backend = array() ;
526
- $new_rules_backend_nonls = array() ;
527
 
528
  // mobile agents
529
- $id = Base::O_CACHE_MOBILE_RULES ;
530
  if ( ! empty( $cfg[ Base::O_CACHE_MOBILE ] ) && ! empty( $cfg[ $id ] ) ) {
531
- $new_rules[] = self::MARKER_MOBILE . self::MARKER_START ;
532
- $new_rules[] = 'RewriteCond %{HTTP_USER_AGENT} ' . Utility::arr2regex( $cfg[ $id ], true ) . ' [NC]' ;
533
- $new_rules[] = 'RewriteRule .* - [E=Cache-Control:vary=ismobile]' ;
534
- $new_rules[] = self::MARKER_MOBILE . self::MARKER_END ;
535
- $new_rules[] = '' ;
536
  }
537
 
538
  // nocache cookie
539
- $id = Base::O_CACHE_EXC_COOKIES ;
540
  if ( ! empty( $cfg[ $id ] ) ) {
541
- $new_rules[] = self::MARKER_NOCACHE_COOKIES . self::MARKER_START ;
542
- $new_rules[] = 'RewriteCond %{HTTP_COOKIE} ' . Utility::arr2regex( $cfg[ $id ], true ) ;
543
- $new_rules[] = 'RewriteRule .* - [E=Cache-Control:no-cache]' ;
544
- $new_rules[] = self::MARKER_NOCACHE_COOKIES . self::MARKER_END ;
545
- $new_rules[] = '' ;
546
  }
547
 
548
  // nocache user agents
549
- $id = Base::O_CACHE_EXC_USERAGENTS ;
550
  if ( ! empty( $cfg[ $id ] ) ) {
551
- $new_rules[] = self::MARKER_NOCACHE_USER_AGENTS . self::MARKER_START ;
552
- $new_rules[] = 'RewriteCond %{HTTP_USER_AGENT} ' . Utility::arr2regex( $cfg[ $id ], true ) ;
553
- $new_rules[] = 'RewriteRule .* - [E=Cache-Control:no-cache]' ;
554
- $new_rules[] = self::MARKER_NOCACHE_USER_AGENTS . self::MARKER_END ;
555
- $new_rules[] = '' ;
556
  }
557
 
558
  // caching php resource
559
- $id = Base::O_CACHE_RES ;
560
  if ( ! empty( $cfg[ $id ] ) ) {
561
- $new_rules[] = $new_rules_backend[] = self::MARKER_CACHE_RESOURCE . self::MARKER_START ;
562
- $new_rules[] = $new_rules_backend[] = 'RewriteRule ' . LSCWP_CONTENT_FOLDER . self::RW_PATTERN_RES . ' - [E=cache-control:max-age=3600]' ;
563
- $new_rules[] = $new_rules_backend[] = self::MARKER_CACHE_RESOURCE . self::MARKER_END ;
564
- $new_rules[] = $new_rules_backend[] = '' ;
565
  }
566
 
567
  // check login cookie
568
- $id = Base::O_CACHE_LOGIN_COOKIE ;
569
 
570
  // Need to keep this due to different behavior of OLS when handling response vary header @Sep/22/2018
571
  if ( LITESPEED_SERVER_TYPE === 'LITESPEED_SERVER_OLS' ) {
572
  if ( ! empty( $cfg[ $id ] ) ) {
573
- $cfg[ $id ] .= ',wp-postpass_' . COOKIEHASH ;
574
  }
575
  else {
576
- $cfg[ $id ] = 'wp-postpass_' . COOKIEHASH ;
577
  }
578
  }
579
 
580
- $tp_cookies = apply_filters( 'litespeed_api_vary', array() ) ;
581
  if ( ! empty( $tp_cookies ) && is_array( $tp_cookies ) ) {
582
  if ( ! empty( $cfg[ $id ] ) ) {
583
- $cfg[ $id ] .= ',' . implode( ',', $tp_cookies ) ;
584
  }
585
  else {
586
- $cfg[ $id ] = implode( ',', $tp_cookies ) ;
587
  }
588
  }
589
  // frontend and backend
590
  if ( ! empty( $cfg[ $id ] ) ) {
591
- $env = 'Cache-Vary:' . $cfg[ $id ] ;
592
  if ( LITESPEED_SERVER_TYPE === 'LITESPEED_SERVER_OLS' ) {
593
- $env = '"' . $env . '"' ;
594
  }
595
- $new_rules[] = $new_rules_backend[] = self::MARKER_LOGIN_COOKIE . self::MARKER_START ;
596
- $new_rules[] = $new_rules_backend[] = 'RewriteRule .? - [E=' . $env . ']' ;
597
- $new_rules[] = $new_rules_backend[] = self::MARKER_LOGIN_COOKIE . self::MARKER_END ;
598
- $new_rules[] = '' ;
599
  }
600
 
601
  // favicon
602
  // frontend and backend
603
- $id = Base::O_CACHE_FAVICON ;
604
  if ( ! empty( $cfg[ $id ] ) ) {
605
- $new_rules[] = $new_rules_backend[] = self::MARKER_FAVICON . self::MARKER_START ;
606
- $new_rules[] = $new_rules_backend[] = 'RewriteRule favicon\.ico$ - [E=cache-control:max-age=86400]' ;
607
- $new_rules[] = $new_rules_backend[] = self::MARKER_FAVICON . self::MARKER_END ;
608
- $new_rules[] = '' ;
609
  }
610
 
611
  // CORS font rules
612
- $id = Base::O_CDN ;
613
  if ( ! empty( $cfg[ $id ] ) ) {
614
- $new_rules[] = self::MARKER_CORS . self::MARKER_START ;
615
- $new_rules = array_merge( $new_rules, $this->_cors_rules() ) ; //todo: network
616
- $new_rules[] = self::MARKER_CORS . self::MARKER_END ;
617
- $new_rules[] = '' ;
618
  }
619
 
620
  // webp support
621
- $id = Base::O_IMG_OPTM_WEBP_REPLACE ;
622
  if ( ! empty( $cfg[ $id ] ) ) {
623
- $new_rules[] = self::MARKER_WEBP . self::MARKER_START ;
624
- $new_rules[] = 'RewriteCond %{HTTP_ACCEPT} "image/webp" [or]' ;
625
- $new_rules[] = 'RewriteCond %{HTTP_USER_AGENT} "Page Speed"' ;
626
- $new_rules[] = 'RewriteRule .* - [E=Cache-Control:vary=%{ENV:LSCACHE_VARY_VALUE}+webp]' ;
627
- $new_rules[] = self::MARKER_WEBP . self::MARKER_END ;
628
- $new_rules[] = '' ;
629
  }
630
 
631
  // drop qs support
632
- $id = Base::O_CACHE_DROP_QS ;
633
  if ( ! empty( $cfg[ $id ] ) ) {
634
- $new_rules[] = self::MARKER_DROPQS . self::MARKER_START ;
635
  foreach ( $cfg[ $id ] as $v ) {
636
- $new_rules[] = 'CacheKeyModify -qs:' . $v ;
637
  }
638
- $new_rules[] = self::MARKER_DROPQS . self::MARKER_END ;
639
- $new_rules[] = '' ;
640
  }
641
 
642
  // Browser cache
643
- $id = Base::O_CACHE_BROWSER ;
644
  if ( ! empty( $cfg[ $id ] ) ) {
645
- $new_rules_nonls[] = $new_rules_backend_nonls[] = self::MARKER_BROWSER_CACHE . self::MARKER_START ;
646
- $new_rules_nonls = array_merge( $new_rules_nonls, $this->_browser_cache_rules( $cfg ) ) ;
647
- $new_rules_backend_nonls = array_merge( $new_rules_backend_nonls, $this->_browser_cache_rules( $cfg ) ) ;
648
- $new_rules_nonls[] = $new_rules_backend_nonls[] = self::MARKER_BROWSER_CACHE . self::MARKER_END ;
649
- $new_rules_nonls[] = '' ;
650
  }
651
 
652
  // Add module wrapper for LiteSpeed rules
653
  if ( $new_rules ) {
654
- $new_rules = $this->_wrap_ls_module( $new_rules ) ;
655
  }
656
 
657
  if ( $new_rules_backend ) {
658
- $new_rules_backend = $this->_wrap_ls_module( $new_rules_backend ) ;
659
  }
660
 
661
- return array( $new_rules, $new_rules_backend, $new_rules_nonls, $new_rules_backend_nonls ) ;
662
  }
663
 
664
  /**
@@ -667,15 +650,14 @@ class Htaccess extends Instance
667
  * @since 2.1.1
668
  * @access private
669
  */
670
- private function _wrap_ls_module( $rules = array() )
671
- {
672
  return array_merge(
673
  array( self::LS_MODULE_START ),
674
  $this->__rewrite_on,
675
  array( '' ),
676
  $rules,
677
  array( self::LS_MODULE_END )
678
- ) ;
679
  }
680
 
681
  /**
@@ -684,10 +666,9 @@ class Htaccess extends Instance
684
  * @since 2.1.1
685
  * @access public
686
  */
687
- public function insert_ls_wrapper()
688
- {
689
- $rules = $this->_wrap_ls_module() ;
690
- $this->_insert_wrapper( $rules ) ;
691
  }
692
 
693
  /**
@@ -697,20 +678,19 @@ class Htaccess extends Instance
697
  * @param array $rules
698
  * @return array wrapped rules with module info
699
  */
700
- private function _wrap_do_no_edit( $rules )
701
- {
702
  // When to clear rules, don't need DONOTEDIT msg
703
  if ( $rules === false || ! is_array( $rules ) ) {
704
- return $rules ;
705
  }
706
 
707
  $rules = array_merge(
708
  array( self::LS_MODULE_DONOTEDIT ),
709
  $rules,
710
  array( self::LS_MODULE_DONOTEDIT )
711
- ) ;
712
 
713
- return $rules ;
714
  }
715
 
716
  /**
@@ -721,20 +701,19 @@ class Htaccess extends Instance
721
  * @since 1.1.0
722
  * @access private
723
  */
724
- private function _insert_wrapper( $rules = array(), $kind = false, $marker = false )
725
- {
726
  if ( $kind != 'backend' ) {
727
- $kind = 'frontend' ;
728
  }
729
 
730
  // Default marker is LiteSpeed marker `LSCACHE`
731
  if ( $marker === false ) {
732
- $marker = self::MARKER ;
733
  }
734
 
735
- $this->_htaccess_backup( $kind ) ;
736
 
737
- File::insert_with_markers( $this->htaccess_path( $kind ), $this->_wrap_do_no_edit( $rules ), $marker, true ) ;
738
  }
739
 
740
  /**
@@ -745,70 +724,69 @@ class Htaccess extends Instance
745
  * @since 1.3
746
  * @access public
747
  */
748
- public function update( $cfg )
749
- {
750
- list( $frontend_rules, $backend_rules, $frontend_rules_nonls, $backend_rules_nonls ) = $this->_generate_rules( $cfg ) ;
751
 
752
  // Check frontend content
753
- list( $rules, $rules_nonls ) = $this->_extract_rules() ;
754
 
755
  // Check Non-LiteSpeed rules
756
  if ( $this->_wrap_do_no_edit( $frontend_rules_nonls ) != $rules_nonls ) {
757
- Debug2::debug( '[Rules] Update non-ls frontend rules' ) ;
758
  // Need to update frontend htaccess
759
  try {
760
- $this->_insert_wrapper( $frontend_rules_nonls, false, self::MARKER_NONLS ) ;
761
  } catch ( \Exception $e ) {
762
- $manual_guide_codes = $this->_rewrite_codes_msg( $this->frontend_htaccess, $frontend_rules_nonls, self::MARKER_NONLS ) ;
763
- Debug2::debug( '[Rules] Update Failed' ) ;
764
- throw new \Exception( $manual_guide_codes ) ;
765
  }
766
  }
767
 
768
  // Check LiteSpeed rules
769
  if ( $this->_wrap_do_no_edit( $frontend_rules ) != $rules ) {
770
- Debug2::debug( '[Rules] Update frontend rules' ) ;
771
  // Need to update frontend htaccess
772
  try {
773
- $this->_insert_wrapper( $frontend_rules ) ;
774
  } catch ( \Exception $e ) {
775
- Debug2::debug( '[Rules] Update Failed' ) ;
776
- $manual_guide_codes = $this->_rewrite_codes_msg( $this->frontend_htaccess, $frontend_rules ) ;
777
- throw new \Exception( $manual_guide_codes ) ;
778
  }
779
  }
780
 
781
  if ( $this->frontend_htaccess !== $this->backend_htaccess ) {
782
- list( $rules, $rules_nonls ) = $this->_extract_rules( 'backend' ) ;
783
 
784
  // Check Non-LiteSpeed rules for backend
785
  if ( $this->_wrap_do_no_edit( $backend_rules_nonls ) != $rules_nonls ) {
786
- Debug2::debug( '[Rules] Update non-ls backend rules' ) ;
787
  // Need to update frontend htaccess
788
  try {
789
- $this->_insert_wrapper( $backend_rules_nonls, 'backend', self::MARKER_NONLS ) ;
790
  } catch ( \Exception $e ) {
791
- Debug2::debug( '[Rules] Update Failed' ) ;
792
- $manual_guide_codes = $this->_rewrite_codes_msg( $this->backend_htaccess, $backend_rules_nonls, self::MARKER_NONLS ) ;
793
- throw new \Exception( $manual_guide_codes ) ;
794
  }
795
  }
796
 
797
  // Check backend content
798
  if ( $this->_wrap_do_no_edit( $backend_rules ) != $rules ) {
799
- Debug2::debug( '[Rules] Update backend rules' ) ;
800
  // Need to update backend htaccess
801
  try {
802
- $this->_insert_wrapper( $backend_rules, 'backend' ) ;
803
  } catch ( \Exception $e ) {
804
- Debug2::debug( '[Rules] Update Failed' ) ;
805
- $manual_guide_codes = $this->_rewrite_codes_msg( $this->backend_htaccess, $backend_rules ) ;
806
- throw new \Exception( $manual_guide_codes ) ;
807
  }
808
  }
809
  }
810
 
811
- return true ;
812
  }
813
 
814
  /**
@@ -820,18 +798,17 @@ class Htaccess extends Instance
820
  * @access private
821
  * @param string $kind Frontend or backend .htaccess file
822
  */
823
- private function _extract_rules( $kind = 'frontend' )
824
- {
825
- clearstatcache() ;
826
- $path = $this->htaccess_path( $kind ) ;
827
  if ( ! $this->_readable( $kind ) ) {
828
- Error::t( 'E_HTA_R' ) ;
829
  }
830
 
831
- $rules = File::extract_from_markers( $path, self::MARKER ) ;
832
- $rules_nonls = File::extract_from_markers( $path, self::MARKER_NONLS ) ;
833
 
834
- return array( $rules, $rules_nonls ) ;
835
  }
836
 
837
  /**
@@ -842,12 +819,11 @@ class Htaccess extends Instance
842
  * @param array $rules
843
  * @return string final msg to output
844
  */
845
- private function _rewrite_codes_msg( $file, $rules, $marker = false )
846
- {
847
  return sprintf( __( '<p>Please add/replace the following codes into the beginning of %1$s:</p> %2$s' , 'litespeed-cache' ),
848
  $file,
849
  '<textarea style="width:100%;" rows="10" readonly>' . htmlspecialchars( $this->_wrap_rules_with_marker( $rules, $marker ) ) . '</textarea>'
850
- ) ;
851
  }
852
 
853
  /**
@@ -855,22 +831,21 @@ class Htaccess extends Instance
855
  *
856
  * @since 1.1.5
857
  */
858
- private function _wrap_rules_with_marker( $rules, $marker = false )
859
- {
860
  // Default marker is LiteSpeed marker `LSCACHE`
861
  if ( $marker === false ) {
862
- $marker = self::MARKER ;
863
  }
864
 
865
- $start_marker = "# BEGIN {$marker}" ;
866
- $end_marker = "# END {$marker}" ;
867
  $new_file_data = implode( "\n", array_merge(
868
  array( $start_marker ),
869
  $this->_wrap_do_no_edit($rules),
870
  array( $end_marker )
871
- ) ) ;
872
 
873
- return $new_file_data ;
874
  }
875
 
876
  /**
@@ -879,15 +854,14 @@ class Htaccess extends Instance
879
  * @since 1.0.4
880
  * @access public
881
  */
882
- public function clear_rules()
883
- {
884
- $this->_insert_wrapper( false ) ;// Use false to avoid do-not-edit msg
885
  // Clear non ls rules
886
- $this->_insert_wrapper( false, false, self::MARKER_NONLS ) ;
887
 
888
  if ( $this->frontend_htaccess !== $this->backend_htaccess ) {
889
- $this->_insert_wrapper( false, 'backend' ) ;
890
- $this->_insert_wrapper( false, 'backend', self::MARKER_NONLS ) ;
891
  }
892
  }
893
 
@@ -899,22 +873,21 @@ class Htaccess extends Instance
899
  * @since 2.9 Used exception when saving
900
  * @access public
901
  */
902
- public function htaccess_editor_save()
903
- {
904
  if ( ! isset( $_POST[ self::EDITOR_TEXTAREA_NAME ] ) ) {
905
- return ;
906
  }
907
 
908
- $content = Admin::cleanup_text($_POST[self::EDITOR_TEXTAREA_NAME]) ;
909
 
910
  try {
911
- $this->htaccess_save($content) ;
912
  } catch( \Exception $e ) {
913
- Admin_Display::error( $e->getMessage() ) ;
914
- return ;
915
  }
916
 
917
- Admin_Display::succeed( __( 'File Saved.', 'litespeed-cache' ) ) ;
918
 
919
  }
920
  }
8
  * @subpackage LiteSpeed/inc
9
  * @author LiteSpeed Technologies <info@litespeedtech.com>
10
  */
11
+ namespace LiteSpeed;
12
+
13
+ defined( 'WPINC' ) || exit;
14
+
15
+ class Htaccess extends Instance {
16
+ protected static $_instance;
17
+
18
+ const EDITOR_TEXTAREA_NAME = 'lscwp_ht_editor';
19
+
20
+ private $frontend_htaccess = null;
21
+ private $_default_frontend_htaccess = null;
22
+ private $backend_htaccess = null;
23
+ private $_default_backend_htaccess = null;
24
+ private $theme_htaccess = null;// Not used yet
25
+ private $frontend_htaccess_readable = false;
26
+ private $frontend_htaccess_writable = false;
27
+ private $backend_htaccess_readable = false;
28
+ private $backend_htaccess_writable = false;
29
+ private $theme_htaccess_readable = false;
30
+ private $theme_htaccess_writable = false;
31
+ private $__rewrite_on;
32
+
33
+ const LS_MODULE_START = '<IfModule LiteSpeed>';
34
+ const EXPIRES_MODULE_START = '<IfModule mod_expires.c>';
35
+ const LS_MODULE_END = '</IfModule>';
36
+ const LS_MODULE_REWRITE_START = '<IfModule mod_rewrite.c>';
37
+ const REWRITE_ON = 'RewriteEngine on';
38
+ const LS_MODULE_DONOTEDIT = "## LITESPEED WP CACHE PLUGIN - Do not edit the contents of this block! ##";
39
+ const MARKER = 'LSCACHE';
40
+ const MARKER_NONLS = 'NON_LSCACHE';
41
+ const MARKER_LOGIN_COOKIE = '### marker LOGIN COOKIE';
42
+ const MARKER_MOBILE = '### marker MOBILE';
43
+ const MARKER_NOCACHE_COOKIES = '### marker NOCACHE COOKIES';
44
+ const MARKER_NOCACHE_USER_AGENTS = '### marker NOCACHE USER AGENTS';
45
+ const MARKER_CACHE_RESOURCE = '### marker CACHE RESOURCE';
46
+ const MARKER_FAVICON = '### marker FAVICON';
47
+ const MARKER_BROWSER_CACHE = '### marker BROWSER CACHE';
48
+ const MARKER_MINIFY = '### marker MINIFY';
49
+ const MARKER_CORS = '### marker CORS';
50
+ const MARKER_WEBP = '### marker WEBP';
51
+ const MARKER_DROPQS = '### marker DROPQS';
52
+ const MARKER_START = ' start ###';
53
+ const MARKER_END = ' end ###';
54
+
55
+ const RW_PATTERN_RES = '/.*/[^/]*(responsive|css|js|dynamic|loader|fonts)\.php';
 
56
 
57
  /**
58
  * Initialize the class and set its properties.
60
  * @since 1.0.7
61
  * @access protected
62
  */
63
+ protected function __construct() {
64
+ $this->_path_set();
65
+ $this->_default_frontend_htaccess = $this->frontend_htaccess;
66
+ $this->_default_backend_htaccess = $this->backend_htaccess;
 
67
 
68
  $frontend_htaccess = Conf::val( Base::O_MISC_HTACCESS_FRONT );
69
  if ( $frontend_htaccess && substr( $frontend_htaccess, -10 ) === '/.htaccess' ) {
75
  }
76
 
77
  // Filter for frontend&backend htaccess path
78
+ $this->frontend_htaccess = apply_filters( 'litespeed_frontend_htaccess', $this->frontend_htaccess );
79
+ $this->backend_htaccess = apply_filters( 'litespeed_backend_htaccess', $this->backend_htaccess );
80
 
81
+ clearstatcache();
82
 
83
  // frontend .htaccess privilege
84
+ $test_permissions = file_exists( $this->frontend_htaccess ) ? $this->frontend_htaccess : dirname( $this->frontend_htaccess );
85
  if ( is_readable( $test_permissions ) ) {
86
+ $this->frontend_htaccess_readable = true;
87
  }
88
  if ( is_writable( $test_permissions ) ) {
89
+ $this->frontend_htaccess_writable = true;
90
  }
91
 
92
  $this->__rewrite_on = array(
93
  self::REWRITE_ON,
94
  "CacheLookup on",
95
  "RewriteRule .* - [E=Cache-Control:no-autoflush]",
96
+ );
97
 
98
  // backend .htaccess privilege
99
  if ( $this->frontend_htaccess === $this->backend_htaccess ) {
100
+ $this->backend_htaccess_readable = $this->frontend_htaccess_readable;
101
+ $this->backend_htaccess_writable = $this->frontend_htaccess_writable;
102
  }
103
  else {
104
+ $test_permissions = file_exists( $this->backend_htaccess ) ? $this->backend_htaccess : dirname( $this->backend_htaccess );
105
  if ( is_readable( $test_permissions ) ) {
106
+ $this->backend_htaccess_readable = true;
107
  }
108
  if ( is_writable( $test_permissions ) ) {
109
+ $this->backend_htaccess_writable = true;
110
  }
111
  }
112
  }
117
  * @since 1.1.0
118
  * @return string
119
  */
120
+ private function _readable( $kind = 'frontend' ) {
 
121
  if( $kind === 'frontend' ) {
122
+ return $this->frontend_htaccess_readable;
123
  }
124
  if( $kind === 'backend' ) {
125
+ return $this->backend_htaccess_readable;
126
  }
127
  }
128
 
132
  * @since 1.1.0
133
  * @return string
134
  */
135
+ public function writable( $kind = 'frontend' ) {
 
136
  if( $kind === 'frontend' ) {
137
+ return $this->frontend_htaccess_writable;
138
  }
139
  if( $kind === 'backend' ) {
140
+ return $this->backend_htaccess_writable;
141
  }
142
  }
143
 
147
  * @since 1.1.0
148
  * @return string
149
  */
150
+ public static function get_frontend_htaccess( $show_default = false ) {
 
151
  if ( $show_default ) {
152
+ return self::get_instance()->_default_frontend_htaccess;
153
  }
154
+ return self::get_instance()->frontend_htaccess;
155
  }
156
 
157
  /**
160
  * @since 1.1.0
161
  * @return string
162
  */
163
+ public static function get_backend_htaccess( $show_default = false ) {
 
164
  if ( $show_default ) {
165
+ return self::get_instance()->_default_backend_htaccess;
166
  }
167
+ return self::get_instance()->backend_htaccess;
168
  }
169
 
170
  /**
201
  * @since 1.0.11
202
  * @access private
203
  */
204
+ private function _path_set() {
205
+ $frontend = Router::frontend_path();
206
+ $frontend_htaccess_search = $this->_htaccess_search( $frontend );// The existing .htaccess path to be used for frontend .htaccess
207
+ $this->frontend_htaccess = ( $frontend_htaccess_search ?: $frontend ) . '/.htaccess';
 
208
 
209
+ $backend = realpath( ABSPATH ); // /home/user/public_html/backend/
210
  if ( $frontend == $backend ) {
211
+ $this->backend_htaccess = $this->frontend_htaccess;
212
+ return;
213
  }
214
 
215
  // Backend is a different path
216
+ $backend_htaccess_search = $this->_htaccess_search( $backend );
217
  // Found affected .htaccess
218
  if ( $backend_htaccess_search ) {
219
+ $this->backend_htaccess = $backend_htaccess_search . '/.htaccess';
220
+ return;
221
  }
222
 
223
  // Frontend path is the parent of backend path
224
  if ( stripos( $backend, $frontend . '/' ) === 0 ) {
225
  // backend use frontend htaccess
226
+ $this->backend_htaccess = $this->frontend_htaccess;
227
+ return;
228
  }
229
 
230
+ $this->backend_htaccess = $backend . '/.htaccess';
231
  }
232
 
233
  /**
237
  * @param string $kind Frontend or backend
238
  * @return string Path
239
  */
240
+ public function htaccess_path( $kind = 'frontend' ) {
 
241
  switch ( $kind ) {
242
  case 'backend' :
243
+ $path = $this->backend_htaccess;
244
+ break;
245
 
246
  case 'frontend' :
247
  default :
248
+ $path = $this->frontend_htaccess;
249
+ break;
250
  }
251
+ return $path;
252
  }
253
 
254
  /**
260
  * @since 2.9 Used exception for failed reading
261
  * @access public
262
  */
263
+ public function htaccess_read( $kind = 'frontend' ) {
264
+ $path = $this->htaccess_path( $kind );
 
265
 
266
  if( ! $path || ! file_exists( $path ) ) {
267
+ return "\n";
268
  }
269
 
270
  if ( ! $this->_readable( $kind ) ) {
271
+ Error::t( 'HTA_R' );
272
  }
273
 
274
+ $content = File::read( $path );
275
  if ( $content === false ) {
276
+ Error::t( 'HTA_GET' );
277
  }
278
 
279
  // Remove ^M characters.
280
+ $content = str_ireplace( "\x0D", "", $content );
281
+ return $content;
282
  }
283
 
284
  /**
289
  * @since 1.0.4
290
  * @access public
291
  */
292
+ public function htaccess_save( $content, $kind = 'frontend' ) {
293
+ $path = $this->htaccess_path( $kind );
 
294
 
295
  if ( ! $this->writable( $kind ) ) {
296
+ Error::t( 'HTA_W' );
297
  }
298
 
299
+ $this->_htaccess_backup( $kind );
300
 
301
  // File put contents will truncate by default. Will create file if doesn't exist.
302
+ $res = File::save( $path, $content, false, false, false );
303
  if ( $res !== true ) {
304
+ throw new \Exception( $res );
305
  }
306
  }
307
 
313
  * @since 1.0.10
314
  * @access private
315
  */
316
+ private function _htaccess_backup( $kind = 'frontend' ) {
317
+ $path = $this->htaccess_path( $kind );
 
318
 
319
  if ( ! file_exists( $path ) ) {
320
+ return;
321
  }
322
 
323
  if ( file_exists( $path . '.bk' ) ) {
324
+ return;
325
  }
326
 
327
+ $res = copy( $path, $path . '.bk' );
328
 
329
  // Failed to backup, abort
330
  if ( ! $res ) {
331
+ Error::t( 'HTA_BK' );
332
  }
333
  }
334
 
339
  *
340
  * @since 1.1.0
341
  */
342
+ public function current_mobile_agents() {
343
+ $rules = $this->_get_rule_by( self::MARKER_MOBILE );
 
344
  if( ! isset( $rules[ 0 ] ) ) {
345
+ Error::t( 'HTA_DNF', self::MARKER_MOBILE );
346
  }
347
 
348
+ $rule = trim( $rules[ 0 ] );
349
+ // 'RewriteCond %{HTTP_USER_AGENT} ' . Utility::arr2regex( $cfg[ $id ], true ) . ' [NC]';
350
+ $match = substr( $rule, strlen( 'RewriteCond %{HTTP_USER_AGENT} ' ), -strlen( ' [NC]' ) );
351
 
352
  if ( ! $match ) {
353
+ Error::t( 'HTA_DNF', __( 'Mobile Agent Rules', 'litespeed-cache' ) );
354
  }
355
 
356
+ return $match;
357
  }
358
 
359
  /**
364
  * @since 1.1.0
365
  * @access public
366
  */
367
+ public function current_login_cookie( $kind = 'frontend' ) {
368
+ $rule = $this->_get_rule_by( self::MARKER_LOGIN_COOKIE, $kind );
 
369
 
370
  if( ! $rule ) {
371
+ Error::t( 'HTA_DNF', self::MARKER_LOGIN_COOKIE );
372
  }
373
 
374
  if( strpos( $rule, 'RewriteRule .? - [E=' ) !== 0 ) {
375
+ Error::t( 'HTA_LOGIN_COOKIE_INVALID' );
376
  }
377
 
378
+ $rule_cookie = substr( $rule, strlen( 'RewriteRule .? - [E=' ), -1 );
379
 
380
  if ( LITESPEED_SERVER_TYPE === 'LITESPEED_SERVER_OLS' ) {
381
+ $rule_cookie = trim( $rule_cookie, '"' );
382
  }
383
 
384
  // Drop `Cache-Vary:`
385
+ $rule_cookie = substr( $rule_cookie, strlen( 'Cache-Vary:' ) );
386
 
387
+ return $rule_cookie;
388
  }
389
 
390
  /**
393
  * @since 2.0
394
  * @access private
395
  */
396
+ private function _get_rule_by( $cond, $kind = 'frontend' ) {
397
+ clearstatcache();
398
+ $path = $this->htaccess_path( $kind );
 
399
  if ( ! $this->_readable( $kind ) ) {
400
+ return false;
401
  }
402
 
403
+ $rules = File::extract_from_markers( $path, self::MARKER );
404
  if( ! in_array( $cond . self::MARKER_START, $rules ) || ! in_array( $cond . self::MARKER_END, $rules ) ) {
405
+ return false;
406
  }
407
 
408
+ $key_start = array_search( $cond . self::MARKER_START, $rules );
409
+ $key_end = array_search( $cond . self::MARKER_END, $rules );
410
  if( $key_start === false || $key_end === false ) {
411
+ return false;
412
  }
413
 
414
+ $results = array_slice( $rules, $key_start + 1, $key_end - $key_start - 1 );
415
  if( ! $results ) {
416
+ return false;
417
  }
418
 
419
  if( count( $results ) == 1 ) {
420
+ return trim( $results[ 0 ] );
421
  }
422
 
423
+ return array_filter( $results );
424
  }
425
 
426
  /**
430
  * @access private
431
  * @return array Rules set
432
  */
433
+ private function _browser_cache_rules( $cfg ) {
 
434
  /**
435
  * Add ttl setting
436
  * @since 1.6.3
437
  */
438
+ $id = Base::O_CACHE_TTL_BROWSER;
439
+ $ttl = $cfg[ $id ];
440
  $rules = array(
441
  self::EXPIRES_MODULE_START,
442
  // '<FilesMatch "\.(pdf|ico|svg|xml|jpg|jpeg|png|gif|webp|ogg|mp4|webm|js|css|woff|woff2|ttf|eot)(\.gz)?$">',
473
  '',
474
  // '</FilesMatch>',
475
  self::LS_MODULE_END,
476
+ );
477
+ return $rules;
478
  }
479
 
480
  /**
484
  * @access private
485
  * @return array Rules set
486
  */
487
+ private function _cors_rules() {
 
488
  return array(
489
  '<FilesMatch "\.(ttf|ttc|otf|eot|woff|woff2|font\.css)$">',
490
  '<IfModule mod_headers.c>',
491
  'Header set Access-Control-Allow-Origin "*"',
492
  '</IfModule>',
493
  '</FilesMatch>',
494
+ );
495
  }
496
 
497
  /**
502
  * @param array $cfg The settings to be used for rewrite rule
503
  * @return array Rules array
504
  */
505
+ private function _generate_rules( $cfg ) {
506
+ $new_rules = array();
507
+ $new_rules_nonls = array();
508
+ $new_rules_backend = array();
509
+ $new_rules_backend_nonls = array();
 
510
 
511
  // mobile agents
512
+ $id = Base::O_CACHE_MOBILE_RULES;
513
  if ( ! empty( $cfg[ Base::O_CACHE_MOBILE ] ) && ! empty( $cfg[ $id ] ) ) {
514
+ $new_rules[] = self::MARKER_MOBILE . self::MARKER_START;
515
+ $new_rules[] = 'RewriteCond %{HTTP_USER_AGENT} ' . Utility::arr2regex( $cfg[ $id ], true ) . ' [NC]';
516
+ $new_rules[] = 'RewriteRule .* - [E=Cache-Control:vary=ismobile]';
517
+ $new_rules[] = self::MARKER_MOBILE . self::MARKER_END;
518
+ $new_rules[] = '';
519
  }
520
 
521
  // nocache cookie
522
+ $id = Base::O_CACHE_EXC_COOKIES;
523
  if ( ! empty( $cfg[ $id ] ) ) {
524
+ $new_rules[] = self::MARKER_NOCACHE_COOKIES . self::MARKER_START;
525
+ $new_rules[] = 'RewriteCond %{HTTP_COOKIE} ' . Utility::arr2regex( $cfg[ $id ], true );
526
+ $new_rules[] = 'RewriteRule .* - [E=Cache-Control:no-cache]';
527
+ $new_rules[] = self::MARKER_NOCACHE_COOKIES . self::MARKER_END;
528
+ $new_rules[] = '';
529
  }
530
 
531
  // nocache user agents
532
+ $id = Base::O_CACHE_EXC_USERAGENTS;
533
  if ( ! empty( $cfg[ $id ] ) ) {
534
+ $new_rules[] = self::MARKER_NOCACHE_USER_AGENTS . self::MARKER_START;
535
+ $new_rules[] = 'RewriteCond %{HTTP_USER_AGENT} ' . Utility::arr2regex( $cfg[ $id ], true );
536
+ $new_rules[] = 'RewriteRule .* - [E=Cache-Control:no-cache]';
537
+ $new_rules[] = self::MARKER_NOCACHE_USER_AGENTS . self::MARKER_END;
538
+ $new_rules[] = '';
539
  }
540
 
541
  // caching php resource
542
+ $id = Base::O_CACHE_RES;
543
  if ( ! empty( $cfg[ $id ] ) ) {
544
+ $new_rules[] = $new_rules_backend[] = self::MARKER_CACHE_RESOURCE . self::MARKER_START;
545
+ $new_rules[] = $new_rules_backend[] = 'RewriteRule ' . LSCWP_CONTENT_FOLDER . self::RW_PATTERN_RES . ' - [E=cache-control:max-age=3600]';
546
+ $new_rules[] = $new_rules_backend[] = self::MARKER_CACHE_RESOURCE . self::MARKER_END;
547
+ $new_rules[] = $new_rules_backend[] = '';
548
  }
549
 
550
  // check login cookie
551
+ $id = Base::O_CACHE_LOGIN_COOKIE;
552
 
553
  // Need to keep this due to different behavior of OLS when handling response vary header @Sep/22/2018
554
  if ( LITESPEED_SERVER_TYPE === 'LITESPEED_SERVER_OLS' ) {
555
  if ( ! empty( $cfg[ $id ] ) ) {
556
+ $cfg[ $id ] .= ',wp-postpass_' . COOKIEHASH;
557
  }
558
  else {
559
+ $cfg[ $id ] = 'wp-postpass_' . COOKIEHASH;
560
  }
561
  }
562
 
563
+ $tp_cookies = apply_filters( 'litespeed_api_vary', array() );
564
  if ( ! empty( $tp_cookies ) && is_array( $tp_cookies ) ) {
565
  if ( ! empty( $cfg[ $id ] ) ) {
566
+ $cfg[ $id ] .= ',' . implode( ',', $tp_cookies );
567
  }
568
  else {
569
+ $cfg[ $id ] = implode( ',', $tp_cookies );
570
  }
571
  }
572
  // frontend and backend
573
  if ( ! empty( $cfg[ $id ] ) ) {
574
+ $env = 'Cache-Vary:' . $cfg[ $id ];
575
  if ( LITESPEED_SERVER_TYPE === 'LITESPEED_SERVER_OLS' ) {
576
+ $env = '"' . $env . '"';
577
  }
578
+ $new_rules[] = $new_rules_backend[] = self::MARKER_LOGIN_COOKIE . self::MARKER_START;
579
+ $new_rules[] = $new_rules_backend[] = 'RewriteRule .? - [E=' . $env . ']';
580
+ $new_rules[] = $new_rules_backend[] = self::MARKER_LOGIN_COOKIE . self::MARKER_END;
581
+ $new_rules[] = '';
582
  }
583
 
584
  // favicon
585
  // frontend and backend
586
+ $id = Base::O_CACHE_FAVICON;
587
  if ( ! empty( $cfg[ $id ] ) ) {
588
+ $new_rules[] = $new_rules_backend[] = self::MARKER_FAVICON . self::MARKER_START;
589
+ $new_rules[] = $new_rules_backend[] = 'RewriteRule favicon\.ico$ - [E=cache-control:max-age=86400]';
590
+ $new_rules[] = $new_rules_backend[] = self::MARKER_FAVICON . self::MARKER_END;
591
+ $new_rules[] = '';
592
  }
593
 
594
  // CORS font rules
595
+ $id = Base::O_CDN;
596
  if ( ! empty( $cfg[ $id ] ) ) {
597
+ $new_rules[] = self::MARKER_CORS . self::MARKER_START;
598
+ $new_rules = array_merge( $new_rules, $this->_cors_rules() ); //todo: network
599
+ $new_rules[] = self::MARKER_CORS . self::MARKER_END;
600
+ $new_rules[] = '';
601
  }
602
 
603
  // webp support
604
+ $id = Base::O_IMG_OPTM_WEBP_REPLACE;
605
  if ( ! empty( $cfg[ $id ] ) ) {
606
+ $new_rules[] = self::MARKER_WEBP . self::MARKER_START;
607
+ $new_rules[] = 'RewriteCond %{HTTP_ACCEPT} "image/webp" [or]';
608
+ $new_rules[] = 'RewriteCond %{HTTP_USER_AGENT} "Page Speed"';
609
+ $new_rules[] = 'RewriteRule .* - [E=Cache-Control:vary=%{ENV:LSCACHE_VARY_VALUE}+webp]';
610
+ $new_rules[] = self::MARKER_WEBP . self::MARKER_END;
611
+ $new_rules[] = '';
612
  }
613
 
614
  // drop qs support
615
+ $id = Base::O_CACHE_DROP_QS;
616
  if ( ! empty( $cfg[ $id ] ) ) {
617
+ $new_rules[] = self::MARKER_DROPQS . self::MARKER_START;
618
  foreach ( $cfg[ $id ] as $v ) {
619
+ $new_rules[] = 'CacheKeyModify -qs:' . $v;
620
  }
621
+ $new_rules[] = self::MARKER_DROPQS . self::MARKER_END;
622
+ $new_rules[] = '';
623
  }
624
 
625
  // Browser cache
626
+ $id = Base::O_CACHE_BROWSER;
627
  if ( ! empty( $cfg[ $id ] ) ) {
628
+ $new_rules_nonls[] = $new_rules_backend_nonls[] = self::MARKER_BROWSER_CACHE . self::MARKER_START;
629
+ $new_rules_nonls = array_merge( $new_rules_nonls, $this->_browser_cache_rules( $cfg ) );
630
+ $new_rules_backend_nonls = array_merge( $new_rules_backend_nonls, $this->_browser_cache_rules( $cfg ) );
631
+ $new_rules_nonls[] = $new_rules_backend_nonls[] = self::MARKER_BROWSER_CACHE . self::MARKER_END;
632
+ $new_rules_nonls[] = '';
633
  }
634
 
635
  // Add module wrapper for LiteSpeed rules
636
  if ( $new_rules ) {
637
+ $new_rules = $this->_wrap_ls_module( $new_rules );
638
  }
639
 
640
  if ( $new_rules_backend ) {
641
+ $new_rules_backend = $this->_wrap_ls_module( $new_rules_backend );
642
  }
643
 
644
+ return array( $new_rules, $new_rules_backend, $new_rules_nonls, $new_rules_backend_nonls );
645
  }
646
 
647
  /**
650
  * @since 2.1.1
651
  * @access private
652
  */
653
+ private function _wrap_ls_module( $rules = array() ) {
 
654
  return array_merge(
655
  array( self::LS_MODULE_START ),
656
  $this->__rewrite_on,
657
  array( '' ),
658
  $rules,
659
  array( self::LS_MODULE_END )
660
+ );
661
  }
662
 
663
  /**
666
  * @since 2.1.1
667
  * @access public
668
  */
669
+ public function insert_ls_wrapper() {
670
+ $rules = $this->_wrap_ls_module();
671
+ $this->_insert_wrapper( $rules );
 
672
  }
673
 
674
  /**
678
  * @param array $rules
679
  * @return array wrapped rules with module info
680
  */
681
+ private function _wrap_do_no_edit( $rules ) {
 
682
  // When to clear rules, don't need DONOTEDIT msg
683
  if ( $rules === false || ! is_array( $rules ) ) {
684
+ return $rules;
685
  }
686
 
687
  $rules = array_merge(
688
  array( self::LS_MODULE_DONOTEDIT ),
689
  $rules,
690
  array( self::LS_MODULE_DONOTEDIT )
691
+ );
692
 
693
+ return $rules;
694
  }
695
 
696
  /**
701
  * @since 1.1.0
702
  * @access private
703
  */
704
+ private function _insert_wrapper( $rules = array(), $kind = false, $marker = false ) {
 
705
  if ( $kind != 'backend' ) {
706
+ $kind = 'frontend';
707
  }
708
 
709
  // Default marker is LiteSpeed marker `LSCACHE`
710
  if ( $marker === false ) {
711
+ $marker = self::MARKER;
712
  }
713
 
714
+ $this->_htaccess_backup( $kind );
715
 
716
+ File::insert_with_markers( $this->htaccess_path( $kind ), $this->_wrap_do_no_edit( $rules ), $marker, true );
717
  }
718
 
719
  /**
724
  * @since 1.3
725
  * @access public
726
  */
727
+ public function update( $cfg ) {
728
+ list( $frontend_rules, $backend_rules, $frontend_rules_nonls, $backend_rules_nonls ) = $this->_generate_rules( $cfg );
 
729
 
730
  // Check frontend content
731
+ list( $rules, $rules_nonls ) = $this->_extract_rules();
732
 
733
  // Check Non-LiteSpeed rules
734
  if ( $this->_wrap_do_no_edit( $frontend_rules_nonls ) != $rules_nonls ) {
735
+ Debug2::debug( '[Rules] Update non-ls frontend rules' );
736
  // Need to update frontend htaccess
737
  try {
738
+ $this->_insert_wrapper( $frontend_rules_nonls, false, self::MARKER_NONLS );
739
  } catch ( \Exception $e ) {
740
+ $manual_guide_codes = $this->_rewrite_codes_msg( $this->frontend_htaccess, $frontend_rules_nonls, self::MARKER_NONLS );
741
+ Debug2::debug( '[Rules] Update Failed' );
742
+ throw new \Exception( $manual_guide_codes );
743
  }
744
  }
745
 
746
  // Check LiteSpeed rules
747
  if ( $this->_wrap_do_no_edit( $frontend_rules ) != $rules ) {
748
+ Debug2::debug( '[Rules] Update frontend rules' );
749
  // Need to update frontend htaccess
750
  try {
751
+ $this->_insert_wrapper( $frontend_rules );
752
  } catch ( \Exception $e ) {
753
+ Debug2::debug( '[Rules] Update Failed' );
754
+ $manual_guide_codes = $this->_rewrite_codes_msg( $this->frontend_htaccess, $frontend_rules );
755
+ throw new \Exception( $manual_guide_codes );
756
  }
757
  }
758
 
759
  if ( $this->frontend_htaccess !== $this->backend_htaccess ) {
760
+ list( $rules, $rules_nonls ) = $this->_extract_rules( 'backend' );
761
 
762
  // Check Non-LiteSpeed rules for backend
763
  if ( $this->_wrap_do_no_edit( $backend_rules_nonls ) != $rules_nonls ) {
764
+ Debug2::debug( '[Rules] Update non-ls backend rules' );
765
  // Need to update frontend htaccess
766
  try {
767
+ $this->_insert_wrapper( $backend_rules_nonls, 'backend', self::MARKER_NONLS );
768
  } catch ( \Exception $e ) {
769
+ Debug2::debug( '[Rules] Update Failed' );
770
+ $manual_guide_codes = $this->_rewrite_codes_msg( $this->backend_htaccess, $backend_rules_nonls, self::MARKER_NONLS );
771
+ throw new \Exception( $manual_guide_codes );
772
  }
773
  }
774
 
775
  // Check backend content
776
  if ( $this->_wrap_do_no_edit( $backend_rules ) != $rules ) {
777
+ Debug2::debug( '[Rules] Update backend rules' );
778
  // Need to update backend htaccess
779
  try {
780
+ $this->_insert_wrapper( $backend_rules, 'backend' );
781
  } catch ( \Exception $e ) {
782
+ Debug2::debug( '[Rules] Update Failed' );
783
+ $manual_guide_codes = $this->_rewrite_codes_msg( $this->backend_htaccess, $backend_rules );
784
+ throw new \Exception( $manual_guide_codes );
785
  }
786
  }
787
  }
788
 
789
+ return true;
790
  }
791
 
792
  /**
798
  * @access private
799
  * @param string $kind Frontend or backend .htaccess file
800
  */
801
+ private function _extract_rules( $kind = 'frontend' ) {
802
+ clearstatcache();
803
+ $path = $this->htaccess_path( $kind );
 
804
  if ( ! $this->_readable( $kind ) ) {
805
+ Error::t( 'E_HTA_R' );
806
  }
807
 
808
+ $rules = File::extract_from_markers( $path, self::MARKER );
809
+ $rules_nonls = File::extract_from_markers( $path, self::MARKER_NONLS );
810
 
811
+ return array( $rules, $rules_nonls );
812
  }
813
 
814
  /**
819
  * @param array $rules
820
  * @return string final msg to output
821
  */
822
+ private function _rewrite_codes_msg( $file, $rules, $marker = false ) {
 
823
  return sprintf( __( '<p>Please add/replace the following codes into the beginning of %1$s:</p> %2$s' , 'litespeed-cache' ),
824
  $file,
825
  '<textarea style="width:100%;" rows="10" readonly>' . htmlspecialchars( $this->_wrap_rules_with_marker( $rules, $marker ) ) . '</textarea>'
826
+ );
827
  }
828
 
829
  /**
831
  *
832
  * @since 1.1.5
833
  */
834
+ private function _wrap_rules_with_marker( $rules, $marker = false ) {
 
835
  // Default marker is LiteSpeed marker `LSCACHE`
836
  if ( $marker === false ) {
837
+ $marker = self::MARKER;
838
  }
839
 
840
+ $start_marker = "# BEGIN {$marker}";
841
+ $end_marker = "# END {$marker}";
842
  $new_file_data = implode( "\n", array_merge(
843
  array( $start_marker ),
844
  $this->_wrap_do_no_edit($rules),
845
  array( $end_marker )
846
+ ) );
847
 
848
+ return $new_file_data;
849
  }
850
 
851
  /**
854
  * @since 1.0.4
855
  * @access public
856
  */
857
+ public function clear_rules() {
858
+ $this->_insert_wrapper( false );// Use false to avoid do-not-edit msg
 
859
  // Clear non ls rules
860
+ $this->_insert_wrapper( false, false, self::MARKER_NONLS );
861
 
862
  if ( $this->frontend_htaccess !== $this->backend_htaccess ) {
863
+ $this->_insert_wrapper( false, 'backend' );
864
+ $this->_insert_wrapper( false, 'backend', self::MARKER_NONLS );
865
  }
866
  }
867
 
873
  * @since 2.9 Used exception when saving
874
  * @access public
875
  */
876
+ public function htaccess_editor_save() {
 
877
  if ( ! isset( $_POST[ self::EDITOR_TEXTAREA_NAME ] ) ) {
878
+ return;
879
  }
880
 
881
+ $content = Admin::cleanup_text($_POST[self::EDITOR_TEXTAREA_NAME]);
882
 
883
  try {
884
+ $this->htaccess_save($content);
885
  } catch( \Exception $e ) {
886
+ Admin_Display::error( $e->getMessage() );
887
+ return;
888
  }
889
 
890
+ Admin_Display::succeed( __( 'File Saved.', 'litespeed-cache' ) );
891
 
892
  }
893
  }
src/media.cls.php CHANGED
@@ -8,18 +8,17 @@
8
  * @subpackage Core/inc
9
  * @author LiteSpeed Technologies <info@litespeedtech.com>
10
  */
11
- namespace LiteSpeed ;
12
 
13
- defined( 'WPINC' ) || exit ;
14
 
15
- class Media extends Instance
16
- {
17
- protected static $_instance ;
18
 
19
- const LIB_FILE_IMG_LAZYLOAD = 'assets/js/lazyload.min.js' ;
20
 
21
- private $content ;
22
- private $_wp_upload_dir ;
23
 
24
  /**
25
  * Init
@@ -27,8 +26,7 @@ class Media extends Instance
27
  * @since 1.4
28
  * @access protected
29
  */
30
- protected function __construct()
31
- {
32
  Debug2::debug2( '[Media] init' );
33
 
34
  $this->_wp_upload_dir = wp_upload_dir();
@@ -43,8 +41,7 @@ class Media extends Instance
43
  * @since 3.0
44
  * @access public
45
  */
46
- public function init()
47
- {
48
  if ( $this->can_media() ) {
49
  // Due to ajax call doesn't send correct accept header, have to limit webp to HTML only
50
  if ( Conf::val( Base::O_IMG_OPTM_WEBP_REPLACE ) ) {
@@ -53,17 +50,17 @@ class Media extends Instance
53
  * @since 1.6.2
54
  */
55
  // Moved to htaccess
56
- // add_filter( 'litespeed_vary', array( $this, 'vary_add' ) ) ;
57
 
58
  //
59
  if ( $this->webp_support() ) {
60
  // Hook to srcset
61
  if ( function_exists( 'wp_calculate_image_srcset' ) ) {
62
- add_filter( 'wp_calculate_image_srcset', array( $this, 'webp_srcset' ), 988 ) ;
63
  }
64
  // Hook to mime icon
65
- // add_filter( 'wp_get_attachment_image_src', array( $this, 'webp_attach_img_src' ), 988 ) ;// todo: need to check why not
66
- // add_filter( 'wp_get_attachment_url', array( $this, 'webp_url' ), 988 ) ; // disabled to avoid wp-admin display
67
  }
68
  }
69
 
@@ -71,14 +68,14 @@ class Media extends Instance
71
  * Replace gravatar
72
  * @since 3.0
73
  */
74
- Avatar::get_instance() ;
75
  }
76
 
77
  /**
78
  * JPG quality control
79
  * @since 3.0
80
  */
81
- add_filter( 'jpeg_quality', array( $this, 'adjust_jpg_quality' ) ) ;
82
 
83
  }
84
 
@@ -88,15 +85,14 @@ class Media extends Instance
88
  * @since 3.0
89
  * @access public
90
  */
91
- public function adjust_jpg_quality( $quality )
92
- {
93
- $v = Conf::val( Base::O_IMG_OPTM_JPG_QUALITY ) ;
94
 
95
  if ( $v ) {
96
- return $v ;
97
  }
98
 
99
- return $quality ;
100
  }
101
 
102
  /**
@@ -105,13 +101,12 @@ class Media extends Instance
105
  * @since 1.6.2
106
  * @access private
107
  */
108
- private function can_media()
109
- {
110
  if ( is_admin() ) {
111
- return false ;
112
  }
113
 
114
- return true ;
115
  }
116
 
117
  /**
@@ -120,15 +115,14 @@ class Media extends Instance
120
  * @since 1.6.3
121
  * @access public
122
  */
123
- public function after_admin_init()
124
- {
125
- add_filter( 'manage_media_columns', array( $this, 'media_row_title' ) ) ;
126
- add_filter( 'manage_media_custom_column', array( $this, 'media_row_actions' ), 10, 2 ) ;
127
 
128
- add_action( 'litespeed_media_row', array( $this, 'media_row_con' ) ) ;
129
 
130
  // Hook to attachment delete action
131
- add_action( 'delete_attachment', array( $this, 'delete_attachment' ) ) ;
132
  }
133
 
134
  /**
@@ -137,10 +131,9 @@ class Media extends Instance
137
  * @since 2.4.3
138
  * @access public
139
  */
140
- public function delete_attachment( $post_id )
141
- {
142
- Debug2::debug( '[Media] delete_attachment [pid] ' . $post_id ) ;
143
- Img_Optm::get_instance()->reset_row( $post_id ) ;
144
  }
145
 
146
  /**
@@ -151,16 +144,15 @@ class Media extends Instance
151
  * @since 2.9.8
152
  * @access public
153
  */
154
- public function info( $short_file_path, $post_id )
155
- {
156
- $real_file = $this->_wp_upload_dir[ 'basedir' ] . '/' . $short_file_path ;
157
 
158
  if ( file_exists( $real_file ) ) {
159
  return array(
160
  'url' => $this->_wp_upload_dir[ 'baseurl' ] . '/' . $short_file_path,
161
  'md5' => md5_file( $real_file ),
162
  'size' => filesize( $real_file ),
163
- ) ;
164
  }
165
 
166
  /**
@@ -168,12 +160,12 @@ class Media extends Instance
168
  * @since 2.9.8
169
  * @return array( 'url', 'md5', 'size' )
170
  */
171
- $info = apply_filters( 'litespeed_media_info', array(), $short_file_path, $post_id ) ;
172
  if ( ! empty( $info[ 'url' ] ) && ! empty( $info[ 'md5' ] ) && ! empty( $info[ 'size' ] ) ) {
173
- return $info ;
174
  }
175
 
176
- return false ;
177
  }
178
 
179
  /**
@@ -182,16 +174,15 @@ class Media extends Instance
182
  * @since 2.9.8
183
  * @access public
184
  */
185
- public function del( $short_file_path, $post_id )
186
- {
187
- $real_file = $this->_wp_upload_dir[ 'basedir' ] . '/' . $short_file_path ;
188
 
189
  if ( file_exists( $real_file ) ) {
190
- unlink( $real_file ) ;
191
- Debug2::debug( '[Media] deleted ' . $real_file ) ;
192
  }
193
 
194
- do_action( 'litespeed_media_del', $short_file_path, $post_id ) ;
195
  }
196
 
197
  /**
@@ -200,17 +191,16 @@ class Media extends Instance
200
  * @since 2.9.8
201
  * @access public
202
  */
203
- public function rename( $short_file_path, $short_file_path_new, $post_id )
204
- {
205
- $real_file = $this->_wp_upload_dir[ 'basedir' ] . '/' . $short_file_path ;
206
- $real_file_new = $this->_wp_upload_dir[ 'basedir' ] . '/' . $short_file_path_new ;
207
 
208
  if ( file_exists( $real_file ) ) {
209
- rename( $real_file, $real_file_new ) ;
210
- Debug2::debug( '[Media] renamed ' . $real_file . ' to ' . $real_file_new ) ;
211
  }
212
 
213
- do_action( 'litespeed_media_rename', $short_file_path, $short_file_path_new, $post_id ) ;
214
  }
215
 
216
  /**
@@ -219,11 +209,10 @@ class Media extends Instance
219
  * @since 1.6.3
220
  * @access public
221
  */
222
- public function media_row_title( $posts_columns )
223
- {
224
- $posts_columns[ 'imgoptm' ] = __( 'LiteSpeed Optimization', 'litespeed-cache' ) ;
225
 
226
- return $posts_columns ;
227
  }
228
 
229
  /**
@@ -232,8 +221,7 @@ class Media extends Instance
232
  * @since 1.6.2
233
  * @access public
234
  */
235
- public function media_row_actions( $column_name, $post_id )
236
- {
237
  if ( $column_name !== 'imgoptm' ) {
238
  return;
239
  }
@@ -247,39 +235,38 @@ class Media extends Instance
247
  *
248
  * @since 3.0
249
  */
250
- public function media_row_con( $post_id )
251
- {
252
- $att_info = wp_get_attachment_metadata( $post_id ) ;
253
  if ( empty( $att_info[ 'file' ] ) ) {
254
  return;
255
  }
256
 
257
- $short_path = $att_info[ 'file' ] ;
258
 
259
- $size_meta = get_post_meta( $post_id, Img_Optm::DB_SIZE, true ) ;
260
 
261
  echo '<p>';
262
  // Original image info
263
  if ( $size_meta && ! empty ( $size_meta[ 'ori_saved' ] ) ) {
264
- $percent = ceil( $size_meta[ 'ori_saved' ] * 100 / $size_meta[ 'ori_total' ] ) ;
265
 
266
- $extension = pathinfo( $short_path, PATHINFO_EXTENSION ) ;
267
- $bk_file = substr( $short_path, 0, -strlen( $extension ) ) . 'bk.' . $extension ;
268
- $bk_optm_file = substr( $short_path, 0, -strlen( $extension ) ) . 'bk.optm.' . $extension ;
269
 
270
- $link = Utility::build_url( Router::ACTION_IMG_OPTM, 'orig' . $post_id ) ;
271
- $desc = false ;
272
 
273
- $cls = '' ;
274
 
275
  if ( $this->info( $bk_file, $post_id ) ) {
276
- $curr_status = __( '(optm)', 'litespeed-cache' ) ;
277
- $desc = __( 'Currently using optimized version of file.', 'litespeed-cache' ) . '&#10;' . __( 'Click to switch to original (unoptimized) version.', 'litespeed-cache' ) ;
278
  }
279
  elseif ( $this->info( $bk_optm_file, $post_id ) ) {
280
- $cls .= ' litespeed-warning' ;
281
- $curr_status = __( '(non-optm)', 'litespeed-cache' ) ;
282
- $desc = __( 'Currently using original (unoptimized) version of file.', 'litespeed-cache' ) . '&#10;' . __( 'Click to switch to optimized version.', 'litespeed-cache' ) ;
283
  }
284
 
285
  echo GUI::pie_tiny( $percent, 24,
@@ -287,19 +274,19 @@ class Media extends Instance
287
  $percent . '%',
288
  Utility::real_size( $size_meta[ 'ori_saved' ] )
289
  ) , 'left'
290
- ) ;
291
 
292
- echo sprintf( __( 'Orig saved %s', 'litespeed-cache' ), $percent . '%' ) ;
293
 
294
  if ( $desc ) {
295
- echo sprintf( ' <a href="%1$s" class="litespeed-media-href %2$s" data-balloon-pos="left" data-balloon-break aria-label="%3$s">%4$s</a>', $link, $cls, $desc, $curr_status ) ;
296
  }
297
  else {
298
  echo sprintf(
299
  ' <span class="litespeed-desc" data-balloon-pos="left" data-balloon-break aria-label="%1$s">%2$s</span>',
300
  __( 'Using optimized version of file. ', 'litespeed-cache' ) . '&#10;' . __( 'No backup of original file exists.', 'litespeed-cache' ),
301
  __( '(optm)', 'litespeed-cache' )
302
- ) ;
303
  }
304
 
305
 
@@ -308,8 +295,8 @@ class Media extends Instance
308
  echo GUI::pie_tiny( 0, 24,
309
  __( 'Congratulation! Your file was already optmized', 'litespeed-cache' ),
310
  'left'
311
- ) ;
312
- echo sprintf( __( 'Orig %s', 'litespeed-cache' ), '<span class="litespeed-desc">' . __( '(no savings)', 'litespeed-cache' ) . '</span>' ) ;
313
  }
314
  else {
315
  echo __( 'Orig', 'litespeed-cache' ) . '<span class="litespeed-left10">—</span>';
@@ -319,21 +306,21 @@ class Media extends Instance
319
  echo '<p>';
320
  // WebP info
321
  if ( $size_meta && ! empty ( $size_meta[ 'webp_saved' ] ) ) {
322
- $percent = ceil( $size_meta[ 'webp_saved' ] * 100 / $size_meta[ 'webp_total' ] ) ;
323
 
324
- $link = Utility::build_url( Router::ACTION_IMG_OPTM, 'webp' . $post_id ) ;
325
- $desc = false ;
326
 
327
- $cls = '' ;
328
 
329
  if ( $this->info( $short_path . '.webp', $post_id ) ) {
330
- $curr_status = __( '(optm)', 'litespeed-cache' ) ;
331
- $desc = __( 'Currently using optimized version of WebP file.', 'litespeed-cache' ) . '&#10;' . __( 'Click to switch to original (unoptimized) version.', 'litespeed-cache' ) ;
332
  }
333
  elseif ( $this->info( $short_path . '.optm.webp', $post_id ) ) {
334
- $cls .= ' litespeed-warning' ;
335
- $curr_status = __( '(non-optm)', 'litespeed-cache' ) ;
336
- $desc = __( 'Currently using original (unoptimized) version of WebP file.', 'litespeed-cache' ) . '&#10;' . __( 'Click to switch to optimized version.', 'litespeed-cache' ) ;
337
  }
338
 
339
  echo GUI::pie_tiny( $percent, 24,
@@ -341,18 +328,18 @@ class Media extends Instance
341
  $percent . '%',
342
  Utility::real_size( $size_meta[ 'webp_saved' ] )
343
  ) , 'left'
344
- ) ;
345
- echo sprintf( __( 'WebP saved %s', 'litespeed-cache' ), $percent . '%' ) ;
346
 
347
  if ( $desc ) {
348
- echo sprintf( ' <a href="%1$s" class="litespeed-media-href %2$s" data-balloon-pos="left" data-balloon-break aria-label="%3$s">%4$s</a>', $link, $cls, $desc, $curr_status ) ;
349
  }
350
  else {
351
  echo sprintf(
352
  ' <span class="litespeed-desc" data-balloon-pos="left" data-balloon-break aria-label="%1$s">%2$s</span>',
353
  __( 'Using optimized version of file. ', 'litespeed-cache' ) . '&#10;' . __( 'No backup of unoptimized WebP file exists.', 'litespeed-cache' ),
354
  __( '(optm)', 'litespeed-cache' )
355
- ) ;
356
  }
357
 
358
  } else {
@@ -367,8 +354,8 @@ class Media extends Instance
367
  echo sprintf( '<div class="row-actions"><span class="delete"><a href="%1$s" class="">%2$s</a></span></div>',
368
  Utility::build_url( Router::ACTION_IMG_OPTM, Img_Optm::TYPE_RESET_ROW, false, null, array( 'id' => $post_id ) ),
369
  __( 'Restore from backup', 'litespeed-cache' )
370
- ) ;
371
- echo '</div>' ;
372
  }
373
  }
374
 
@@ -381,24 +368,24 @@ class Media extends Instance
381
  * @return array $sizes Data for all currently-registered image sizes.
382
  */
383
  public function get_image_sizes() {
384
- global $_wp_additional_image_sizes ;
385
  $sizes = array();
386
 
387
  foreach ( get_intermediate_image_sizes() as $_size ) {
388
  if ( in_array( $_size, array( 'thumbnail', 'medium', 'medium_large', 'large' ) ) ) {
389
- $sizes[ $_size ][ 'width' ] = get_option( $_size . '_size_w' ) ;
390
- $sizes[ $_size ][ 'height' ] = get_option( $_size . '_size_h' ) ;
391
- $sizes[ $_size ][ 'crop' ] = (bool) get_option( $_size . '_crop' ) ;
392
  } elseif ( isset( $_wp_additional_image_sizes[ $_size ] ) ) {
393
  $sizes[ $_size ] = array(
394
  'width' => $_wp_additional_image_sizes[ $_size ][ 'width' ],
395
  'height' => $_wp_additional_image_sizes[ $_size ][ 'height' ],
396
  'crop' => $_wp_additional_image_sizes[ $_size ][ 'crop' ]
397
- ) ;
398
  }
399
  }
400
 
401
- return $sizes ;
402
  }
403
 
404
 
@@ -408,17 +395,16 @@ class Media extends Instance
408
  * @since 1.6.2
409
  * @access public
410
  */
411
- private function webp_support()
412
- {
413
  if ( ! empty( $_SERVER[ 'HTTP_ACCEPT' ] ) && strpos( $_SERVER[ 'HTTP_ACCEPT' ], 'image/webp' ) !== false ) {
414
- return true ;
415
  }
416
 
417
  if ( ! empty( $_SERVER[ 'HTTP_USER_AGENT' ] ) && strpos( $_SERVER[ 'HTTP_USER_AGENT' ], 'Page Speed' ) !== false ) {
418
- return true ;
419
  }
420
 
421
- return false ;
422
  }
423
 
424
  /**
@@ -431,25 +417,24 @@ class Media extends Instance
431
  * @access public
432
  * @return string The buffer
433
  */
434
- public static function finalize( $content )
435
- {
436
  if ( defined( 'LITESPEED_NO_LAZY' ) ) {
437
- Debug2::debug2( '[Media] bypass: NO_LAZY const' ) ;
438
- return $content ;
439
  }
440
 
441
  if ( ! defined( 'LITESPEED_IS_HTML' ) ) {
442
- Debug2::debug2( '[Media] bypass: Not frontend HTML type' ) ;
443
- return $content ;
444
  }
445
 
446
- Debug2::debug( '[Media] finalize' ) ;
447
 
448
- $instance = self::get_instance() ;
449
- $instance->content = $content ;
450
 
451
- $instance->_finalize() ;
452
- return $instance->content ;
453
  }
454
 
455
  /**
@@ -458,81 +443,80 @@ class Media extends Instance
458
  * @since 1.4
459
  * @access private
460
  */
461
- private function _finalize()
462
- {
463
  /**
464
  * Use webp for optimized images
465
  * @since 1.6.2
466
  */
467
  if ( Conf::val( Base::O_IMG_OPTM_WEBP_REPLACE ) && $this->webp_support() ) {
468
- $this->_replace_buffer_img_webp() ;
469
  }
470
 
471
  /**
472
  * Check if URI is excluded
473
  * @since 3.0
474
  */
475
- $excludes = Conf::val( Base::O_MEDIA_LAZY_URI_EXC ) ;
476
  if ( $excludes ) {
477
- $result = Utility::str_hit_array( $_SERVER[ 'REQUEST_URI' ], $excludes ) ;
478
  if ( $result ) {
479
- Debug2::debug( '[Media] bypass lazyload: hit URI Excludes setting: ' . $result ) ;
480
- return ;
481
  }
482
  }
483
 
484
- $cfg_lazy = Conf::val( Base::O_MEDIA_LAZY ) ;
485
- $cfg_iframe_lazy = Conf::val( Base::O_MEDIA_IFRAME_LAZY ) ;
486
 
487
  if ( $cfg_lazy ) {
488
- list( $src_list, $html_list, $placeholder_list ) = $this->_parse_img() ;
489
- $html_list_ori = $html_list ;
490
  }
491
 
492
  // image lazy load
493
  if ( $cfg_lazy ) {
494
 
495
- $__placeholder = Placeholder::get_instance() ;
496
 
497
  foreach ( $html_list as $k => $v ) {
498
- $size = $placeholder_list[ $k ] ;
499
- $src = $src_list[ $k ] ;
500
 
501
- $html_list[ $k ] = $__placeholder->replace( $v, $src, $size ) ;
502
  }
503
  }
504
 
505
  if ( $cfg_lazy ) {
506
- $this->content = str_replace( $html_list_ori, $html_list, $this->content ) ;
507
  }
508
 
509
  // iframe lazy load
510
  if ( $cfg_iframe_lazy ) {
511
- $html_list = $this->_parse_iframe() ;
512
- $html_list_ori = $html_list ;
513
 
514
  foreach ( $html_list as $k => $v ) {
515
- $snippet = Conf::val( Base::O_OPTM_NOSCRIPT_RM ) ? '' : '<noscript>' . $v . '</noscript>' ;
516
- $v = str_replace( ' src=', ' data-src=', $v ) ;
517
- $v = str_replace( '<iframe ', '<iframe data-lazyloaded="1" src="about:blank" ', $v ) ;
518
- $snippet = $v . $snippet ;
519
 
520
- $html_list[ $k ] = $snippet ;
521
  }
522
 
523
- $this->content = str_replace( $html_list_ori, $html_list, $this->content ) ;
524
  }
525
 
526
  // Include lazyload lib js and init lazyload
527
  if ( $cfg_lazy || $cfg_iframe_lazy ) {
528
  if ( Conf::val( Base::O_MEDIA_LAZYJS_INLINE ) ) {
529
- $lazy_lib = '<script>' . File::read( LSCWP_DIR . self::LIB_FILE_IMG_LAZYLOAD ) . '</script>' ;
530
  } else {
531
- $lazy_lib_url = LSWCP_PLUGIN_URL . self::LIB_FILE_IMG_LAZYLOAD ;
532
- $lazy_lib = '<script src="' . $lazy_lib_url . '"></script>' ;
533
  }
534
 
535
- $this->content = str_replace( '</body>', $lazy_lib . '</body>', $this->content ) ;
536
  }
537
  }
538
 
@@ -544,41 +528,40 @@ class Media extends Instance
544
  * @access private
545
  * @return array All the src & related raw html list
546
  */
547
- private function _parse_img()
548
- {
549
  /**
550
  * Exclude list
551
  * @since 1.5
552
  * @since 2.7.1 Changed to array
553
  */
554
- $excludes = apply_filters( 'litespeed_media_lazy_img_excludes', Conf::val( Base::O_MEDIA_LAZY_EXC ) ) ;
555
 
556
- $cls_excludes = apply_filters( 'litespeed_media_lazy_img_cls_excludes', Conf::val( Base::O_MEDIA_LAZY_CLS_EXC ) ) ;
557
  $cls_excludes[] = 'skip-lazy'; // https://core.trac.wordpress.org/ticket/44427
558
 
559
- $src_list = array() ;
560
- $html_list = array() ;
561
- $placeholder_list = array() ;
562
 
563
- $content = preg_replace( '#<!--.*-->#sU', '', $this->content ) ;
564
  /**
565
  * Exclude parent classes
566
  * @since 3.0
567
  */
568
- $parent_cls_exc = apply_filters( 'litespeed_media_lazy_img_parent_cls_excludes', Conf::val( Base::O_MEDIA_LAZY_PARENT_CLS_EXC ) ) ;
569
  if ( $parent_cls_exc ) {
570
- Debug2::debug2( '[Media] Lazyload Class excludes', $parent_cls_exc ) ;
571
  foreach ( $parent_cls_exc as $v ) {
572
- $content = preg_replace( '#<(\w+) [^>]*class=(\'|")[^\'"]*' . preg_quote( $v, '#' ) . '[^\'"]*\2[^>]*>.*</\1>#sU', '', $content ) ;
573
  }
574
  }
575
 
576
- preg_match_all( '#<img \s*([^>]+)/?>#isU', $content, $matches, PREG_SET_ORDER ) ;
577
  foreach ( $matches as $match ) {
578
- $attrs = Utility::parse_attr( $match[ 1 ] ) ;
579
 
580
  if ( empty( $attrs[ 'src' ] ) ) {
581
- continue ;
582
  }
583
 
584
  /**
@@ -586,20 +569,20 @@ class Media extends Instance
586
  * @since 1.6
587
  */
588
  if ( strpos( $attrs[ 'src' ], 'base64' ) !== false || substr( $attrs[ 'src' ], 0, 5 ) === 'data:' ) {
589
- Debug2::debug2( '[Media] lazyload bypassed base64 img' ) ;
590
- continue ;
591
  }
592
 
593
- Debug2::debug2( '[Media] lazyload found: ' . $attrs[ 'src' ] ) ;
594
 
595
  if ( ! empty( $attrs[ 'data-no-lazy' ] ) || ! empty( $attrs[ 'data-skip-lazy' ] ) || ! empty( $attrs[ 'data-lazyloaded' ] ) || ! empty( $attrs[ 'data-src' ] ) || ! empty( $attrs[ 'data-srcset' ] ) ) {
596
- Debug2::debug2( '[Media] bypassed' ) ;
597
- continue ;
598
  }
599
 
600
  if ( ! empty( $attrs[ 'class' ] ) && $hit = Utility::str_hit_array( $attrs[ 'class' ], $cls_excludes ) ) {
601
- Debug2::debug2( '[Media] lazyload image cls excludes [hit] ' . $hit ) ;
602
- continue ;
603
  }
604
 
605
  /**
@@ -607,8 +590,8 @@ class Media extends Instance
607
  * @since 1.5
608
  */
609
  if ( $excludes && Utility::str_hit_array( $attrs[ 'src' ], $excludes ) ) {
610
- Debug2::debug2( '[Media] lazyload image exclude ' . $attrs[ 'src' ] ) ;
611
- continue ;
612
  }
613
 
614
  /**
@@ -617,26 +600,26 @@ class Media extends Instance
617
  * @since 3.0
618
  */
619
  if ( strpos( $attrs[ 'src' ], '{' ) !== false ) {
620
- Debug2::debug2( '[Media] image src has {} ' . $attrs[ 'src' ] ) ;
621
- continue ;
622
  }
623
 
624
  // to avoid multiple replacement
625
  if ( in_array( $match[ 0 ], $html_list ) ) {
626
- continue ;
627
  }
628
 
629
- $placeholder = false ;
630
  if ( ! empty( $attrs[ 'width' ] ) && ! empty( $attrs[ 'height' ] ) ) {
631
- $placeholder = $attrs[ 'width' ] . 'x' . $attrs[ 'height' ] ;
632
  }
633
 
634
- $src_list[] = $attrs[ 'src' ] ;
635
- $html_list[] = $match[ 0 ] ;
636
- $placeholder_list[] = $placeholder ;
637
  }
638
 
639
- return array( $src_list, $html_list, $placeholder_list ) ;
640
  }
641
 
642
  /**
@@ -646,61 +629,60 @@ class Media extends Instance
646
  * @access private
647
  * @return array All the src & related raw html list
648
  */
649
- private function _parse_iframe()
650
- {
651
  $cls_excludes = apply_filters( 'litespeed_media_iframe_lazy_cls_excludes', Conf::val( Base::O_MEDIA_IFRAME_LAZY_CLS_EXC ) );
652
  $cls_excludes[] = 'skip-lazy'; // https://core.trac.wordpress.org/ticket/44427
653
 
654
- $html_list = array() ;
655
 
656
- $content = preg_replace( '#<!--.*-->#sU', '', $this->content ) ;
657
 
658
  /**
659
  * Exclude parent classes
660
  * @since 3.0
661
  */
662
- $parent_cls_exc = apply_filters( 'litespeed_media_iframe_lazy_parent_cls_excludes', Conf::val( Base::O_MEDIA_IFRAME_LAZY_PARENT_CLS_EXC ) ) ;
663
  if ( $parent_cls_exc ) {
664
- Debug2::debug2( '[Media] Iframe Lazyload Class excludes', $parent_cls_exc ) ;
665
  foreach ( $parent_cls_exc as $v ) {
666
- $content = preg_replace( '#<(\w+) [^>]*class=(\'|")[^\'"]*' . preg_quote( $v, '#' ) . '[^\'"]*\2[^>]*>.*</\1>#sU', '', $content ) ;
667
  }
668
  }
669
 
670
- preg_match_all( '#<iframe \s*([^>]+)></iframe>#isU', $content, $matches, PREG_SET_ORDER ) ;
671
  foreach ( $matches as $match ) {
672
- $attrs = Utility::parse_attr( $match[ 1 ] ) ;
673
 
674
  if ( empty( $attrs[ 'src' ] ) ) {
675
- continue ;
676
  }
677
 
678
- Debug2::debug2( '[Media] found iframe: ' . $attrs[ 'src' ] ) ;
679
 
680
  if ( ! empty( $attrs[ 'data-no-lazy' ] ) || ! empty( $attrs[ 'data-skip-lazy' ] ) || ! empty( $attrs[ 'data-lazyloaded' ] ) || ! empty( $attrs[ 'data-src' ] ) ) {
681
- Debug2::debug2( '[Media] bypassed' ) ;
682
- continue ;
683
  }
684
 
685
  if ( ! empty( $attrs[ 'class' ] ) && $hit = Utility::str_hit_array( $attrs[ 'class' ], $cls_excludes ) ) {
686
- Debug2::debug2( '[Media] iframe lazyload cls excludes [hit] ' . $hit ) ;
687
- continue ;
688
  }
689
 
690
  if ( apply_filters( 'litespeed_iframe_lazyload_exc', false, $attrs[ 'src' ] ) ) {
691
- Debug2::debug2( '[Media] bypassed by filter' ) ;
692
- continue ;
693
  }
694
 
695
  // to avoid multiple replacement
696
  if ( in_array( $match[ 0 ], $html_list ) ) {
697
- continue ;
698
  }
699
 
700
- $html_list[] = $match[ 0 ] ;
701
  }
702
 
703
- return $html_list ;
704
  }
705
 
706
  /**
@@ -709,42 +691,41 @@ class Media extends Instance
709
  * @since 1.6.2
710
  * @access private
711
  */
712
- private function _replace_buffer_img_webp()
713
- {
714
- // preg_match_all( '#<img([^>]+?)src=([\'"\\\]*)([^\'"\s\\\>]+)([\'"\\\]*)([^>]*)>#i', $this->content, $matches ) ;
715
  /**
716
  * Added custom element & attribute support
717
  * @since 2.2.2
718
  */
719
- $webp_ele_to_check = Conf::val( Base::O_IMG_OPTM_WEBP_ATTR ) ;
720
 
721
  foreach ( $webp_ele_to_check as $v ) {
722
  if ( ! $v || strpos( $v, '.' ) === false ) {
723
- Debug2::debug2( '[Media] buffer_webp no . attribute ' . $v ) ;
724
- continue ;
725
  }
726
 
727
- Debug2::debug2( '[Media] buffer_webp attribute ' . $v ) ;
728
 
729
- $v = explode( '.', $v ) ;
730
- $attr = preg_quote( $v[ 1 ], '#' ) ;
731
  if ( $v[ 0 ] ) {
732
- $pattern = '#<' . preg_quote( $v[ 0 ], '#' ) . '([^>]+)' . $attr . '=([\'"])(.+)\g{2}#iU' ;
733
  }
734
  else {
735
- $pattern = '# ' . $attr . '=([\'"])(.+)\g{1}#iU' ;
736
  }
737
 
738
- preg_match_all( $pattern, $this->content, $matches ) ;
739
 
740
  foreach ( $matches[ $v[ 0 ] ? 3 : 2 ] as $k2 => $url ) {
741
  // Check if is a DATA-URI
742
  if ( strpos( $url, 'data:image' ) !== false ) {
743
- continue ;
744
  }
745
 
746
  if ( ! $url2 = $this->replace_webp( $url ) ) {
747
- continue ;
748
  }
749
 
750
  if ( $v[ 0 ] ) {
@@ -752,16 +733,16 @@ class Media extends Instance
752
  '<' . $v[ 0 ] . '%1$s' . $v[ 1 ] . '=%2$s',
753
  $matches[ 1 ][ $k2 ],
754
  $matches[ 2 ][ $k2 ] . $url2 . $matches[ 2 ][ $k2 ]
755
- ) ;
756
  }
757
  else {
758
  $html_snippet = sprintf(
759
  ' ' . $v[ 1 ] . '=%1$s',
760
  $matches[ 1 ][ $k2 ] . $url2 . $matches[ 1 ][ $k2 ]
761
- ) ;
762
  }
763
 
764
- $this->content = str_replace( $matches[ 0 ][ $k2 ], $html_snippet, $this->content ) ;
765
 
766
  }
767
  }
@@ -769,30 +750,30 @@ class Media extends Instance
769
  // parse srcset
770
  // todo: should apply this to cdn too
771
  if ( Conf::val( Base::O_IMG_OPTM_WEBP_REPLACE_SRCSET ) ) {
772
- $this->content = Utility::srcset_replace( $this->content, array( $this, 'replace_webp' ) ) ;
773
  }
774
 
775
  // Replace background-image
776
- preg_match_all( '#background\-image:(\s*)url\((.*)\)#iU', $this->content, $matches ) ;
777
  foreach ( $matches[ 2 ] as $k => $url ) {
778
  // Check if is a DATA-URI
779
  if ( strpos( $url, 'data:image' ) !== false ) {
780
- continue ;
781
  }
782
 
783
  /**
784
  * Support quotes in src `background-image: url('src')`
785
  * @since 2.9.3
786
  */
787
- $url = trim( $url, '\'"' ) ;
788
 
789
  if ( ! $url2 = $this->replace_webp( $url ) ) {
790
- continue ;
791
  }
792
 
793
- // $html_snippet = sprintf( 'background-image:%1$surl(%2$s)', $matches[ 1 ][ $k ], $url2 ) ;
794
- $html_snippet = str_replace( $url, $url2, $matches[ 0 ][ $k ] ) ;
795
- $this->content = str_replace( $matches[ 0 ][ $k ], $html_snippet, $this->content ) ;
796
  }
797
  }
798
 
@@ -804,13 +785,12 @@ class Media extends Instance
804
  * @param array $img The URL of the attachment image src, the width, the height
805
  * @return array
806
  */
807
- public function webp_attach_img_src( $img )
808
- {
809
- Debug2::debug2( '[Media] changing attach src: ' . $img[0] ) ;
810
  if ( $img && $url = $this->replace_webp( $img[ 0 ] ) ) {
811
- $img[ 0 ] = $url ;
812
  }
813
- return $img ;
814
  }
815
 
816
  /**
@@ -821,12 +801,11 @@ class Media extends Instance
821
  * @param string $url
822
  * @return string
823
  */
824
- public function webp_url( $url )
825
- {
826
  if ( $url && $url2 = $this->replace_webp( $url ) ) {
827
- $url = $url2 ;
828
  }
829
- return $url ;
830
  }
831
 
832
  /**
@@ -837,17 +816,16 @@ class Media extends Instance
837
  * @param array $srcs
838
  * @return array
839
  */
840
- public function webp_srcset( $srcs )
841
- {
842
  if ( $srcs ) {
843
  foreach ( $srcs as $w => $data ) {
844
  if( ! $url = $this->replace_webp( $data[ 'url' ] ) ) {
845
- continue ;
846
  }
847
- $srcs[ $w ][ 'url' ] = $url ;
848
  }
849
  }
850
- return $srcs ;
851
  }
852
 
853
  /**
@@ -856,13 +834,12 @@ class Media extends Instance
856
  * @since 1.6.2
857
  * @access public
858
  */
859
- public function replace_webp( $url )
860
- {
861
- Debug2::debug2( '[Media] webp replacing: ' . $url, 4 ) ;
862
 
863
  if ( substr( $url, -5 ) == '.webp' ) {
864
- Debug2::debug2( '[Media] already webp' ) ;
865
- return false ;
866
  }
867
 
868
  /**
@@ -874,21 +851,21 @@ class Media extends Instance
874
  if ( apply_filters( 'litespeed_media_check_ori', Utility::is_internal_file( $url ), $url ) ) {
875
  // check if has webp file
876
  if ( apply_filters( 'litespeed_media_check_webp', Utility::is_internal_file( $url, 'webp' ), $url ) ) {
877
- $url .= '.webp' ;
878
  }
879
  else {
880
- Debug2::debug2( '[Media] -no WebP file, bypassed' ) ;
881
- return false ;
882
  }
883
  }
884
  else {
885
- Debug2::debug2( '[Media] -no file, bypassed' ) ;
886
- return false ;
887
  }
888
 
889
- Debug2::debug2( '[Media] - replaced to: ' . $url ) ;
890
 
891
- return $url ;
892
  }
893
 
894
  }
8
  * @subpackage Core/inc
9
  * @author LiteSpeed Technologies <info@litespeedtech.com>
10
  */
11
+ namespace LiteSpeed;
12
 
13
+ defined( 'WPINC' ) || exit;
14
 
15
+ class Media extends Instance {
16
+ protected static $_instance;
 
17
 
18
+ const LIB_FILE_IMG_LAZYLOAD = 'assets/js/lazyload.min.js';
19
 
20
+ private $content;
21
+ private $_wp_upload_dir;
22
 
23
  /**
24
  * Init
26
  * @since 1.4
27
  * @access protected
28
  */
29
+ protected function __construct() {
 
30
  Debug2::debug2( '[Media] init' );
31
 
32
  $this->_wp_upload_dir = wp_upload_dir();
41
  * @since 3.0
42
  * @access public
43
  */
44
+ public function init() {
 
45
  if ( $this->can_media() ) {
46
  // Due to ajax call doesn't send correct accept header, have to limit webp to HTML only
47
  if ( Conf::val( Base::O_IMG_OPTM_WEBP_REPLACE ) ) {
50
  * @since 1.6.2
51
  */
52
  // Moved to htaccess
53
+ // add_filter( 'litespeed_vary', array( $this, 'vary_add' ) );
54
 
55
  //
56
  if ( $this->webp_support() ) {
57
  // Hook to srcset
58
  if ( function_exists( 'wp_calculate_image_srcset' ) ) {
59
+ add_filter( 'wp_calculate_image_srcset', array( $this, 'webp_srcset' ), 988 );
60
  }
61
  // Hook to mime icon
62
+ // add_filter( 'wp_get_attachment_image_src', array( $this, 'webp_attach_img_src' ), 988 );// todo: need to check why not
63
+ // add_filter( 'wp_get_attachment_url', array( $this, 'webp_url' ), 988 ); // disabled to avoid wp-admin display
64
  }
65
  }
66
 
68
  * Replace gravatar
69
  * @since 3.0
70
  */
71
+ Avatar::get_instance();
72
  }
73
 
74
  /**
75
  * JPG quality control
76
  * @since 3.0
77
  */
78
+ add_filter( 'jpeg_quality', array( $this, 'adjust_jpg_quality' ) );
79
 
80
  }
81
 
85
  * @since 3.0
86
  * @access public
87
  */
88
+ public function adjust_jpg_quality( $quality ) {
89
+ $v = Conf::val( Base::O_IMG_OPTM_JPG_QUALITY );
 
90
 
91
  if ( $v ) {
92
+ return $v;
93
  }
94
 
95
+ return $quality;
96
  }
97
 
98
  /**
101
  * @since 1.6.2
102
  * @access private
103
  */
104
+ private function can_media() {
 
105
  if ( is_admin() ) {
106
+ return false;
107
  }
108
 
109
+ return true;
110
  }
111
 
112
  /**
115
  * @since 1.6.3
116
  * @access public
117
  */
118
+ public function after_admin_init() {
119
+ add_filter( 'manage_media_columns', array( $this, 'media_row_title' ) );
120
+ add_filter( 'manage_media_custom_column', array( $this, 'media_row_actions' ), 10, 2 );
 
121
 
122
+ add_action( 'litespeed_media_row', array( $this, 'media_row_con' ) );
123
 
124
  // Hook to attachment delete action
125
+ add_action( 'delete_attachment', array( $this, 'delete_attachment' ) );
126
  }
127
 
128
  /**
131
  * @since 2.4.3
132
  * @access public
133
  */
134
+ public function delete_attachment( $post_id ) {
135
+ Debug2::debug( '[Media] delete_attachment [pid] ' . $post_id );
136
+ Img_Optm::get_instance()->reset_row( $post_id );
 
137
  }
138
 
139
  /**
144
  * @since 2.9.8
145
  * @access public
146
  */
147
+ public function info( $short_file_path, $post_id ) {
148
+ $real_file = $this->_wp_upload_dir[ 'basedir' ] . '/' . $short_file_path;
 
149
 
150
  if ( file_exists( $real_file ) ) {
151
  return array(
152
  'url' => $this->_wp_upload_dir[ 'baseurl' ] . '/' . $short_file_path,
153
  'md5' => md5_file( $real_file ),
154
  'size' => filesize( $real_file ),
155
+ );
156
  }
157
 
158
  /**
160
  * @since 2.9.8
161
  * @return array( 'url', 'md5', 'size' )
162
  */
163
+ $info = apply_filters( 'litespeed_media_info', array(), $short_file_path, $post_id );
164
  if ( ! empty( $info[ 'url' ] ) && ! empty( $info[ 'md5' ] ) && ! empty( $info[ 'size' ] ) ) {
165
+ return $info;
166
  }
167
 
168
+ return false;
169
  }
170
 
171
  /**
174
  * @since 2.9.8
175
  * @access public
176
  */
177
+ public function del( $short_file_path, $post_id ) {
178
+ $real_file = $this->_wp_upload_dir[ 'basedir' ] . '/' . $short_file_path;
 
179
 
180
  if ( file_exists( $real_file ) ) {
181
+ unlink( $real_file );
182
+ Debug2::debug( '[Media] deleted ' . $real_file );
183
  }
184
 
185
+ do_action( 'litespeed_media_del', $short_file_path, $post_id );
186
  }
187
 
188
  /**
191
  * @since 2.9.8
192
  * @access public
193
  */
194
+ public function rename( $short_file_path, $short_file_path_new, $post_id ) {
195
+ $real_file = $this->_wp_upload_dir[ 'basedir' ] . '/' . $short_file_path;
196
+ $real_file_new = $this->_wp_upload_dir[ 'basedir' ] . '/' . $short_file_path_new;
 
197
 
198
  if ( file_exists( $real_file ) ) {
199
+ rename( $real_file, $real_file_new );
200
+ Debug2::debug( '[Media] renamed ' . $real_file . ' to ' . $real_file_new );
201
  }
202
 
203
+ do_action( 'litespeed_media_rename', $short_file_path, $short_file_path_new, $post_id );
204
  }
205
 
206
  /**
209
  * @since 1.6.3
210
  * @access public
211
  */
212
+ public function media_row_title( $posts_columns ) {
213
+ $posts_columns[ 'imgoptm' ] = __( 'LiteSpeed Optimization', 'litespeed-cache' );
 
214
 
215
+ return $posts_columns;
216
  }
217
 
218
  /**
221
  * @since 1.6.2
222
  * @access public
223
  */
224
+ public function media_row_actions( $column_name, $post_id ) {
 
225
  if ( $column_name !== 'imgoptm' ) {
226
  return;
227
  }
235
  *
236
  * @since 3.0
237
  */
238
+ public function media_row_con( $post_id ) {
239
+ $att_info = wp_get_attachment_metadata( $post_id );
 
240
  if ( empty( $att_info[ 'file' ] ) ) {
241
  return;
242
  }
243
 
244
+ $short_path = $att_info[ 'file' ];
245
 
246
+ $size_meta = get_post_meta( $post_id, Img_Optm::DB_SIZE, true );
247
 
248
  echo '<p>';
249
  // Original image info
250
  if ( $size_meta && ! empty ( $size_meta[ 'ori_saved' ] ) ) {
251
+ $percent = ceil( $size_meta[ 'ori_saved' ] * 100 / $size_meta[ 'ori_total' ] );
252
 
253
+ $extension = pathinfo( $short_path, PATHINFO_EXTENSION );
254
+ $bk_file = substr( $short_path, 0, -strlen( $extension ) ) . 'bk.' . $extension;
255
+ $bk_optm_file = substr( $short_path, 0, -strlen( $extension ) ) . 'bk.optm.' . $extension;
256
 
257
+ $link = Utility::build_url( Router::ACTION_IMG_OPTM, 'orig' . $post_id );
258
+ $desc = false;
259
 
260
+ $cls = '';
261
 
262
  if ( $this->info( $bk_file, $post_id ) ) {
263
+ $curr_status = __( '(optm)', 'litespeed-cache' );
264
+ $desc = __( 'Currently using optimized version of file.', 'litespeed-cache' ) . '&#10;' . __( 'Click to switch to original (unoptimized) version.', 'litespeed-cache' );
265
  }
266
  elseif ( $this->info( $bk_optm_file, $post_id ) ) {
267
+ $cls .= ' litespeed-warning';
268
+ $curr_status = __( '(non-optm)', 'litespeed-cache' );
269
+ $desc = __( 'Currently using original (unoptimized) version of file.', 'litespeed-cache' ) . '&#10;' . __( 'Click to switch to optimized version.', 'litespeed-cache' );
270
  }
271
 
272
  echo GUI::pie_tiny( $percent, 24,
274
  $percent . '%',
275
  Utility::real_size( $size_meta[ 'ori_saved' ] )
276
  ) , 'left'
277
+ );
278
 
279
+ echo sprintf( __( 'Orig saved %s', 'litespeed-cache' ), $percent . '%' );
280
 
281
  if ( $desc ) {
282
+ echo sprintf( ' <a href="%1$s" class="litespeed-media-href %2$s" data-balloon-pos="left" data-balloon-break aria-label="%3$s">%4$s</a>', $link, $cls, $desc, $curr_status );
283
  }
284
  else {
285
  echo sprintf(
286
  ' <span class="litespeed-desc" data-balloon-pos="left" data-balloon-break aria-label="%1$s">%2$s</span>',
287
  __( 'Using optimized version of file. ', 'litespeed-cache' ) . '&#10;' . __( 'No backup of original file exists.', 'litespeed-cache' ),
288
  __( '(optm)', 'litespeed-cache' )
289
+ );
290
  }
291
 
292
 
295
  echo GUI::pie_tiny( 0, 24,
296
  __( 'Congratulation! Your file was already optmized', 'litespeed-cache' ),
297
  'left'
298
+ );
299
+ echo sprintf( __( 'Orig %s', 'litespeed-cache' ), '<span class="litespeed-desc">' . __( '(no savings)', 'litespeed-cache' ) . '</span>' );
300
  }
301
  else {
302
  echo __( 'Orig', 'litespeed-cache' ) . '<span class="litespeed-left10">—</span>';
306
  echo '<p>';
307
  // WebP info
308
  if ( $size_meta && ! empty ( $size_meta[ 'webp_saved' ] ) ) {
309
+ $percent = ceil( $size_meta[ 'webp_saved' ] * 100 / $size_meta[ 'webp_total' ] );
310
 
311
+ $link = Utility::build_url( Router::ACTION_IMG_OPTM, 'webp' . $post_id );
312
+ $desc = false;
313
 
314
+ $cls = '';
315
 
316
  if ( $this->info( $short_path . '.webp', $post_id ) ) {
317
+ $curr_status = __( '(optm)', 'litespeed-cache' );
318
+ $desc = __( 'Currently using optimized version of WebP file.', 'litespeed-cache' ) . '&#10;' . __( 'Click to switch to original (unoptimized) version.', 'litespeed-cache' );
319
  }
320
  elseif ( $this->info( $short_path . '.optm.webp', $post_id ) ) {
321
+ $cls .= ' litespeed-warning';
322
+ $curr_status = __( '(non-optm)', 'litespeed-cache' );
323
+ $desc = __( 'Currently using original (unoptimized) version of WebP file.', 'litespeed-cache' ) . '&#10;' . __( 'Click to switch to optimized version.', 'litespeed-cache' );
324
  }
325
 
326
  echo GUI::pie_tiny( $percent, 24,
328
  $percent . '%',
329
  Utility::real_size( $size_meta[ 'webp_saved' ] )
330
  ) , 'left'
331
+ );
332
+ echo sprintf( __( 'WebP saved %s', 'litespeed-cache' ), $percent . '%' );
333
 
334
  if ( $desc ) {
335
+ echo sprintf( ' <a href="%1$s" class="litespeed-media-href %2$s" data-balloon-pos="left" data-balloon-break aria-label="%3$s">%4$s</a>', $link, $cls, $desc, $curr_status );
336
  }
337
  else {
338
  echo sprintf(
339
  ' <span class="litespeed-desc" data-balloon-pos="left" data-balloon-break aria-label="%1$s">%2$s</span>',
340
  __( 'Using optimized version of file. ', 'litespeed-cache' ) . '&#10;' . __( 'No backup of unoptimized WebP file exists.', 'litespeed-cache' ),
341
  __( '(optm)', 'litespeed-cache' )
342
+ );
343
  }
344
 
345
  } else {
354
  echo sprintf( '<div class="row-actions"><span class="delete"><a href="%1$s" class="">%2$s</a></span></div>',
355
  Utility::build_url( Router::ACTION_IMG_OPTM, Img_Optm::TYPE_RESET_ROW, false, null, array( 'id' => $post_id ) ),
356
  __( 'Restore from backup', 'litespeed-cache' )
357
+ );
358
+ echo '</div>';
359
  }
360
  }
361
 
368
  * @return array $sizes Data for all currently-registered image sizes.
369
  */
370
  public function get_image_sizes() {
371
+ global $_wp_additional_image_sizes;
372
  $sizes = array();
373
 
374
  foreach ( get_intermediate_image_sizes() as $_size ) {
375
  if ( in_array( $_size, array( 'thumbnail', 'medium', 'medium_large', 'large' ) ) ) {
376
+ $sizes[ $_size ][ 'width' ] = get_option( $_size . '_size_w' );
377
+ $sizes[ $_size ][ 'height' ] = get_option( $_size . '_size_h' );
378
+ $sizes[ $_size ][ 'crop' ] = (bool) get_option( $_size . '_crop' );
379
  } elseif ( isset( $_wp_additional_image_sizes[ $_size ] ) ) {
380
  $sizes[ $_size ] = array(
381
  'width' => $_wp_additional_image_sizes[ $_size ][ 'width' ],
382
  'height' => $_wp_additional_image_sizes[ $_size ][ 'height' ],
383
  'crop' => $_wp_additional_image_sizes[ $_size ][ 'crop' ]
384
+ );
385
  }
386
  }
387
 
388
+ return $sizes;
389
  }
390
 
391
 
395
  * @since 1.6.2
396
  * @access public
397
  */
398
+ private function webp_support() {
 
399
  if ( ! empty( $_SERVER[ 'HTTP_ACCEPT' ] ) && strpos( $_SERVER[ 'HTTP_ACCEPT' ], 'image/webp' ) !== false ) {
400
+ return true;
401
  }
402
 
403
  if ( ! empty( $_SERVER[ 'HTTP_USER_AGENT' ] ) && strpos( $_SERVER[ 'HTTP_USER_AGENT' ], 'Page Speed' ) !== false ) {
404
+ return true;
405
  }
406
 
407
+ return false;
408
  }
409
 
410
  /**
417
  * @access public
418
  * @return string The buffer
419
  */
420
+ public static function finalize( $content ) {
 
421
  if ( defined( 'LITESPEED_NO_LAZY' ) ) {
422
+ Debug2::debug2( '[Media] bypass: NO_LAZY const' );
423
+ return $content;
424
  }
425
 
426
  if ( ! defined( 'LITESPEED_IS_HTML' ) ) {
427
+ Debug2::debug2( '[Media] bypass: Not frontend HTML type' );
428
+ return $content;
429
  }
430
 
431
+ Debug2::debug( '[Media] finalize' );
432
 
433
+ $instance = self::get_instance();
434
+ $instance->content = $content;
435
 
436
+ $instance->_finalize();
437
+ return $instance->content;
438
  }
439
 
440
  /**
443
  * @since 1.4
444
  * @access private
445
  */
446
+ private function _finalize() {
 
447
  /**
448
  * Use webp for optimized images
449
  * @since 1.6.2
450
  */
451
  if ( Conf::val( Base::O_IMG_OPTM_WEBP_REPLACE ) && $this->webp_support() ) {
452
+ $this->_replace_buffer_img_webp();
453
  }
454
 
455
  /**
456
  * Check if URI is excluded
457
  * @since 3.0
458
  */
459
+ $excludes = Conf::val( Base::O_MEDIA_LAZY_URI_EXC );
460
  if ( $excludes ) {
461
+ $result = Utility::str_hit_array( $_SERVER[ 'REQUEST_URI' ], $excludes );
462
  if ( $result ) {
463
+ Debug2::debug( '[Media] bypass lazyload: hit URI Excludes setting: ' . $result );
464
+ return;
465
  }
466
  }
467
 
468
+ $cfg_lazy = Conf::val( Base::O_MEDIA_LAZY );
469
+ $cfg_iframe_lazy = Conf::val( Base::O_MEDIA_IFRAME_LAZY );
470
 
471
  if ( $cfg_lazy ) {
472
+ list( $src_list, $html_list, $placeholder_list ) = $this->_parse_img();
473
+ $html_list_ori = $html_list;
474
  }
475
 
476
  // image lazy load
477
  if ( $cfg_lazy ) {
478
 
479
+ $__placeholder = Placeholder::get_instance();
480
 
481
  foreach ( $html_list as $k => $v ) {
482
+ $size = $placeholder_list[ $k ];
483
+ $src = $src_list[ $k ];
484
 
485
+ $html_list[ $k ] = $__placeholder->replace( $v, $src, $size );
486
  }
487
  }
488
 
489
  if ( $cfg_lazy ) {
490
+ $this->content = str_replace( $html_list_ori, $html_list, $this->content );
491
  }
492
 
493
  // iframe lazy load
494
  if ( $cfg_iframe_lazy ) {
495
+ $html_list = $this->_parse_iframe();
496
+ $html_list_ori = $html_list;
497
 
498
  foreach ( $html_list as $k => $v ) {
499
+ $snippet = Conf::val( Base::O_OPTM_NOSCRIPT_RM ) ? '' : '<noscript>' . $v . '</noscript>';
500
+ $v = str_replace( ' src=', ' data-src=', $v );
501
+ $v = str_replace( '<iframe ', '<iframe data-lazyloaded="1" src="about:blank" ', $v );
502
+ $snippet = $v . $snippet;
503
 
504
+ $html_list[ $k ] = $snippet;
505
  }
506
 
507
+ $this->content = str_replace( $html_list_ori, $html_list, $this->content );
508
  }
509
 
510
  // Include lazyload lib js and init lazyload
511
  if ( $cfg_lazy || $cfg_iframe_lazy ) {
512
  if ( Conf::val( Base::O_MEDIA_LAZYJS_INLINE ) ) {
513
+ $lazy_lib = '<script>' . File::read( LSCWP_DIR . self::LIB_FILE_IMG_LAZYLOAD ) . '</script>';
514
  } else {
515
+ $lazy_lib_url = LSWCP_PLUGIN_URL . self::LIB_FILE_IMG_LAZYLOAD;
516
+ $lazy_lib = '<script src="' . $lazy_lib_url . '"></script>';
517
  }
518
 
519
+ $this->content = str_replace( '</body>', $lazy_lib . '</body>', $this->content );
520
  }
521
  }
522
 
528
  * @access private
529
  * @return array All the src & related raw html list
530
  */
531
+ private function _parse_img() {
 
532
  /**
533
  * Exclude list
534
  * @since 1.5
535
  * @since 2.7.1 Changed to array
536
  */
537
+ $excludes = apply_filters( 'litespeed_media_lazy_img_excludes', Conf::val( Base::O_MEDIA_LAZY_EXC ) );
538
 
539
+ $cls_excludes = apply_filters( 'litespeed_media_lazy_img_cls_excludes', Conf::val( Base::O_MEDIA_LAZY_CLS_EXC ) );
540
  $cls_excludes[] = 'skip-lazy'; // https://core.trac.wordpress.org/ticket/44427
541
 
542
+ $src_list = array();
543
+ $html_list = array();
544
+ $placeholder_list = array();
545
 
546
+ $content = preg_replace( '#<!--.*-->#sU', '', $this->content );
547
  /**
548
  * Exclude parent classes
549
  * @since 3.0
550
  */
551
+ $parent_cls_exc = apply_filters( 'litespeed_media_lazy_img_parent_cls_excludes', Conf::val( Base::O_MEDIA_LAZY_PARENT_CLS_EXC ) );
552
  if ( $parent_cls_exc ) {
553
+ Debug2::debug2( '[Media] Lazyload Class excludes', $parent_cls_exc );
554
  foreach ( $parent_cls_exc as $v ) {
555
+ $content = preg_replace( '#<(\w+) [^>]*class=(\'|")[^\'"]*' . preg_quote( $v, '#' ) . '[^\'"]*\2[^>]*>.*</\1>#sU', '', $content );
556
  }
557
  }
558
 
559
+ preg_match_all( '#<img \s*([^>]+)/?>#isU', $content, $matches, PREG_SET_ORDER );
560
  foreach ( $matches as $match ) {
561
+ $attrs = Utility::parse_attr( $match[ 1 ] );
562
 
563
  if ( empty( $attrs[ 'src' ] ) ) {
564
+ continue;
565
  }
566
 
567
  /**
569
  * @since 1.6
570
  */
571
  if ( strpos( $attrs[ 'src' ], 'base64' ) !== false || substr( $attrs[ 'src' ], 0, 5 ) === 'data:' ) {
572
+ Debug2::debug2( '[Media] lazyload bypassed base64 img' );
573
+ continue;
574
  }
575
 
576
+ Debug2::debug2( '[Media] lazyload found: ' . $attrs[ 'src' ] );
577
 
578
  if ( ! empty( $attrs[ 'data-no-lazy' ] ) || ! empty( $attrs[ 'data-skip-lazy' ] ) || ! empty( $attrs[ 'data-lazyloaded' ] ) || ! empty( $attrs[ 'data-src' ] ) || ! empty( $attrs[ 'data-srcset' ] ) ) {
579
+ Debug2::debug2( '[Media] bypassed' );
580
+ continue;
581
  }
582
 
583
  if ( ! empty( $attrs[ 'class' ] ) && $hit = Utility::str_hit_array( $attrs[ 'class' ], $cls_excludes ) ) {
584
+ Debug2::debug2( '[Media] lazyload image cls excludes [hit] ' . $hit );
585
+ continue;
586
  }
587
 
588
  /**
590
  * @since 1.5
591
  */
592
  if ( $excludes && Utility::str_hit_array( $attrs[ 'src' ], $excludes ) ) {
593
+ Debug2::debug2( '[Media] lazyload image exclude ' . $attrs[ 'src' ] );
594
+ continue;
595
  }
596
 
597
  /**
600
  * @since 3.0
601
  */
602
  if ( strpos( $attrs[ 'src' ], '{' ) !== false ) {
603
+ Debug2::debug2( '[Media] image src has {} ' . $attrs[ 'src' ] );
604
+ continue;
605
  }
606
 
607
  // to avoid multiple replacement
608
  if ( in_array( $match[ 0 ], $html_list ) ) {
609
+ continue;
610
  }
611
 
612
+ $placeholder = false;
613
  if ( ! empty( $attrs[ 'width' ] ) && ! empty( $attrs[ 'height' ] ) ) {
614
+ $placeholder = $attrs[ 'width' ] . 'x' . $attrs[ 'height' ];
615
  }
616
 
617
+ $src_list[] = $attrs[ 'src' ];
618
+ $html_list[] = $match[ 0 ];
619
+ $placeholder_list[] = $placeholder;
620
  }
621
 
622
+ return array( $src_list, $html_list, $placeholder_list );
623
  }
624
 
625
  /**
629
  * @access private
630
  * @return array All the src & related raw html list
631
  */
632
+ private function _parse_iframe() {
 
633
  $cls_excludes = apply_filters( 'litespeed_media_iframe_lazy_cls_excludes', Conf::val( Base::O_MEDIA_IFRAME_LAZY_CLS_EXC ) );
634
  $cls_excludes[] = 'skip-lazy'; // https://core.trac.wordpress.org/ticket/44427
635
 
636
+ $html_list = array();
637
 
638
+ $content = preg_replace( '#<!--.*-->#sU', '', $this->content );
639
 
640
  /**
641
  * Exclude parent classes
642
  * @since 3.0
643
  */
644
+ $parent_cls_exc = apply_filters( 'litespeed_media_iframe_lazy_parent_cls_excludes', Conf::val( Base::O_MEDIA_IFRAME_LAZY_PARENT_CLS_EXC ) );
645
  if ( $parent_cls_exc ) {
646
+ Debug2::debug2( '[Media] Iframe Lazyload Class excludes', $parent_cls_exc );
647
  foreach ( $parent_cls_exc as $v ) {
648
+ $content = preg_replace( '#<(\w+) [^>]*class=(\'|")[^\'"]*' . preg_quote( $v, '#' ) . '[^\'"]*\2[^>]*>.*</\1>#sU', '', $content );
649
  }
650
  }
651
 
652
+ preg_match_all( '#<iframe \s*([^>]+)></iframe>#isU', $content, $matches, PREG_SET_ORDER );
653
  foreach ( $matches as $match ) {
654
+ $attrs = Utility::parse_attr( $match[ 1 ] );
655
 
656
  if ( empty( $attrs[ 'src' ] ) ) {
657
+ continue;
658
  }
659
 
660
+ Debug2::debug2( '[Media] found iframe: ' . $attrs[ 'src' ] );
661
 
662
  if ( ! empty( $attrs[ 'data-no-lazy' ] ) || ! empty( $attrs[ 'data-skip-lazy' ] ) || ! empty( $attrs[ 'data-lazyloaded' ] ) || ! empty( $attrs[ 'data-src' ] ) ) {
663
+ Debug2::debug2( '[Media] bypassed' );
664
+ continue;
665
  }
666
 
667
  if ( ! empty( $attrs[ 'class' ] ) && $hit = Utility::str_hit_array( $attrs[ 'class' ], $cls_excludes ) ) {
668
+ Debug2::debug2( '[Media] iframe lazyload cls excludes [hit] ' . $hit );
669
+ continue;
670
  }
671
 
672
  if ( apply_filters( 'litespeed_iframe_lazyload_exc', false, $attrs[ 'src' ] ) ) {
673
+ Debug2::debug2( '[Media] bypassed by filter' );
674
+ continue;
675
  }
676
 
677
  // to avoid multiple replacement
678
  if ( in_array( $match[ 0 ], $html_list ) ) {
679
+ continue;
680
  }
681
 
682
+ $html_list[] = $match[ 0 ];
683
  }
684
 
685
+ return $html_list;
686
  }
687
 
688
  /**
691
  * @since 1.6.2
692
  * @access private
693
  */
694
+ private function _replace_buffer_img_webp() {
695
+ // preg_match_all( '#<img([^>]+?)src=([\'"\\\]*)([^\'"\s\\\>]+)([\'"\\\]*)([^>]*)>#i', $this->content, $matches );
 
696
  /**
697
  * Added custom element & attribute support
698
  * @since 2.2.2
699
  */
700
+ $webp_ele_to_check = Conf::val( Base::O_IMG_OPTM_WEBP_ATTR );
701
 
702
  foreach ( $webp_ele_to_check as $v ) {
703
  if ( ! $v || strpos( $v, '.' ) === false ) {
704
+ Debug2::debug2( '[Media] buffer_webp no . attribute ' . $v );
705
+ continue;
706
  }
707
 
708
+ Debug2::debug2( '[Media] buffer_webp attribute ' . $v );
709
 
710
+ $v = explode( '.', $v );
711
+ $attr = preg_quote( $v[ 1 ], '#' );
712
  if ( $v[ 0 ] ) {
713
+ $pattern = '#<' . preg_quote( $v[ 0 ], '#' ) . '([^>]+)' . $attr . '=([\'"])(.+)\g{2}#iU';
714
  }
715
  else {
716
+ $pattern = '# ' . $attr . '=([\'"])(.+)\g{1}#iU';
717
  }
718
 
719
+ preg_match_all( $pattern, $this->content, $matches );
720
 
721
  foreach ( $matches[ $v[ 0 ] ? 3 : 2 ] as $k2 => $url ) {
722
  // Check if is a DATA-URI
723
  if ( strpos( $url, 'data:image' ) !== false ) {
724
+ continue;
725
  }
726
 
727
  if ( ! $url2 = $this->replace_webp( $url ) ) {
728
+ continue;
729
  }
730
 
731
  if ( $v[ 0 ] ) {
733
  '<' . $v[ 0 ] . '%1$s' . $v[ 1 ] . '=%2$s',
734
  $matches[ 1 ][ $k2 ],
735
  $matches[ 2 ][ $k2 ] . $url2 . $matches[ 2 ][ $k2 ]
736
+ );
737
  }
738
  else {
739
  $html_snippet = sprintf(
740
  ' ' . $v[ 1 ] . '=%1$s',
741
  $matches[ 1 ][ $k2 ] . $url2 . $matches[ 1 ][ $k2 ]
742
+ );
743
  }
744
 
745
+ $this->content = str_replace( $matches[ 0 ][ $k2 ], $html_snippet, $this->content );
746
 
747
  }
748
  }
750
  // parse srcset
751
  // todo: should apply this to cdn too
752
  if ( Conf::val( Base::O_IMG_OPTM_WEBP_REPLACE_SRCSET ) ) {
753
+ $this->content = Utility::srcset_replace( $this->content, array( $this, 'replace_webp' ) );
754
  }
755
 
756
  // Replace background-image
757
+ preg_match_all( '#background\-image:(\s*)url\((.*)\)#iU', $this->content, $matches );
758
  foreach ( $matches[ 2 ] as $k => $url ) {
759
  // Check if is a DATA-URI
760
  if ( strpos( $url, 'data:image' ) !== false ) {
761
+ continue;
762
  }
763
 
764
  /**
765
  * Support quotes in src `background-image: url('src')`
766
  * @since 2.9.3
767
  */
768
+ $url = trim( $url, '\'"' );
769
 
770
  if ( ! $url2 = $this->replace_webp( $url ) ) {
771
+ continue;
772
  }
773
 
774
+ // $html_snippet = sprintf( 'background-image:%1$surl(%2$s)', $matches[ 1 ][ $k ], $url2 );
775
+ $html_snippet = str_replace( $url, $url2, $matches[ 0 ][ $k ] );
776
+ $this->content = str_replace( $matches[ 0 ][ $k ], $html_snippet, $this->content );
777
  }
778
  }
779
 
785
  * @param array $img The URL of the attachment image src, the width, the height
786
  * @return array
787
  */
788
+ public function webp_attach_img_src( $img ) {
789
+ Debug2::debug2( '[Media] changing attach src: ' . $img[0] );
 
790
  if ( $img && $url = $this->replace_webp( $img[ 0 ] ) ) {
791
+ $img[ 0 ] = $url;
792
  }
793
+ return $img;
794
  }
795
 
796
  /**
801
  * @param string $url
802
  * @return string
803
  */
804
+ public function webp_url( $url ) {
 
805
  if ( $url && $url2 = $this->replace_webp( $url ) ) {
806
+ $url = $url2;
807
  }
808
+ return $url;
809
  }
810
 
811
  /**
816
  * @param array $srcs
817
  * @return array
818
  */
819
+ public function webp_srcset( $srcs ) {
 
820
  if ( $srcs ) {
821
  foreach ( $srcs as $w => $data ) {
822
  if( ! $url = $this->replace_webp( $data[ 'url' ] ) ) {
823
+ continue;
824
  }
825
+ $srcs[ $w ][ 'url' ] = $url;
826
  }
827
  }
828
+ return $srcs;
829
  }
830
 
831
  /**
834
  * @since 1.6.2
835
  * @access public
836
  */
837
+ public function replace_webp( $url ) {
838
+ Debug2::debug2( '[Media] webp replacing: ' . $url, 4 );
 
839
 
840
  if ( substr( $url, -5 ) == '.webp' ) {
841
+ Debug2::debug2( '[Media] already webp' );
842
+ return false;
843
  }
844
 
845
  /**
851
  if ( apply_filters( 'litespeed_media_check_ori', Utility::is_internal_file( $url ), $url ) ) {
852
  // check if has webp file
853
  if ( apply_filters( 'litespeed_media_check_webp', Utility::is_internal_file( $url, 'webp' ), $url ) ) {
854
+ $url .= '.webp';
855
  }
856
  else {
857
+ Debug2::debug2( '[Media] -no WebP file, bypassed' );
858
+ return false;
859
  }
860
  }
861
  else {
862
+ Debug2::debug2( '[Media] -no file, bypassed' );
863
+ return false;
864
  }
865
 
866
+ Debug2::debug2( '[Media] - replaced to: ' . $url );
867
 
868
+ return $url;
869
  }
870
 
871
  }
src/object.lib.php CHANGED
@@ -4,27 +4,25 @@
4
  *
5
  * @since 1.8
6
  */
7
- defined( 'WPINC' ) || exit ;
8
 
9
  /**
10
  * Handle exception
11
  */
12
  if ( ! function_exists( 'litespeed_exception_handler' ) ) {
13
- function litespeed_exception_handler( $errno, $errstr, $errfile, $errline )
14
- {
15
- throw new \ErrorException($errstr, 0, $errno, $errfile, $errline) ;
16
  }
17
  }
18
 
19
- require_once __DIR__ . '/object-cache.cls.php' ;
20
 
21
  /**
22
  * Sets up Object Cache Global and assigns it.
23
  *
24
  * @since 1.8
25
  */
26
- function wp_cache_init()
27
- {
28
  $GLOBALS['wp_object_cache'] = WP_Object_Cache::get_instance();
29
  }
30
 
@@ -33,11 +31,10 @@ function wp_cache_init()
33
  *
34
  * @since 1.8
35
  */
36
- function wp_cache_get( $key, $group = '', $force = false, &$found = null )
37
- {
38
- global $wp_object_cache ;
39
 
40
- return $wp_object_cache->get( $key, $group, $force, $found ) ;
41
  }
42
 
43
  /**
@@ -45,11 +42,10 @@ function wp_cache_get( $key, $group = '', $force = false, &$found = null )
45
  *
46
  * @since 1.8
47
  */
48
- function wp_cache_set( $key, $data, $group = '', $expire = 0 )
49
- {
50
- global $wp_object_cache ;
51
 
52
- return $wp_object_cache->set( $key, $data, $group, $expire ) ;
53
  }
54
 
55
  /**
@@ -57,11 +53,10 @@ function wp_cache_set( $key, $data, $group = '', $expire = 0 )
57
  *
58
  * @since 1.8
59
  */
60
- function wp_cache_add( $key, $data, $group = '', $expire = 0 )
61
- {
62
- global $wp_object_cache ;
63
 
64
- return $wp_object_cache->add( $key, $data, $group, $expire ) ;
65
  }
66
 
67
  /**
@@ -69,11 +64,10 @@ function wp_cache_add( $key, $data, $group = '', $expire = 0 )
69
  *
70
  * @since 1.8
71
  */
72
- function wp_cache_replace( $key, $data, $group = '', $expire = 0 )
73
- {
74
- global $wp_object_cache ;
75
 
76
- return $wp_object_cache->replace( $key, $data, $group, $expire ) ;
77
  }
78
 
79
  /**
@@ -81,11 +75,10 @@ function wp_cache_replace( $key, $data, $group = '', $expire = 0 )
81
  *
82
  * @since 1.8
83
  */
84
- function wp_cache_incr( $key, $offset = 1, $group = '' )
85
- {
86
- global $wp_object_cache ;
87
 
88
- return $wp_object_cache->incr_desr( $key, $offset, $group ) ;
89
  }
90
 
91
  /**
@@ -93,11 +86,10 @@ function wp_cache_incr( $key, $offset = 1, $group = '' )
93
  *
94
  * @since 1.8
95
  */
96
- function wp_cache_decr( $key, $offset = 1, $group = '' )
97
- {
98
- global $wp_object_cache ;
99
 
100
- return $wp_object_cache->incr_desr( $key, $offset, $group, false ) ;
101
  }
102
 
103
  /**
@@ -105,11 +97,10 @@ function wp_cache_decr( $key, $offset = 1, $group = '' )
105
  *
106
  * @since 1.8
107
  */
108
- function wp_cache_delete( $key, $group = '' )
109
- {
110
- global $wp_object_cache ;
111
 
112
- return $wp_object_cache->delete( $key, $group ) ;
113
  }
114
 
115
  /**
@@ -117,11 +108,10 @@ function wp_cache_delete( $key, $group = '' )
117
  *
118
  * @since 1.8
119
  */
120
- function wp_cache_flush()
121
- {
122
- global $wp_object_cache ;
123
 
124
- return $wp_object_cache->flush() ;
125
  }
126
 
127
  /**
@@ -129,11 +119,10 @@ function wp_cache_flush()
129
  *
130
  * @since 1.8
131
  */
132
- function wp_cache_add_global_groups( $groups )
133
- {
134
- global $wp_object_cache ;
135
 
136
- $wp_object_cache->add_global_groups( $groups ) ;
137
  }
138
 
139
  /**
@@ -141,11 +130,10 @@ function wp_cache_add_global_groups( $groups )
141
  *
142
  * @since 1.8
143
  */
144
- function wp_cache_add_non_persistent_groups( $groups )
145
- {
146
- global $wp_object_cache ;
147
 
148
- $wp_object_cache->add_non_persistent_groups( $groups ) ;
149
  }
150
 
151
  /**
@@ -161,9 +149,9 @@ function wp_cache_add_non_persistent_groups( $groups )
161
  * @param int $blog_id Site ID.
162
  */
163
  function wp_cache_switch_to_blog( $blog_id ) {
164
- global $wp_object_cache ;
165
 
166
- $wp_object_cache->switch_to_blog( $blog_id ) ;
167
  }
168
 
169
  /**
@@ -171,30 +159,28 @@ function wp_cache_switch_to_blog( $blog_id ) {
171
  *
172
  * @since 1.8
173
  */
174
- function wp_cache_close()
175
- {
176
- return true ;
177
  }
178
 
179
 
180
 
181
- class WP_Object_Cache
182
- {
183
- protected static $_instance ;
184
 
185
- private $_object_cache ;
186
 
187
- private $_cache = array() ;
188
- private $_cache_404 = array() ;
189
 
190
- private $cache_total = 0 ;
191
- private $count_hit_incall = 0 ;
192
- private $count_hit = 0 ;
193
- private $count_miss_incall = 0 ;
194
- private $count_miss = 0 ;
195
- private $count_set = 0 ;
196
 
197
- private $blog_prefix ;
198
 
199
  /**
200
  * Init
@@ -202,18 +188,17 @@ class WP_Object_Cache
202
  * @since 1.8
203
  * @access protected
204
  */
205
- protected function __construct()
206
- {
207
- $this->_object_cache = \LiteSpeed\Object_Cache::get_instance() ;
208
 
209
- $this->multisite = is_multisite() ;
210
- $this->blog_prefix = $this->multisite ? get_current_blog_id() . ':' : '' ;
211
 
212
  /**
213
  * Fix multiple instance using same oc issue
214
  * @since 1.8.2
215
  */
216
- ! defined( 'LSOC_PREFIX' ) && define( 'LSOC_PREFIX', substr( md5( __FILE__ ), -5 ) ) ;
217
  }
218
 
219
  /**
@@ -222,16 +207,15 @@ class WP_Object_Cache
222
  * @since 1.8
223
  * @access public
224
  */
225
- public function debug()
226
- {
227
  $log = ' [total] ' . $this->cache_total
228
  . ' [hit_incall] ' . $this->count_hit_incall
229
  . ' [hit] ' . $this->count_hit
230
  . ' [miss_incall] ' . $this->count_miss_incall
231
  . ' [miss] ' . $this->count_miss
232
- . ' [set] ' . $this->count_set ;
233
 
234
- return $log ;
235
  }
236
 
237
  /**
@@ -240,62 +224,61 @@ class WP_Object_Cache
240
  * @since 1.8
241
  * @access public
242
  */
243
- public function get( $key, $group = 'default', $force = false, &$found = null )
244
- {
245
- $final_key = $this->_key( $key, $group ) ;
246
  // error_log('');
247
  // error_log("oc: get \t\t\t[key] " . $final_key . ( $force ? "\t\t\t [forced] " : '' ) );
248
- $found = false ;
249
- $found_in_oc = false ;
250
- $cache_val = false ;
251
  if ( array_key_exists( $final_key, $this->_cache ) && ! $force ) {
252
- $found = true ;
253
- $cache_val = $this->_cache[ $final_key ] ;
254
- $this->count_hit_incall ++ ;
255
  }
256
  elseif ( ! array_key_exists( $final_key, $this->_cache_404 ) && ! $this->_object_cache->is_non_persistent( $group ) ) {
257
- $v = $this->_object_cache->get( $final_key ) ;
258
 
259
  if ( $v !== null ) {
260
- $v = @maybe_unserialize( $v ) ;
261
  }
262
 
263
  // To be compatible with false val
264
  if ( is_array( $v ) && array_key_exists( 'data', $v ) ) {
265
- $this->count_hit ++ ;
266
- $found = true ;
267
- $found_in_oc = true ;
268
- $cache_val = $v[ 'data' ] ;
269
  }
270
  else { // Can't find key, cache it to 404
271
- // error_log("oc: add404\t\t\t[key] " . $final_key ) ;
272
- $this->_cache_404[ $final_key ] = 1 ;
273
- $this->count_miss ++ ;
274
  }
275
  }
276
  else {
277
- $this->count_miss_incall ++ ;
278
  }
279
 
280
  if ( is_object( $cache_val ) ) {
281
- $cache_val = clone $cache_val ;
282
  }
283
 
284
  // If not found but has `Store Transients` cfg on, still need to follow WP's get_transient() logic
285
  if ( ! $found && $this->_object_cache->store_transients( $group ) ) {
286
- $cache_val = $this->_transient_get( $key, $group ) ;
287
  if ( $cache_val ) {
288
- $found = true ; // $found not used for now (v1.8.3)
289
  }
290
  }
291
 
292
  if ( $found_in_oc ) {
293
- $this->_cache[ $final_key ] = $cache_val ;
294
  }
295
 
296
- $this->cache_total ++ ;
297
 
298
- return $cache_val ;
299
  }
300
 
301
  /**
@@ -304,31 +287,30 @@ class WP_Object_Cache
304
  * @since 1.8
305
  * @access public
306
  */
307
- public function set( $key, $data, $group = 'default', $expire = 0 )
308
- {
309
- $final_key = $this->_key( $key, $group ) ;
310
 
311
  if ( is_object( $data ) ) {
312
- $data = clone $data ;
313
  }
314
- // error_log("oc: set \t\t\t[key] " . $final_key ) ;
315
- $this->_cache[ $final_key ] = $data ;
316
 
317
  if( array_key_exists( $final_key, $this->_cache_404 ) ) {
318
- // error_log("oc: unset404\t\t\t[key] " . $final_key ) ;
319
- unset( $this->_cache_404[ $final_key ] ) ;
320
  }
321
 
322
  if ( ! $this->_object_cache->is_non_persistent( $group ) ) {
323
- $this->_object_cache->set( $final_key, serialize( array( 'data' => $data ) ), $expire ) ;
324
- $this->count_set ++ ;
325
  }
326
 
327
  if ( $this->_object_cache->store_transients( $group ) ) {
328
- $this->_transient_set( $key, $data, $group, $expire ) ;
329
  }
330
 
331
- return true ;
332
  }
333
 
334
  /**
@@ -337,19 +319,18 @@ class WP_Object_Cache
337
  * @since 1.8
338
  * @access public
339
  */
340
- public function add( $key, $data, $group = 'default', $expire = 0 )
341
- {
342
  if ( wp_suspend_cache_addition() ) {
343
- return false ;
344
  }
345
 
346
- $final_key = $this->_key( $key, $group ) ;
347
 
348
  if ( array_key_exists( $final_key, $this->_cache ) ) {
349
- return false ;
350
  }
351
 
352
- return $this->set( $key, $data, $group, $expire ) ;
353
  }
354
 
355
  /**
@@ -358,15 +339,14 @@ class WP_Object_Cache
358
  * @since 1.8
359
  * @access public
360
  */
361
- public function replace( $key, $data, $group = 'default', $expire = 0 )
362
- {
363
- $final_key = $this->_key( $key, $group ) ;
364
 
365
  if ( ! array_key_exists( $final_key, $this->_cache ) ) {
366
- return false ;
367
  }
368
 
369
- return $this->set( $key, $data, $group, $expire ) ;
370
  }
371
 
372
  /**
@@ -375,34 +355,33 @@ class WP_Object_Cache
375
  * @since 1.8
376
  * @access public
377
  */
378
- public function incr_desr( $key, $offset = 1, $group = 'default', $incr = true )
379
- {
380
- $cache_val = $this->get( $key, $group ) ;
381
 
382
  if ( $cache_val === false ) {
383
- return false ;
384
  }
385
 
386
  if ( ! is_numeric( $cache_val ) ) {
387
- $cache_val = 0 ;
388
  }
389
 
390
- $offset = (int) $offset ;
391
 
392
  if ( $incr ) {
393
- $cache_val += $offset ;
394
  }
395
  else {
396
- $cache_val -= $offset ;
397
  }
398
 
399
  if ( $cache_val < 0 ) {
400
- $cache_val = 0 ;
401
  }
402
 
403
- $this->set( $key, $cache_val, $group ) ;
404
 
405
- return $cache_val ;
406
  }
407
 
408
  /**
@@ -411,25 +390,24 @@ class WP_Object_Cache
411
  * @since 1.8
412
  * @access public
413
  */
414
- public function delete( $key, $group = 'default' )
415
- {
416
 
417
- $final_key = $this->_key( $key, $group ) ;
418
 
419
  if ( $this->_object_cache->store_transients( $group ) ) {
420
- $this->_transient_del( $key, $group ) ;
421
  }
422
 
423
  if ( array_key_exists( $final_key, $this->_cache ) ) {
424
- unset( $this->_cache[ $final_key ] ) ;
425
  }
426
- // error_log("oc: delete \t\t\t[key] " . $final_key ) ;
427
 
428
  if ( $this->_object_cache->is_non_persistent( $group ) ) {
429
- return false ;
430
  }
431
 
432
- return $this->_object_cache->delete( $final_key ) ;
433
  }
434
 
435
  /**
@@ -438,15 +416,14 @@ class WP_Object_Cache
438
  * @since 1.8
439
  * @access public
440
  */
441
- public function flush()
442
- {
443
- $this->_cache = array() ;
444
- $this->_cache_404 = array() ;
445
- // error_log("oc: flush " ) ;
446
 
447
- $this->_object_cache->flush() ;
448
 
449
- return true ;
450
  }
451
 
452
  /**
@@ -455,9 +432,8 @@ class WP_Object_Cache
455
  * @since 1.8
456
  * @access public
457
  */
458
- public function add_global_groups( $groups )
459
- {
460
- $this->_object_cache->add_global_groups( $groups ) ;
461
  }
462
 
463
  /**
@@ -466,9 +442,8 @@ class WP_Object_Cache
466
  * @since 1.8
467
  * @access public
468
  */
469
- public function add_non_persistent_groups( $groups )
470
- {
471
- $this->_object_cache->add_non_persistent_groups( $groups ) ;
472
  }
473
 
474
  /**
@@ -477,11 +452,10 @@ class WP_Object_Cache
477
  * @since 1.8
478
  * @access private
479
  */
480
- private function _key( $key, $group = 'default' )
481
- {
482
- $prefix = $this->_object_cache->is_global( $group ) ? '' : $this->blog_prefix ;
483
 
484
- return LSOC_PREFIX . $prefix . $group . '.' . $key ;
485
  }
486
 
487
  /**
@@ -494,8 +468,8 @@ class WP_Object_Cache
494
  * @param int $blog_id Blog ID.
495
  */
496
  public function switch_to_blog( $blog_id ) {
497
- $blog_id = (int) $blog_id ;
498
- $this->blog_prefix = $this->multisite ? $blog_id . ':' : '' ;
499
  }
500
 
501
  /**
@@ -505,8 +479,7 @@ class WP_Object_Cache
505
  * @access private
506
  * @see `wp-includes/option.php` function `get_transient`/`set_site_transient`
507
  */
508
- private function _transient_get( $transient, $group )
509
- {
510
  if ( $group == 'transient' ) {
511
  /**** Ori WP func start ****/
512
  $transient_option = '_transient_' . $transient;
@@ -547,10 +520,10 @@ class WP_Object_Cache
547
  /**** Ori WP func end ****/
548
  }
549
  else {
550
- $value = false ;
551
  }
552
 
553
- return $value ;
554
  }
555
 
556
  /**
@@ -560,8 +533,7 @@ class WP_Object_Cache
560
  * @access private
561
  * @see `wp-includes/option.php` function `set_transient`/`set_site_transient`
562
  */
563
- private function _transient_set( $transient, $value, $group, $expiration )
564
- {
565
  if ( $group == 'transient' ) {
566
  /**** Ori WP func start ****/
567
  $transient_timeout = '_transient_timeout_' . $transient;
@@ -609,10 +581,10 @@ class WP_Object_Cache
609
  /**** Ori WP func end ****/
610
  }
611
  else {
612
- $result = null ;
613
  }
614
 
615
- return $result ;
616
  }
617
 
618
  /**
@@ -622,8 +594,7 @@ class WP_Object_Cache
622
  * @access private
623
  * @see `wp-includes/option.php` function `delete_transient`/`delete_site_transient`
624
  */
625
- private function _transient_del( $transient, $group )
626
- {
627
  if ( $group == 'transient' ) {
628
  /**** Ori WP func start ****/
629
  $option_timeout = '_transient_timeout_' . $transient;
@@ -650,12 +621,11 @@ class WP_Object_Cache
650
  * @since 1.8
651
  * @access public
652
  */
653
- public static function get_instance()
654
- {
655
  if ( ! isset( self::$_instance ) ) {
656
- self::$_instance = new self() ;
657
  }
658
 
659
- return self::$_instance ;
660
  }
661
  }
4
  *
5
  * @since 1.8
6
  */
7
+ defined( 'WPINC' ) || exit;
8
 
9
  /**
10
  * Handle exception
11
  */
12
  if ( ! function_exists( 'litespeed_exception_handler' ) ) {
13
+ function litespeed_exception_handler( $errno, $errstr, $errfile, $errline ) {
14
+ throw new \ErrorException($errstr, 0, $errno, $errfile, $errline);
 
15
  }
16
  }
17
 
18
+ require_once __DIR__ . '/object-cache.cls.php';
19
 
20
  /**
21
  * Sets up Object Cache Global and assigns it.
22
  *
23
  * @since 1.8
24
  */
25
+ function wp_cache_init() {
 
26
  $GLOBALS['wp_object_cache'] = WP_Object_Cache::get_instance();
27
  }
28
 
31
  *
32
  * @since 1.8
33
  */
34
+ function wp_cache_get( $key, $group = '', $force = false, &$found = null ) {
35
+ global $wp_object_cache;
 
36
 
37
+ return $wp_object_cache->get( $key, $group, $force, $found );
38
  }
39
 
40
  /**
42
  *
43
  * @since 1.8
44
  */
45
+ function wp_cache_set( $key, $data, $group = '', $expire = 0 ) {
46
+ global $wp_object_cache;
 
47
 
48
+ return $wp_object_cache->set( $key, $data, $group, $expire );
49
  }
50
 
51
  /**
53
  *
54
  * @since 1.8
55
  */
56
+ function wp_cache_add( $key, $data, $group = '', $expire = 0 ) {
57
+ global $wp_object_cache;
 
58
 
59
+ return $wp_object_cache->add( $key, $data, $group, $expire );
60
  }
61
 
62
  /**
64
  *
65
  * @since 1.8
66
  */
67
+ function wp_cache_replace( $key, $data, $group = '', $expire = 0 ) {
68
+ global $wp_object_cache;
 
69
 
70
+ return $wp_object_cache->replace( $key, $data, $group, $expire );
71
  }
72
 
73
  /**
75
  *
76
  * @since 1.8
77
  */
78
+ function wp_cache_incr( $key, $offset = 1, $group = '' ) {
79
+ global $wp_object_cache;
 
80
 
81
+ return $wp_object_cache->incr_desr( $key, $offset, $group );
82
  }
83
 
84
  /**
86
  *
87
  * @since 1.8
88
  */
89
+ function wp_cache_decr( $key, $offset = 1, $group = '' ) {
90
+ global $wp_object_cache;
 
91
 
92
+ return $wp_object_cache->incr_desr( $key, $offset, $group, false );
93
  }
94
 
95
  /**
97
  *
98
  * @since 1.8
99
  */
100
+ function wp_cache_delete( $key, $group = '' ) {
101
+ global $wp_object_cache;
 
102
 
103
+ return $wp_object_cache->delete( $key, $group );
104
  }
105
 
106
  /**
108
  *
109
  * @since 1.8
110
  */
111
+ function wp_cache_flush() {
112
+ global $wp_object_cache;
 
113
 
114
+ return $wp_object_cache->flush();
115
  }
116
 
117
  /**
119
  *
120
  * @since 1.8
121
  */
122
+ function wp_cache_add_global_groups( $groups ) {
123
+ global $wp_object_cache;
 
124
 
125
+ $wp_object_cache->add_global_groups( $groups );
126
  }
127
 
128
  /**
130
  *
131
  * @since 1.8
132
  */
133
+ function wp_cache_add_non_persistent_groups( $groups ) {
134
+ global $wp_object_cache;
 
135
 
136
+ $wp_object_cache->add_non_persistent_groups( $groups );
137
  }
138
 
139
  /**
149
  * @param int $blog_id Site ID.
150
  */
151
  function wp_cache_switch_to_blog( $blog_id ) {
152
+ global $wp_object_cache;
153
 
154
+ $wp_object_cache->switch_to_blog( $blog_id );
155
  }
156
 
157
  /**
159
  *
160
  * @since 1.8
161
  */
162
+ function wp_cache_close() {
163
+ return true;
 
164
  }
165
 
166
 
167
 
168
+ class WP_Object_Cache {
169
+ protected static $_instance;
 
170
 
171
+ private $_object_cache;
172
 
173
+ private $_cache = array();
174
+ private $_cache_404 = array();
175
 
176
+ private $cache_total = 0;
177
+ private $count_hit_incall = 0;
178
+ private $count_hit = 0;
179
+ private $count_miss_incall = 0;
180
+ private $count_miss = 0;
181
+ private $count_set = 0;
182
 
183
+ private $blog_prefix;
184
 
185
  /**
186
  * Init
188
  * @since 1.8
189
  * @access protected
190
  */
191
+ protected function __construct() {
192
+ $this->_object_cache = \LiteSpeed\Object_Cache::get_instance();
 
193
 
194
+ $this->multisite = is_multisite();
195
+ $this->blog_prefix = $this->multisite ? get_current_blog_id() . ':' : '';
196
 
197
  /**
198
  * Fix multiple instance using same oc issue
199
  * @since 1.8.2
200
  */
201
+ ! defined( 'LSOC_PREFIX' ) && define( 'LSOC_PREFIX', substr( md5( __FILE__ ), -5 ) );
202
  }
203
 
204
  /**
207
  * @since 1.8
208
  * @access public
209
  */
210
+ public function debug() {
 
211
  $log = ' [total] ' . $this->cache_total
212
  . ' [hit_incall] ' . $this->count_hit_incall
213
  . ' [hit] ' . $this->count_hit
214
  . ' [miss_incall] ' . $this->count_miss_incall
215
  . ' [miss] ' . $this->count_miss
216
+ . ' [set] ' . $this->count_set;
217
 
218
+ return $log;
219
  }
220
 
221
  /**
224
  * @since 1.8
225
  * @access public
226
  */
227
+ public function get( $key, $group = 'default', $force = false, &$found = null ) {
228
+ $final_key = $this->_key( $key, $group );
 
229
  // error_log('');
230
  // error_log("oc: get \t\t\t[key] " . $final_key . ( $force ? "\t\t\t [forced] " : '' ) );
231
+ $found = false;
232
+ $found_in_oc = false;
233
+ $cache_val = false;
234
  if ( array_key_exists( $final_key, $this->_cache ) && ! $force ) {
235
+ $found = true;
236
+ $cache_val = $this->_cache[ $final_key ];
237
+ $this->count_hit_incall ++;
238
  }
239
  elseif ( ! array_key_exists( $final_key, $this->_cache_404 ) && ! $this->_object_cache->is_non_persistent( $group ) ) {
240
+ $v = $this->_object_cache->get( $final_key );
241
 
242
  if ( $v !== null ) {
243
+ $v = @maybe_unserialize( $v );
244
  }
245
 
246
  // To be compatible with false val
247
  if ( is_array( $v ) && array_key_exists( 'data', $v ) ) {
248
+ $this->count_hit ++;
249
+ $found = true;
250
+ $found_in_oc = true;
251
+ $cache_val = $v[ 'data' ];
252
  }
253
  else { // Can't find key, cache it to 404
254
+ // error_log("oc: add404\t\t\t[key] " . $final_key );
255
+ $this->_cache_404[ $final_key ] = 1;
256
+ $this->count_miss ++;
257
  }
258
  }
259
  else {
260
+ $this->count_miss_incall ++;
261
  }
262
 
263
  if ( is_object( $cache_val ) ) {
264
+ $cache_val = clone $cache_val;
265
  }
266
 
267
  // If not found but has `Store Transients` cfg on, still need to follow WP's get_transient() logic
268
  if ( ! $found && $this->_object_cache->store_transients( $group ) ) {
269
+ $cache_val = $this->_transient_get( $key, $group );
270
  if ( $cache_val ) {
271
+ $found = true; // $found not used for now (v1.8.3)
272
  }
273
  }
274
 
275
  if ( $found_in_oc ) {
276
+ $this->_cache[ $final_key ] = $cache_val;
277
  }
278
 
279
+ $this->cache_total ++;
280
 
281
+ return $cache_val;
282
  }
283
 
284
  /**
287
  * @since 1.8
288
  * @access public
289
  */
290
+ public function set( $key, $data, $group = 'default', $expire = 0 ) {
291
+ $final_key = $this->_key( $key, $group );
 
292
 
293
  if ( is_object( $data ) ) {
294
+ $data = clone $data;
295
  }
296
+ // error_log("oc: set \t\t\t[key] " . $final_key );
297
+ $this->_cache[ $final_key ] = $data;
298
 
299
  if( array_key_exists( $final_key, $this->_cache_404 ) ) {
300
+ // error_log("oc: unset404\t\t\t[key] " . $final_key );
301
+ unset( $this->_cache_404[ $final_key ] );
302
  }
303
 
304
  if ( ! $this->_object_cache->is_non_persistent( $group ) ) {
305
+ $this->_object_cache->set( $final_key, serialize( array( 'data' => $data ) ), $expire );
306
+ $this->count_set ++;
307
  }
308
 
309
  if ( $this->_object_cache->store_transients( $group ) ) {
310
+ $this->_transient_set( $key, $data, $group, $expire );
311
  }
312
 
313
+ return true;
314
  }
315
 
316
  /**
319
  * @since 1.8
320
  * @access public
321
  */
322
+ public function add( $key, $data, $group = 'default', $expire = 0 ) {
 
323
  if ( wp_suspend_cache_addition() ) {
324
+ return false;
325
  }
326
 
327
+ $final_key = $this->_key( $key, $group );
328
 
329
  if ( array_key_exists( $final_key, $this->_cache ) ) {
330
+ return false;
331
  }
332
 
333
+ return $this->set( $key, $data, $group, $expire );
334
  }
335
 
336
  /**
339
  * @since 1.8
340
  * @access public
341
  */
342
+ public function replace( $key, $data, $group = 'default', $expire = 0 ) {
343
+ $final_key = $this->_key( $key, $group );
 
344
 
345
  if ( ! array_key_exists( $final_key, $this->_cache ) ) {
346
+ return false;
347
  }
348
 
349
+ return $this->set( $key, $data, $group, $expire );
350
  }
351
 
352
  /**
355
  * @since 1.8
356
  * @access public
357
  */
358
+ public function incr_desr( $key, $offset = 1, $group = 'default', $incr = true ) {
359
+ $cache_val = $this->get( $key, $group );
 
360
 
361
  if ( $cache_val === false ) {
362
+ return false;
363
  }
364
 
365
  if ( ! is_numeric( $cache_val ) ) {
366
+ $cache_val = 0;
367
  }
368
 
369
+ $offset = (int) $offset;
370
 
371
  if ( $incr ) {
372
+ $cache_val += $offset;
373
  }
374
  else {
375
+ $cache_val -= $offset;
376
  }
377
 
378
  if ( $cache_val < 0 ) {
379
+ $cache_val = 0;
380
  }
381
 
382
+ $this->set( $key, $cache_val, $group );
383
 
384
+ return $cache_val;
385
  }
386
 
387
  /**
390
  * @since 1.8
391
  * @access public
392
  */
393
+ public function delete( $key, $group = 'default' ) {
 
394
 
395
+ $final_key = $this->_key( $key, $group );
396
 
397
  if ( $this->_object_cache->store_transients( $group ) ) {
398
+ $this->_transient_del( $key, $group );
399
  }
400
 
401
  if ( array_key_exists( $final_key, $this->_cache ) ) {
402
+ unset( $this->_cache[ $final_key ] );
403
  }
404
+ // error_log("oc: delete \t\t\t[key] " . $final_key );
405
 
406
  if ( $this->_object_cache->is_non_persistent( $group ) ) {
407
+ return false;
408
  }
409
 
410
+ return $this->_object_cache->delete( $final_key );
411
  }
412
 
413
  /**
416
  * @since 1.8
417
  * @access public
418
  */
419
+ public function flush() {
420
+ $this->_cache = array();
421
+ $this->_cache_404 = array();
422
+ // error_log("oc: flush " );
 
423
 
424
+ $this->_object_cache->flush();
425
 
426
+ return true;
427
  }
428
 
429
  /**
432
  * @since 1.8
433
  * @access public
434
  */
435
+ public function add_global_groups( $groups ) {
436
+ $this->_object_cache->add_global_groups( $groups );
 
437
  }
438
 
439
  /**
442
  * @since 1.8
443
  * @access public
444
  */
445
+ public function add_non_persistent_groups( $groups ) {
446
+ $this->_object_cache->add_non_persistent_groups( $groups );
 
447
  }
448
 
449
  /**
452
  * @since 1.8
453
  * @access private
454
  */
455
+ private function _key( $key, $group = 'default' ) {
456
+ $prefix = $this->_object_cache->is_global( $group ) ? '' : $this->blog_prefix;
 
457
 
458
+ return LSOC_PREFIX . $prefix . $group . '.' . $key;
459
  }
460
 
461
  /**
468
  * @param int $blog_id Blog ID.
469
  */
470
  public function switch_to_blog( $blog_id ) {
471
+ $blog_id = (int) $blog_id;
472
+ $this->blog_prefix = $this->multisite ? $blog_id . ':' : '';
473
  }
474
 
475
  /**
479
  * @access private
480
  * @see `wp-includes/option.php` function `get_transient`/`set_site_transient`
481
  */
482
+ private function _transient_get( $transient, $group ) {
 
483
  if ( $group == 'transient' ) {
484
  /**** Ori WP func start ****/
485
  $transient_option = '_transient_' . $transient;
520
  /**** Ori WP func end ****/
521
  }
522
  else {
523
+ $value = false;
524
  }
525
 
526
+ return $value;
527
  }
528
 
529
  /**
533
  * @access private
534
  * @see `wp-includes/option.php` function `set_transient`/`set_site_transient`
535
  */
536
+ private function _transient_set( $transient, $value, $group, $expiration ) {
 
537
  if ( $group == 'transient' ) {
538
  /**** Ori WP func start ****/
539
  $transient_timeout = '_transient_timeout_' . $transient;
581
  /**** Ori WP func end ****/
582
  }
583
  else {
584
+ $result = null;
585
  }
586
 
587
+ return $result;
588
  }
589
 
590
  /**
594
  * @access private
595
  * @see `wp-includes/option.php` function `delete_transient`/`delete_site_transient`
596
  */
597
+ private function _transient_del( $transient, $group ) {
 
598
  if ( $group == 'transient' ) {
599
  /**** Ori WP func start ****/
600
  $option_timeout = '_transient_timeout_' . $transient;
621
  * @since 1.8
622
  * @access public
623
  */
624
+ public static function get_instance() {
 
625
  if ( ! isset( self::$_instance ) ) {
626
+ self::$_instance = new self();
627
  }
628
 
629
+ return self::$_instance;
630
  }
631
  }
src/optimize.cls.php CHANGED
@@ -1160,8 +1160,17 @@ class Optimize extends Base {
1160
  if ( in_array( $match[ 0 ], $html_list ) ) {
1161
  continue;
1162
  }
1163
- // todo @v2.0: allow defer even exclude from optm
1164
  if ( $excludes && $exclude = Utility::str_hit_array( $attrs[ 'src' ], $excludes ) ) {
 
 
 
 
 
 
 
 
 
1165
  Debug2::debug2( '[Optm] _parse_js bypassed exclude ' . $exclude );
1166
  continue;
1167
  }
1160
  if ( in_array( $match[ 0 ], $html_list ) ) {
1161
  continue;
1162
  }
1163
+
1164
  if ( $excludes && $exclude = Utility::str_hit_array( $attrs[ 'src' ], $excludes ) ) {
1165
+ // Maybe defer
1166
+ if ( $this->cfg_js_defer ) {
1167
+ $deferred = $this->_js_defer( array( $match[ 0 ] ) );
1168
+ $deferred = $deferred[ 0 ];
1169
+ if ( $deferred != $match[ 0 ] ) {
1170
+ $this->content = str_replace( $match[ 0 ], $deferred, $this->content );
1171
+ }
1172
+ }
1173
+
1174
  Debug2::debug2( '[Optm] _parse_js bypassed exclude ' . $exclude );
1175
  continue;
1176
  }
src/optimizer.cls.php CHANGED
@@ -103,7 +103,16 @@ class Optimizer extends Instance {
103
  // Parse real file path
104
  $real_files = array();
105
  foreach ( $src_list as $src_info ) {
106
- $real_file = Utility::is_internal_file( ! empty( $src_info[ 'src' ] ) ? $src_info[ 'src' ] : $src_info );
 
 
 
 
 
 
 
 
 
107
 
108
  if ( ! $real_file ) {
109
  continue;
@@ -111,12 +120,12 @@ class Optimizer extends Instance {
111
 
112
  if ( ! empty( $src_info[ 'media' ] ) ) {
113
  $real_files[] = array(
114
- 'src' => $real_file[ 0 ],
115
  'media' => $src_info[ 'media' ],
116
  );
117
  }
118
  else {
119
- $real_files[] = $real_file[ 0 ];
120
  }
121
  }
122
 
@@ -182,7 +191,16 @@ class Optimizer extends Instance {
182
  $real_path = $path_info;
183
  }
184
  Debug2::debug2( '[Optmer] [real_path] ' . $real_path );
185
- $data = File::read( $real_path );
 
 
 
 
 
 
 
 
 
186
 
187
  // Font optimize
188
  if ( $this->_conf_css_font_display ) {
@@ -195,7 +213,7 @@ class Optimizer extends Instance {
195
  $data = self::minify_css( $data );
196
  }
197
 
198
- $data = Lib\CSS_MIN\UriRewriter::rewrite( $data, dirname( $real_path ) );
199
 
200
  if ( $media ) {
201
  $data = '@media ' . $media . '{' . $data . "\n}";
@@ -216,7 +234,13 @@ class Optimizer extends Instance {
216
  private function _serve_js( $files, $concat_only ) {
217
  $con = array();
218
  foreach ( $files as $real_path ) {
219
- $data = File::read( $real_path );
 
 
 
 
 
 
220
 
221
  if ( ! $concat_only && ! $this->_is_min( $real_path ) ) {
222
  $data = self::minify_js( $data );
103
  // Parse real file path
104
  $real_files = array();
105
  foreach ( $src_list as $src_info ) {
106
+ $src = ! empty( $src_info[ 'src' ] ) ? $src_info[ 'src' ] : $src_info;
107
+ $postfix = pathinfo( parse_url( $src, PHP_URL_PATH ), PATHINFO_EXTENSION );
108
+ if ( $postfix != $file_type ) {
109
+ Debug2::debug2( '[Optmer] Not static file, will read as remote [src] ' . $src );
110
+ $real_file = $src;
111
+ }
112
+ else {
113
+ $real_file = Utility::is_internal_file( $src );
114
+ $real_file = ! empty( $real_file[ 0 ] ) ? $real_file[ 0 ] : false;
115
+ }
116
 
117
  if ( ! $real_file ) {
118
  continue;
120
 
121
  if ( ! empty( $src_info[ 'media' ] ) ) {
122
  $real_files[] = array(
123
+ 'src' => $real_file,
124
  'media' => $src_info[ 'media' ],
125
  );
126
  }
127
  else {
128
+ $real_files[] = $real_file;
129
  }
130
  }
131
 
191
  $real_path = $path_info;
192
  }
193
  Debug2::debug2( '[Optmer] [real_path] ' . $real_path );
194
+
195
+ // Check if its remote or local path
196
+ if ( strpos( $real_path, 'http' ) === 0 ) {
197
+ $data = wp_remote_retrieve_body( wp_remote_get( $real_path ) );
198
+ $dirname = dirname( parse_url( $real_path, PHP_URL_PATH ) );
199
+ }
200
+ else {
201
+ $data = File::read( $real_path );
202
+ $dirname = dirname( $real_path );
203
+ }
204
 
205
  // Font optimize
206
  if ( $this->_conf_css_font_display ) {
213
  $data = self::minify_css( $data );
214
  }
215
 
216
+ $data = Lib\CSS_MIN\UriRewriter::rewrite( $data, $dirname );
217
 
218
  if ( $media ) {
219
  $data = '@media ' . $media . '{' . $data . "\n}";
234
  private function _serve_js( $files, $concat_only ) {
235
  $con = array();
236
  foreach ( $files as $real_path ) {
237
+ // Check if its remote or local path
238
+ if ( strpos( $real_path, 'http' ) === 0 ) {
239
+ $data = wp_remote_retrieve_body( wp_remote_get( $real_path ) );
240
+ }
241
+ else {
242
+ $data = File::read( $real_path );
243
+ }
244
 
245
  if ( ! $concat_only && ! $this->_is_min( $real_path ) ) {
246
  $data = self::minify_js( $data );
src/tag.cls.php CHANGED
@@ -43,10 +43,9 @@ class Tag extends Instance {
43
  *
44
  * @since 2.2.3
45
  */
46
- protected function __construct()
47
- {
48
  // register recent posts widget tag before theme renders it to make it work
49
- add_filter( 'widget_posts_args', array( $this, 'add_widget_recent_posts' ) ) ;
50
 
51
  }
52
 
@@ -59,33 +58,32 @@ class Tag extends Instance {
59
  * @since 1.0.0
60
  * @access public
61
  */
62
- public static function check_login_cacheable()
63
- {
64
  if ( ! Conf::val( Base::O_CACHE_PAGE_LOGIN ) ) {
65
- return ;
66
  }
67
  if ( Control::isset_notcacheable() ) {
68
- return ;
69
  }
70
 
71
  if ( ! empty( $_GET ) ) {
72
- Control::set_nocache( 'has GET request' ) ;
73
- return ;
74
  }
75
 
76
- Control::set_cacheable() ;
77
 
78
- self::add( self::TYPE_LOGIN ) ;
79
 
80
  // we need to send lsc-cookie manually to make it be sent to all other users when is cacheable
81
- $list = headers_list() ;
82
  if ( empty( $list ) ) {
83
- return ;
84
  }
85
  foreach ( $list as $hdr ) {
86
  if ( strncasecmp( $hdr, 'set-cookie:', 11 ) == 0 ) {
87
- $cookie = substr( $hdr, 12 ) ;
88
- @header( 'lsc-cookie: ' . $cookie, false ) ;
89
  }
90
  }
91
  }
@@ -98,10 +96,9 @@ class Tag extends Instance {
98
  * @access public
99
  * @param array $params [wordpress params for widget_posts_args]
100
  */
101
- public function add_widget_recent_posts( $params )
102
- {
103
- self::add( self::TYPE_PAGES_WITH_RECENT_POSTS ) ;
104
- return $params ;
105
  }
106
 
107
  /**
@@ -111,13 +108,12 @@ class Tag extends Instance {
111
  * @access public
112
  * @param mixed $tags A string or array of cache tags to add to the current list.
113
  */
114
- public static function add( $tags )
115
- {
116
  if ( ! is_array( $tags ) ) {
117
- $tags = array( $tags ) ;
118
  }
119
 
120
- self::$_tags = array_merge( self::$_tags, $tags ) ;
121
  }
122
 
123
  /**
@@ -126,8 +122,7 @@ class Tag extends Instance {
126
  * @since 3.0
127
  * @access public
128
  */
129
- public static function add_post( $pid )
130
- {
131
  self::add( self::TYPE_POST . $pid );
132
  }
133
 
@@ -137,8 +132,7 @@ class Tag extends Instance {
137
  * @since 3.0
138
  * @access public
139
  */
140
- public static function add_widget( $id )
141
- {
142
  self::add( self::TYPE_WIDGET . $id );
143
  }
144
 
@@ -148,8 +142,7 @@ class Tag extends Instance {
148
  * @since 3.0
149
  * @access public
150
  */
151
- public static function add_private_esi( $tag )
152
- {
153
  self::add_private( self::TYPE_ESI . $tag );
154
  }
155
 
@@ -160,13 +153,12 @@ class Tag extends Instance {
160
  * @access public
161
  * @param mixed $tags A string or array of cache tags to add to the current list.
162
  */
163
- public static function add_private( $tags )
164
- {
165
  if ( ! is_array( $tags ) ) {
166
- $tags = array( $tags ) ;
167
  }
168
 
169
- self::$_tags_priv = array_merge( self::$_tags_priv, $tags ) ;
170
  }
171
 
172
  /**
@@ -175,9 +167,8 @@ class Tag extends Instance {
175
  * @since 1.1.3
176
  * @access public
177
  */
178
- public static function output_tags()
179
- {
180
- return self::$_tags ;
181
  }
182
 
183
  /**
@@ -189,20 +180,19 @@ class Tag extends Instance {
189
  * @param boolean $ori Return the original url or not
190
  * @return bool|string False on input error, hash otherwise.
191
  */
192
- public static function get_uri_tag( $uri, $ori = false )
193
- {
194
- $no_qs = strtok( $uri, '?' ) ;
195
  if ( empty( $no_qs ) ) {
196
- return false ;
197
  }
198
- $slashed = trailingslashit( $no_qs ) ;
199
 
200
  // If only needs uri tag
201
  if ( $ori ) {
202
- return $slashed ;
203
  }
204
- // return self::TYPE_URL . ( $slashed ) ;
205
- return self::TYPE_URL . md5( $slashed ) ;
206
  }
207
 
208
  /**
@@ -212,9 +202,8 @@ class Tag extends Instance {
212
  * @access public
213
  * @param boolean $ori Return the original url or not
214
  */
215
- public static function build_uri_tag( $ori = false )
216
- {
217
- return self::get_uri_tag( urldecode( $_SERVER['REQUEST_URI'] ), $ori ) ;
218
  }
219
 
220
  /**
@@ -227,88 +216,87 @@ class Tag extends Instance {
227
  * @access private
228
  * @return array The list of cache tags to set.
229
  */
230
- private static function _build_type_tags()
231
- {
232
- $tags = array() ;
233
 
234
- $tags[] = Utility::page_type() ;
235
 
236
- $tags[] = self::build_uri_tag() ;
237
 
238
  if ( is_front_page() ) {
239
- $tags[] = self::TYPE_FRONTPAGE ;
240
  }
241
  elseif ( is_home() ) {
242
- $tags[] = self::TYPE_HOME ;
243
  }
244
 
245
- $queried_obj_id = get_queried_object_id() ;
246
  if ( is_archive() ) {
247
  //An Archive is a Category, Tag, Author, Date, Custom Post Type or Custom Taxonomy based pages.
248
  if ( is_category() || is_tag() || is_tax() ) {
249
- $tags[] = self::TYPE_ARCHIVE_TERM . $queried_obj_id ;
250
  }
251
  elseif ( is_post_type_archive() ) {
252
- global $wp_query ;
253
- $post_type = $wp_query->get( 'post_type' ) ;
254
- $tags[] = self::TYPE_ARCHIVE_POSTTYPE . $post_type ;
255
  }
256
  elseif ( is_author() ) {
257
- $tags[] = self::TYPE_AUTHOR . $queried_obj_id ;
258
  }
259
  elseif ( is_date() ) {
260
- global $post ;
261
- $date = $post->post_date ;
262
- $date = strtotime( $date ) ;
263
  if ( is_day() ) {
264
- $tags[] = self::TYPE_ARCHIVE_DATE . date( 'Ymd', $date ) ;
265
  }
266
  elseif ( is_month() ) {
267
- $tags[] = self::TYPE_ARCHIVE_DATE . date( 'Ym', $date ) ;
268
  }
269
  elseif ( is_year() ) {
270
- $tags[] = self::TYPE_ARCHIVE_DATE . date( 'Y', $date ) ;
271
  }
272
  }
273
  }
274
  elseif ( is_singular() ) {
275
  //$this->is_singular = $this->is_single || $this->is_page || $this->is_attachment;
276
- $tags[] = self::TYPE_POST . $queried_obj_id ;
277
 
278
  if ( is_page() ) {
279
- $tags[] = self::TYPE_PAGES ;
280
  }
281
  }
282
  elseif ( is_feed() ) {
283
- $tags[] = self::TYPE_FEED ;
284
  }
285
 
286
  // Check REST API
287
  if ( REST::get_instance()->is_rest() ) {
288
- $tags[] = self::TYPE_REST ;
289
 
290
- $path = ! empty( $_SERVER[ 'SCRIPT_URL' ] ) ? $_SERVER[ 'SCRIPT_URL' ] : false ;
291
  if ( $path ) {
292
  // posts collections tag
293
  if ( substr( $path, -6 ) == '/posts' ) {
294
- $tags[] = self::TYPE_LIST ;// Not used for purge yet
295
  }
296
 
297
  // single post tag
298
  global $post;
299
  if ( ! empty( $post->ID ) && substr( $path, - strlen( $post->ID ) - 1 ) === '/' . $post->ID ) {
300
- $tags[] = self::TYPE_POST . $post->ID ;
301
  }
302
 
303
  // pages collections & single page tag
304
  if ( stripos( $path, '/pages' ) !== false ) {
305
- $tags[] = self::TYPE_PAGES ;
306
  }
307
  }
308
 
309
  }
310
 
311
- return $tags ;
312
  }
313
 
314
  /**
@@ -317,19 +305,18 @@ class Tag extends Instance {
317
  * @access private
318
  * @since 1.1.3
319
  */
320
- private static function _finalize()
321
- {
322
  // run 3rdparty hooks to tag
323
  do_action( 'litespeed_tag_finalize' );
324
  // generate wp tags
325
  if ( ! defined( 'LSCACHE_IS_ESI' ) ) {
326
- $type_tags = self::_build_type_tags() ;
327
- self::$_tags = array_merge( self::$_tags, $type_tags ) ;
328
  }
329
  // append blog main tag
330
- self::$_tags[] = '' ;
331
  // removed duplicates
332
- self::$_tags = array_unique( self::$_tags ) ;
333
  }
334
 
335
  /**
@@ -340,32 +327,31 @@ class Tag extends Instance {
340
  * @access public
341
  * @return string empty string if empty, otherwise the cache tags header.
342
  */
343
- public static function output()
344
- {
345
- self::_finalize() ;
346
 
347
- $prefix_tags = array() ;
348
  /**
349
  * Only append blog_id when is multisite
350
  * @since 2.9.3
351
  */
352
- $prefix = LSWCP_TAG_PREFIX . ( is_multisite() ? get_current_blog_id() : '' ) . '_' ;
353
 
354
  // If is_private and has private tags, append them first, then specify prefix to `public` for public tags
355
  if ( Control::is_private() ) {
356
  foreach ( self::$_tags_priv as $priv_tag ) {
357
- $prefix_tags[] = $prefix . $priv_tag ;
358
  }
359
- $prefix = 'public:' . $prefix ;
360
  }
361
 
362
  foreach ( self::$_tags as $tag ) {
363
- $prefix_tags[] = $prefix . $tag ;
364
  }
365
 
366
- $hdr = self::X_HEADER . ': ' . implode( ',', $prefix_tags ) ;
367
 
368
- return $hdr ;
369
  }
370
 
371
  }
43
  *
44
  * @since 2.2.3
45
  */
46
+ protected function __construct() {
 
47
  // register recent posts widget tag before theme renders it to make it work
48
+ add_filter( 'widget_posts_args', array( $this, 'add_widget_recent_posts' ) );
49
 
50
  }
51
 
58
  * @since 1.0.0
59
  * @access public
60
  */
61
+ public static function check_login_cacheable() {
 
62
  if ( ! Conf::val( Base::O_CACHE_PAGE_LOGIN ) ) {
63
+ return;
64
  }
65
  if ( Control::isset_notcacheable() ) {
66
+ return;
67
  }
68
 
69
  if ( ! empty( $_GET ) ) {
70
+ Control::set_nocache( 'has GET request' );
71
+ return;
72
  }
73
 
74
+ Control::set_cacheable();
75
 
76
+ self::add( self::TYPE_LOGIN );
77
 
78
  // we need to send lsc-cookie manually to make it be sent to all other users when is cacheable
79
+ $list = headers_list();
80
  if ( empty( $list ) ) {
81
+ return;
82
  }
83
  foreach ( $list as $hdr ) {
84
  if ( strncasecmp( $hdr, 'set-cookie:', 11 ) == 0 ) {
85
+ $cookie = substr( $hdr, 12 );
86
+ @header( 'lsc-cookie: ' . $cookie, false );
87
  }
88
  }
89
  }
96
  * @access public
97
  * @param array $params [wordpress params for widget_posts_args]
98
  */
99
+ public function add_widget_recent_posts( $params ) {
100
+ self::add( self::TYPE_PAGES_WITH_RECENT_POSTS );
101
+ return $params;
 
102
  }
103
 
104
  /**
108
  * @access public
109
  * @param mixed $tags A string or array of cache tags to add to the current list.
110
  */
111
+ public static function add( $tags ) {
 
112
  if ( ! is_array( $tags ) ) {
113
+ $tags = array( $tags );
114
  }
115
 
116
+ self::$_tags = array_merge( self::$_tags, $tags );
117
  }
118
 
119
  /**
122
  * @since 3.0
123
  * @access public
124
  */
125
+ public static function add_post( $pid ) {
 
126
  self::add( self::TYPE_POST . $pid );
127
  }
128
 
132
  * @since 3.0
133
  * @access public
134
  */
135
+ public static function add_widget( $id ) {
 
136
  self::add( self::TYPE_WIDGET . $id );
137
  }
138
 
142
  * @since 3.0
143
  * @access public
144
  */
145
+ public static function add_private_esi( $tag ) {
 
146
  self::add_private( self::TYPE_ESI . $tag );
147
  }
148
 
153
  * @access public
154
  * @param mixed $tags A string or array of cache tags to add to the current list.
155
  */
156
+ public static function add_private( $tags ) {
 
157
  if ( ! is_array( $tags ) ) {
158
+ $tags = array( $tags );
159
  }
160
 
161
+ self::$_tags_priv = array_merge( self::$_tags_priv, $tags );
162
  }
163
 
164
  /**
167
  * @since 1.1.3
168
  * @access public
169
  */
170
+ public static function output_tags() {
171
+ return self::$_tags;
 
172
  }
173
 
174
  /**
180
  * @param boolean $ori Return the original url or not
181
  * @return bool|string False on input error, hash otherwise.
182
  */
183
+ public static function get_uri_tag( $uri, $ori = false ) {
184
+ $no_qs = strtok( $uri, '?' );
 
185
  if ( empty( $no_qs ) ) {
186
+ return false;
187
  }
188
+ $slashed = trailingslashit( $no_qs );
189
 
190
  // If only needs uri tag
191
  if ( $ori ) {
192
+ return $slashed;
193
  }
194
+ // return self::TYPE_URL . ( $slashed );
195
+ return self::TYPE_URL . md5( $slashed );
196
  }
197
 
198
  /**
202
  * @access public
203
  * @param boolean $ori Return the original url or not
204
  */
205
+ public static function build_uri_tag( $ori = false ) {
206
+ return self::get_uri_tag( urldecode( $_SERVER['REQUEST_URI'] ), $ori );
 
207
  }
208
 
209
  /**
216
  * @access private
217
  * @return array The list of cache tags to set.
218
  */
219
+ private static function _build_type_tags() {
220
+ $tags = array();
 
221
 
222
+ $tags[] = Utility::page_type();
223
 
224
+ $tags[] = self::build_uri_tag();
225
 
226
  if ( is_front_page() ) {
227
+ $tags[] = self::TYPE_FRONTPAGE;
228
  }
229
  elseif ( is_home() ) {
230
+ $tags[] = self::TYPE_HOME;
231
  }
232
 
233
+ $queried_obj_id = get_queried_object_id();
234
  if ( is_archive() ) {
235
  //An Archive is a Category, Tag, Author, Date, Custom Post Type or Custom Taxonomy based pages.
236
  if ( is_category() || is_tag() || is_tax() ) {
237
+ $tags[] = self::TYPE_ARCHIVE_TERM . $queried_obj_id;
238
  }
239
  elseif ( is_post_type_archive() ) {
240
+ global $wp_query;
241
+ $post_type = $wp_query->get( 'post_type' );
242
+ $tags[] = self::TYPE_ARCHIVE_POSTTYPE . $post_type;
243
  }
244
  elseif ( is_author() ) {
245
+ $tags[] = self::TYPE_AUTHOR . $queried_obj_id;
246
  }
247
  elseif ( is_date() ) {
248
+ global $post;
249
+ $date = $post->post_date;
250
+ $date = strtotime( $date );
251
  if ( is_day() ) {
252
+ $tags[] = self::TYPE_ARCHIVE_DATE . date( 'Ymd', $date );
253
  }
254
  elseif ( is_month() ) {
255
+ $tags[] = self::TYPE_ARCHIVE_DATE . date( 'Ym', $date );
256
  }
257
  elseif ( is_year() ) {
258
+ $tags[] = self::TYPE_ARCHIVE_DATE . date( 'Y', $date );
259
  }
260
  }
261
  }
262
  elseif ( is_singular() ) {
263
  //$this->is_singular = $this->is_single || $this->is_page || $this->is_attachment;
264
+ $tags[] = self::TYPE_POST . $queried_obj_id;
265
 
266
  if ( is_page() ) {
267
+ $tags[] = self::TYPE_PAGES;
268
  }
269
  }
270
  elseif ( is_feed() ) {
271
+ $tags[] = self::TYPE_FEED;
272
  }
273
 
274
  // Check REST API
275
  if ( REST::get_instance()->is_rest() ) {
276
+ $tags[] = self::TYPE_REST;
277
 
278
+ $path = ! empty( $_SERVER[ 'SCRIPT_URL' ] ) ? $_SERVER[ 'SCRIPT_URL' ] : false;
279
  if ( $path ) {
280
  // posts collections tag
281
  if ( substr( $path, -6 ) == '/posts' ) {
282
+ $tags[] = self::TYPE_LIST;// Not used for purge yet
283
  }
284
 
285
  // single post tag
286
  global $post;
287
  if ( ! empty( $post->ID ) && substr( $path, - strlen( $post->ID ) - 1 ) === '/' . $post->ID ) {
288
+ $tags[] = self::TYPE_POST . $post->ID;
289
  }
290
 
291
  // pages collections & single page tag
292
  if ( stripos( $path, '/pages' ) !== false ) {
293
+ $tags[] = self::TYPE_PAGES;
294
  }
295
  }
296
 
297
  }
298
 
299
+ return $tags;
300
  }
301
 
302
  /**
305
  * @access private
306
  * @since 1.1.3
307
  */
308
+ private static function _finalize() {
 
309
  // run 3rdparty hooks to tag
310
  do_action( 'litespeed_tag_finalize' );
311
  // generate wp tags
312
  if ( ! defined( 'LSCACHE_IS_ESI' ) ) {
313
+ $type_tags = self::_build_type_tags();
314
+ self::$_tags = array_merge( self::$_tags, $type_tags );
315
  }
316
  // append blog main tag
317
+ self::$_tags[] = '';
318
  // removed duplicates
319
+ self::$_tags = array_unique( self::$_tags );
320
  }
321
 
322
  /**
327
  * @access public
328
  * @return string empty string if empty, otherwise the cache tags header.
329
  */
330
+ public static function output() {
331
+ self::_finalize();
 
332
 
333
+ $prefix_tags = array();
334
  /**
335
  * Only append blog_id when is multisite
336
  * @since 2.9.3
337
  */
338
+ $prefix = LSWCP_TAG_PREFIX . ( is_multisite() ? get_current_blog_id() : '' ) . '_';
339
 
340
  // If is_private and has private tags, append them first, then specify prefix to `public` for public tags
341
  if ( Control::is_private() ) {
342
  foreach ( self::$_tags_priv as $priv_tag ) {
343
+ $prefix_tags[] = $prefix . $priv_tag;
344
  }
345
+ $prefix = 'public:' . $prefix;
346
  }
347
 
348
  foreach ( self::$_tags as $tag ) {
349
+ $prefix_tags[] = $prefix . $tag;
350
  }
351
 
352
+ $hdr = self::X_HEADER . ': ' . implode( ',', $prefix_tags );
353
 
354
+ return $hdr;
355
  }
356
 
357
  }
src/tool.cls.php CHANGED
@@ -7,20 +7,19 @@
7
  * @subpackage LiteSpeed/inc
8
  * @author LiteSpeed Technologies <info@litespeedtech.com>
9
  */
10
- namespace LiteSpeed ;
11
 
12
- defined( 'WPINC' ) || exit ;
13
 
14
- class Tool extends Instance
15
- {
16
- protected static $_instance ;
17
 
18
- private $_conf_heartbeat_front ;
19
- private $_conf_heartbeat_front_ttl ;
20
- private $_conf_heartbeat_back ;
21
- private $_conf_heartbeat_back_ttl ;
22
- private $_conf_heartbeat_editor ;
23
- private $_conf_heartbeat_editor_ttl ;
24
 
25
  /**
26
  * Init
@@ -28,14 +27,13 @@ class Tool extends Instance
28
  * @since 3.0
29
  * @access protected
30
  */
31
- protected function __construct()
32
- {
33
- $this->_conf_heartbeat_front = Conf::val( Base::O_MISC_HEARTBEAT_FRONT ) ;
34
- $this->_conf_heartbeat_front_ttl = Conf::val( Base::O_MISC_HEARTBEAT_FRONT_TTL ) ;
35
- $this->_conf_heartbeat_back = Conf::val( Base::O_MISC_HEARTBEAT_BACK ) ;
36
- $this->_conf_heartbeat_back_ttl = Conf::val( Base::O_MISC_HEARTBEAT_BACK_TTL ) ;
37
- $this->_conf_heartbeat_editor = Conf::val( Base::O_MISC_HEARTBEAT_EDITOR ) ;
38
- $this->_conf_heartbeat_editor_ttl = Conf::val( Base::O_MISC_HEARTBEAT_EDITOR_TTL ) ;
39
  }
40
 
41
  /**
@@ -44,21 +42,20 @@ class Tool extends Instance
44
  * @since 3.0
45
  * @access public
46
  */
47
- public function check_ip()
48
- {
49
- Debug2::debug( '[Tool] ✅ check_ip' ) ;
50
 
51
- $response = wp_remote_get( 'https://www.doapi.us/ip' ) ;
52
 
53
  if ( is_wp_error( $response ) ) {
54
- return new \WP_Error( 'remote_get_fail', 'Failed to fetch from https://www.doapi.us/ip', array( 'status' => 404 ) ) ;
55
  }
56
 
57
- $data = $response[ 'body' ] ;
58
 
59
- Debug2::debug( '[Tool] result [ip] ' . $data ) ;
60
 
61
- return $data ;
62
  }
63
 
64
  /**
@@ -69,13 +66,12 @@ class Tool extends Instance
69
  * @since 3.0
70
  * @access public
71
  */
72
- public static function heartbeat()
73
- {
74
- $instance = self::get_instance() ;
75
 
76
- add_action( 'wp_enqueue_scripts', array( $instance, 'heartbeat_frontend' ) ) ;
77
- add_action( 'admin_enqueue_scripts', array( $instance, 'heartbeat_backend' ) ) ;
78
- add_filter( 'heartbeat_settings', array( $instance, 'heartbeat_settings' ) ) ;
79
  }
80
 
81
  /**
@@ -84,15 +80,14 @@ class Tool extends Instance
84
  * @since 3.0
85
  * @access public
86
  */
87
- public function heartbeat_frontend()
88
- {
89
  if ( ! $this->_conf_heartbeat_front ) {
90
- return ;
91
  }
92
 
93
  if ( ! $this->_conf_heartbeat_front_ttl ) {
94
- wp_deregister_script( 'heartbeat' ) ;
95
- Debug2::debug( '[Tool] Deregistered frontend heartbeat' ) ;
96
  }
97
  }
98
 
@@ -102,26 +97,25 @@ class Tool extends Instance
102
  * @since 3.0
103
  * @access public
104
  */
105
- public function heartbeat_backend()
106
- {
107
  if ( $this->_is_editor() ) {
108
  if ( ! $this->_conf_heartbeat_editor ) {
109
- return ;
110
  }
111
 
112
  if ( ! $this->_conf_heartbeat_editor_ttl ) {
113
- wp_deregister_script( 'heartbeat' ) ;
114
- Debug2::debug( '[Tool] Deregistered editor heartbeat' ) ;
115
  }
116
  }
117
  else {
118
  if ( ! $this->_conf_heartbeat_back ) {
119
- return ;
120
  }
121
 
122
  if ( ! $this->_conf_heartbeat_back_ttl ) {
123
- wp_deregister_script( 'heartbeat' ) ;
124
- Debug2::debug( '[Tool] Deregistered backend heartbeat' ) ;
125
  }
126
  }
127
 
@@ -133,28 +127,27 @@ class Tool extends Instance
133
  * @since 3.0
134
  * @access public
135
  */
136
- public function heartbeat_settings( $settings )
137
- {
138
  // Check editor first to make frontend editor valid too
139
  if ( $this->_is_editor() ) {
140
  if ( $this->_conf_heartbeat_editor ) {
141
- $settings[ 'interval' ] = $this->_conf_heartbeat_editor_ttl ;
142
- Debug2::debug( '[Tool] Heartbeat interval set to ' . $this->_conf_heartbeat_editor_ttl ) ;
143
  }
144
  }
145
  elseif ( ! is_admin() ) {
146
  if ( $this->_conf_heartbeat_front ) {
147
- $settings[ 'interval' ] = $this->_conf_heartbeat_front_ttl ;
148
- Debug2::debug( '[Tool] Heartbeat interval set to ' . $this->_conf_heartbeat_front_ttl ) ;
149
  }
150
  }
151
  else {
152
  if ( $this->_conf_heartbeat_back ) {
153
- $settings[ 'interval' ] = $this->_conf_heartbeat_back_ttl ;
154
- Debug2::debug( '[Tool] Heartbeat interval set to ' . $this->_conf_heartbeat_back_ttl ) ;
155
  }
156
  }
157
- return $settings ;
158
  }
159
 
160
  /**
@@ -163,11 +156,10 @@ class Tool extends Instance
163
  * @since 3.0
164
  * @access public
165
  */
166
- private function _is_editor()
167
- {
168
- $res = is_admin() && Utility::str_hit_array( $_SERVER[ 'REQUEST_URI' ], array( 'post.php', 'post-new.php' ) ) ;
169
 
170
- return apply_filters( 'litespeed_is_editor', $res ) ;
171
  }
172
 
173
  }
7
  * @subpackage LiteSpeed/inc
8
  * @author LiteSpeed Technologies <info@litespeedtech.com>
9
  */
10
+ namespace LiteSpeed;
11
 
12
+ defined( 'WPINC' ) || exit;
13
 
14
+ class Tool extends Instance {
15
+ protected static $_instance;
 
16
 
17
+ private $_conf_heartbeat_front;
18
+ private $_conf_heartbeat_front_ttl;
19
+ private $_conf_heartbeat_back;
20
+ private $_conf_heartbeat_back_ttl;
21
+ private $_conf_heartbeat_editor;
22
+ private $_conf_heartbeat_editor_ttl;
23
 
24
  /**
25
  * Init
27
  * @since 3.0
28
  * @access protected
29
  */
30
+ protected function __construct() {
31
+ $this->_conf_heartbeat_front = Conf::val( Base::O_MISC_HEARTBEAT_FRONT );
32
+ $this->_conf_heartbeat_front_ttl = Conf::val( Base::O_MISC_HEARTBEAT_FRONT_TTL );
33
+ $this->_conf_heartbeat_back = Conf::val( Base::O_MISC_HEARTBEAT_BACK );
34
+ $this->_conf_heartbeat_back_ttl = Conf::val( Base::O_MISC_HEARTBEAT_BACK_TTL );
35
+ $this->_conf_heartbeat_editor = Conf::val( Base::O_MISC_HEARTBEAT_EDITOR );
36
+ $this->_conf_heartbeat_editor_ttl = Conf::val( Base::O_MISC_HEARTBEAT_EDITOR_TTL );
 
37
  }
38
 
39
  /**
42
  * @since 3.0
43
  * @access public
44
  */
45
+ public function check_ip() {
46
+ Debug2::debug( '[Tool] ✅ check_ip' );
 
47
 
48
+ $response = wp_remote_get( 'https://www.doapi.us/ip' );
49
 
50
  if ( is_wp_error( $response ) ) {
51
+ return new \WP_Error( 'remote_get_fail', 'Failed to fetch from https://www.doapi.us/ip', array( 'status' => 404 ) );
52
  }
53
 
54
+ $data = $response[ 'body' ];
55
 
56
+ Debug2::debug( '[Tool] result [ip] ' . $data );
57
 
58
+ return $data;
59
  }
60
 
61
  /**
66
  * @since 3.0
67
  * @access public
68
  */
69
+ public static function heartbeat() {
70
+ $instance = self::get_instance();
 
71
 
72
+ add_action( 'wp_enqueue_scripts', array( $instance, 'heartbeat_frontend' ) );
73
+ add_action( 'admin_enqueue_scripts', array( $instance, 'heartbeat_backend' ) );
74
+ add_filter( 'heartbeat_settings', array( $instance, 'heartbeat_settings' ) );
75
  }
76
 
77
  /**
80
  * @since 3.0
81
  * @access public
82
  */
83
+ public function heartbeat_frontend() {
 
84
  if ( ! $this->_conf_heartbeat_front ) {
85
+ return;
86
  }
87
 
88
  if ( ! $this->_conf_heartbeat_front_ttl ) {
89
+ wp_deregister_script( 'heartbeat' );
90
+ Debug2::debug( '[Tool] Deregistered frontend heartbeat' );
91
  }
92
  }
93
 
97
  * @since 3.0
98
  * @access public
99
  */
100
+ public function heartbeat_backend() {
 
101
  if ( $this->_is_editor() ) {
102
  if ( ! $this->_conf_heartbeat_editor ) {
103
+ return;
104
  }
105
 
106
  if ( ! $this->_conf_heartbeat_editor_ttl ) {
107
+ wp_deregister_script( 'heartbeat' );
108
+ Debug2::debug( '[Tool] Deregistered editor heartbeat' );
109
  }
110
  }
111
  else {
112
  if ( ! $this->_conf_heartbeat_back ) {
113
+ return;
114
  }
115
 
116
  if ( ! $this->_conf_heartbeat_back_ttl ) {
117
+ wp_deregister_script( 'heartbeat' );
118
+ Debug2::debug( '[Tool] Deregistered backend heartbeat' );
119
  }
120
  }
121
 
127
  * @since 3.0
128
  * @access public
129
  */
130
+ public function heartbeat_settings( $settings ) {
 
131
  // Check editor first to make frontend editor valid too
132
  if ( $this->_is_editor() ) {
133
  if ( $this->_conf_heartbeat_editor ) {
134
+ $settings[ 'interval' ] = $this->_conf_heartbeat_editor_ttl;
135
+ Debug2::debug( '[Tool] Heartbeat interval set to ' . $this->_conf_heartbeat_editor_ttl );
136
  }
137
  }
138
  elseif ( ! is_admin() ) {
139
  if ( $this->_conf_heartbeat_front ) {
140
+ $settings[ 'interval' ] = $this->_conf_heartbeat_front_ttl;
141
+ Debug2::debug( '[Tool] Heartbeat interval set to ' . $this->_conf_heartbeat_front_ttl );
142
  }
143
  }
144
  else {
145
  if ( $this->_conf_heartbeat_back ) {
146
+ $settings[ 'interval' ] = $this->_conf_heartbeat_back_ttl;
147
+ Debug2::debug( '[Tool] Heartbeat interval set to ' . $this->_conf_heartbeat_back_ttl );
148
  }
149
  }
150
+ return $settings;
151
  }
152
 
153
  /**
156
  * @since 3.0
157
  * @access public
158
  */
159
+ private function _is_editor() {
160
+ $res = is_admin() && Utility::str_hit_array( $_SERVER[ 'REQUEST_URI' ], array( 'post.php', 'post-new.php' ) );
 
161
 
162
+ return apply_filters( 'litespeed_is_editor', $res );
163
  }
164
 
165
  }
src/vary.cls.php CHANGED
@@ -128,48 +128,47 @@ class Vary extends Instance {
128
  * @param array $comments The current comments to output
129
  * @return array The comments to output.
130
  */
131
- public function check_commenter( $comments )
132
- {
133
  /**
134
  * Hook to bypass pending comment check for comment related plugins compatibility
135
  * @since 2.9.5
136
  */
137
  if ( apply_filters( 'litespeed_vary_check_commenter_pending', true ) ) {
138
- $pending = false ;
139
  foreach ( $comments as $comment ) {
140
  if ( ! $comment->comment_approved ) {// current user has pending comment
141
- $pending = true ;
142
- break ;
143
  }
144
  }
145
 
146
  // No pending comments, don't need to add private cache
147
  if ( ! $pending ) {
148
- $this->remove_commenter() ;
149
 
150
  // Remove commenter prefilled info if exists, for public cache
151
  foreach( $_COOKIE as $cookie_name => $cookie_value ) {
152
  if ( strlen( $cookie_name ) >= 15 && strpos( $cookie_name, 'comment_author_' ) === 0 ) {
153
- unset( $_COOKIE[ $cookie_name ] ) ;
154
  }
155
  }
156
 
157
- return $comments ;
158
  }
159
  }
160
 
161
  // Current user/visitor has pending comments
162
  // set vary=2 for next time vary lookup
163
- $this->add_commenter() ;
164
 
165
  if ( Conf::val( Base::O_CACHE_COMMENTER ) ) {
166
- Control::set_private( 'existing commenter' ) ;
167
  }
168
  else {
169
- Control::set_nocache( 'existing commenter' ) ;
170
  }
171
 
172
- return $comments ;
173
  }
174
 
175
  /**
@@ -178,12 +177,11 @@ class Vary extends Instance {
178
  * @since 1.1.3
179
  * @access public
180
  */
181
- public static function has_vary()
182
- {
183
  if ( empty( $_COOKIE[ self::$_vary_name ] ) ) {
184
- return false ;
185
  }
186
- return $_COOKIE[ self::$_vary_name ] ;
187
  }
188
 
189
  /**
@@ -193,18 +191,17 @@ class Vary extends Instance {
193
  * @since 1.6.2 Removed static referral
194
  * @access public
195
  */
196
- public function add_logged_in( $logged_in_cookie = false, $expire = false, $expiration = false, $uid = false )
197
- {
198
- Debug2::debug( '[Vary] add_logged_in' ) ;
199
 
200
  /**
201
  * NOTE: Run before `$this->_update_default_vary()` to make vary changeable
202
  * @since 2.2.2
203
  */
204
- self::can_ajax_vary() ;
205
 
206
  // If the cookie is lost somehow, set it
207
- $this->_update_default_vary( $uid, $expire ) ;
208
  }
209
 
210
  /**
@@ -214,18 +211,17 @@ class Vary extends Instance {
214
  * @since 1.6.2 Removed static referral
215
  * @access public
216
  */
217
- public function remove_logged_in()
218
- {
219
- Debug2::debug( '[Vary] remove_logged_in' ) ;
220
 
221
  /**
222
  * NOTE: Run before `$this->_update_default_vary()` to make vary changeable
223
  * @since 2.2.2
224
  */
225
- self::can_ajax_vary() ;
226
 
227
  // Force update vary to remove login status
228
- $this->_update_default_vary( -1 ) ;
229
  }
230
 
231
  /**
@@ -235,9 +231,8 @@ class Vary extends Instance {
235
  * @since 2.6 Changed to static
236
  * @access public
237
  */
238
- public static function can_ajax_vary()
239
- {
240
- Debug2::debug( '[Vary] _can_change_vary -> true' ) ;
241
  self::$_can_change_vary = true;
242
  }
243
 
@@ -247,13 +242,12 @@ class Vary extends Instance {
247
  * @since 1.6.2
248
  * @access private
249
  */
250
- private function can_change_vary()
251
- {
252
  // Don't change for ajax due to ajax not sending webp header
253
  if ( Router::is_ajax() ) {
254
  if ( ! self::$_can_change_vary ) {
255
- Debug2::debug( '[Vary] can_change_vary bypassed due to ajax call' ) ;
256
- return false ;
257
  }
258
  }
259
 
@@ -262,8 +256,8 @@ class Vary extends Instance {
262
  * @since 1.6.5
263
  */
264
  if ( $_SERVER["REQUEST_METHOD"] !== 'GET' && $_SERVER["REQUEST_METHOD"] !== 'POST' ) {
265
- Debug2::debug( '[Vary] can_change_vary bypassed due to method not get/post' ) ;
266
- return false ;
267
  }
268
 
269
  /**
@@ -271,16 +265,16 @@ class Vary extends Instance {
271
  * @since 2.9.8 To enable woocommerce cart not empty warm up (@Taba)
272
  */
273
  if ( ! empty( $_SERVER[ 'HTTP_USER_AGENT' ] ) && strpos( $_SERVER[ 'HTTP_USER_AGENT' ], Crawler::FAST_USER_AGENT ) === 0 ) {
274
- Debug2::debug( '[Vary] can_change_vary bypassed due to crawler' ) ;
275
- return false ;
276
  }
277
 
278
  if ( ! apply_filters( 'litespeed_can_change_vary', true ) ) {
279
- Debug2::debug( '[Vary] can_change_vary bypassed due to litespeed_can_change_vary hook' ) ;
280
- return false ;
281
  }
282
 
283
- return true ;
284
  }
285
 
286
  /**
@@ -290,30 +284,29 @@ class Vary extends Instance {
290
  * @since 1.6.6.1 Add ran check to make it only run once ( No run multiple times due to login process doesn't have valid uid )
291
  * @access private
292
  */
293
- private function _update_default_vary( $uid = false, $expire = false )
294
- {
295
  // Make sure header output only run once
296
  if ( ! defined( 'LITESPEED_DID_' . __FUNCTION__ ) ) {
297
- define( 'LITESPEED_DID_' . __FUNCTION__, true ) ;
298
  }
299
  else {
300
- Debug2::debug2( "[Vary] _update_default_vary bypassed due to run already" ) ;
301
- return ;
302
  }
303
 
304
  // If the cookie is lost somehow, set it
305
- $vary = $this->finalize_default_vary( $uid ) ;
306
- $current_vary = self::has_vary() ;
307
  if ( $current_vary !== $vary && $current_vary !== 'commenter' && $this->can_change_vary() ) {
308
- // $_COOKIE[ self::$_vary_name ] = $vary ; // not needed
309
 
310
  // save it
311
  if ( ! $expire ) {
312
- $expire = time() + 2 * DAY_IN_SECONDS ;
313
  }
314
- self::_cookie( $vary, $expire ) ;
315
- Debug2::debug( "[Vary] set_cookie ---> $vary" ) ;
316
- Control::set_nocache( 'changing default vary' . " $current_vary => $vary" ) ;
317
  }
318
  }
319
 
@@ -323,9 +316,8 @@ class Vary extends Instance {
323
  * @since 1.9.1
324
  * @access public
325
  */
326
- public function get_vary_name()
327
- {
328
- return self::$_vary_name ;
329
  }
330
 
331
  /**
@@ -337,22 +329,21 @@ class Vary extends Instance {
337
  * @param string $role The user role
338
  * @return int The set value if already set
339
  */
340
- public function in_vary_group( $role )
341
- {
342
- $group = 0 ;
343
- $vary_groups = Conf::val( Base::O_CACHE_VARY_GROUP ) ;
344
  if ( array_key_exists( $role, $vary_groups ) ) {
345
- $group = $vary_groups[ $role ] ;
346
  }
347
  elseif ( $role === 'administrator' ) {
348
- $group = 99 ;
349
  }
350
 
351
  if ( $group ) {
352
- Debug2::debug2( '[Vary] role in vary_group [group] ' . $group ) ;
353
  }
354
 
355
- return $group ;
356
  }
357
 
358
  /**
@@ -376,31 +367,31 @@ class Vary extends Instance {
376
  }
377
 
378
  // get user's group id
379
- $role = Router::get_role( $uid ) ;
380
 
381
  if ( $uid > 0 && $role ) {
382
- $vary[ 'logged-in' ] = 1 ;
383
 
384
  // parse role group from settings
385
  if ( $role_group = $this->in_vary_group( $role ) ) {
386
- $vary[ 'role' ] = $role_group ;
387
  }
388
 
389
  // Get admin bar set
390
  // see @_get_admin_bar_pref()
391
- $pref = get_user_option( 'show_admin_bar_front', $uid ) ;
392
- Debug2::debug2( '[Vary] show_admin_bar_front: ' . $pref ) ;
393
- $admin_bar = $pref === false || $pref === 'true' ;
394
 
395
  if ( $admin_bar ) {
396
- $vary[ 'admin_bar' ] = 1 ;
397
- Debug2::debug2( '[Vary] admin bar : true' ) ;
398
  }
399
 
400
  }
401
  else {
402
  // Guest user
403
- Debug2::debug( '[Vary] role id: failed, guest' ) ;
404
 
405
  }
406
 
@@ -413,21 +404,21 @@ class Vary extends Instance {
413
  $vary = apply_filters( 'litespeed_vary', $vary );
414
 
415
  if ( ! $vary ) {
416
- return false ;
417
  }
418
 
419
- ksort( $vary ) ;
420
- $res = array() ;
421
  foreach ( $vary as $key => $val ) {
422
- $res[] = $key . ':' . $val ;
423
  }
424
 
425
- $res = implode( ';', $res ) ;
426
  if ( defined( 'LSCWP_LOG' ) ) {
427
- return $res ;
428
  }
429
  // Encrypt in production
430
- return md5( Conf::val( Base::HASH ) . $res ) ;
431
 
432
  }
433
 
@@ -439,9 +430,8 @@ class Vary extends Instance {
439
  * @since 1.1.6
440
  * @access public
441
  */
442
- public function append_commenter()
443
- {
444
- $this->add_commenter( true ) ;
445
  }
446
 
447
  /**
@@ -451,16 +441,15 @@ class Vary extends Instance {
451
  * @access private
452
  * @param boolean $from_redirect If the request is from redirect page or not
453
  */
454
- private function add_commenter( $from_redirect = false )
455
- {
456
  // If the cookie is lost somehow, set it
457
  if ( self::has_vary() !== 'commenter' ) {
458
- // $_COOKIE[ self::$_vary_name ] = 'commenter' ; // not needed
459
 
460
  // save it
461
  // only set commenter status for current domain path
462
- self::_cookie( 'commenter', time() + apply_filters( 'comment_cookie_lifetime', 30000000 ), self::_relative_path( $from_redirect ) ) ;
463
- Control::set_nocache( 'adding commenter status' ) ;
464
  }
465
  }
466
 
@@ -470,15 +459,14 @@ class Vary extends Instance {
470
  * @since 1.1.3
471
  * @access private
472
  */
473
- private function remove_commenter()
474
- {
475
  if ( self::has_vary() === 'commenter' ) {
476
  // remove logged in status from global var
477
- // unset( $_COOKIE[ self::$_vary_name ] ) ; // not needed
478
 
479
  // save it
480
- self::_cookie( false, false, self::_relative_path() ) ;
481
- Control::set_nocache( 'removing commenter status' ) ;
482
  }
483
  }
484
 
@@ -489,16 +477,15 @@ class Vary extends Instance {
489
  * @access private
490
  * @param boolean $from_redirect If the request is from redirect page or not
491
  */
492
- private static function _relative_path( $from_redirect = false )
493
- {
494
- $path = false ;
495
- $tag = $from_redirect ? 'HTTP_REFERER' : 'SCRIPT_URL' ;
496
  if ( ! empty( $_SERVER[ $tag ] ) ) {
497
- $path = parse_url( $_SERVER[ $tag ] ) ;
498
- $path = ! empty( $path[ 'path' ] ) ? $path[ 'path' ] : false ;
499
- Debug2::debug( '[Vary] Cookie Vary path: ' . $path ) ;
500
  }
501
- return $path ;
502
  }
503
 
504
  /**
@@ -512,45 +499,43 @@ class Vary extends Instance {
512
  * @return mixed false if the user has the postpass cookie. Empty string
513
  * if the post is not password protected. Vary header otherwise.
514
  */
515
- public static function finalize()
516
- {
517
- return self::get_instance()->_finalize() ;
518
 
519
  }
520
 
521
- private function _finalize()
522
- {
523
  // Finalize default vary
524
- $this->_update_default_vary() ;
525
 
526
  /**
527
  * Non caccheable page can still set vary ( for logged in process )
528
  * @since 1.6.6.1
529
  */
530
  // if ( ! Control::is_cacheable() ) {
531
- // Debug2::debug2( 'Vary: bypass finalize due to not cacheable' ) ;
532
  // return false;
533
  // }
534
 
535
- $tp_cookies = $this->_format_vary_cookies() ;
536
- global $post ;
537
  if ( ! empty($post->post_password) ) {
538
  if ( isset($_COOKIE['wp-postpass_' . COOKIEHASH]) ) {
539
- Debug2::debug( '[Vary] finalize bypassed due to password protected vary ' ) ;
540
  // If user has password cookie, do not cache
541
- Control::set_nocache('password protected vary') ;
542
- return ;
543
  }
544
 
545
- $tp_cookies[] = 'cookie=wp-postpass_' . COOKIEHASH ;
546
  }
547
 
548
  if ( empty($tp_cookies) ) {
549
- Debug2::debug2( '[Vary] no custimzed vary ' ) ;
550
- return ;
551
  }
552
 
553
- return self::X_HEADER . ': ' . implode(',', $tp_cookies) ;
554
 
555
  }
556
 
@@ -561,35 +546,34 @@ class Vary extends Instance {
561
  * @access private
562
  * @return array An array of all vary cookies currently added.
563
  */
564
- private function _format_vary_cookies()
565
- {
566
  /**
567
  * To add new varys, use hook `API::filter_vary_cookies()` before here
568
  */
569
- do_action( 'litespeed_vary_add' ) ;
570
 
571
  /**
572
  * Give a filter to manipulate vary
573
  * @since 2.7.1
574
  */
575
- $cookies = apply_filters( 'litespeed_vary_cookies', self::$_vary_cookies ) ;
576
  if ( $cookies !== self::$_vary_cookies ) {
577
- Debug2::debug( '[Vary] vary changed by filter [Old] ' . var_export( self::$_vary_cookies, true ) . ' [New] ' . var_export( $cookies, true ) ) ;
578
  }
579
 
580
  if ( ! empty( $cookies ) ) {
581
- $cookies = array_filter( array_unique( $cookies ) ) ;
582
  }
583
 
584
  if ( empty($cookies) ) {
585
- return false ;
586
  }
587
 
588
  foreach ($cookies as $key => $val) {
589
- $cookies[$key] = 'cookie=' . $val ;
590
  }
591
 
592
- return $cookies ;
593
  }
594
 
595
  /**
@@ -601,15 +585,14 @@ class Vary extends Instance {
601
  * @access public
602
  * @param mixed $vary A string or array of vary cookies to add to the current list.
603
  */
604
- public static function add( $vary )
605
- {
606
  if ( ! is_array( $vary ) ) {
607
- $vary = array( $vary ) ;
608
  }
609
 
610
- error_log( 'Deprecated since LSCWP 2.7.1! [Vary] Add new vary ' . var_export( $vary, true ) ) ;
611
 
612
- self::$_vary_cookies = array_merge(self::$_vary_cookies, $vary) ;
613
  }
614
 
615
  /**
@@ -618,8 +601,7 @@ class Vary extends Instance {
618
  * @since 2.6
619
  * @access public
620
  */
621
- public static function append( $name, $val )
622
- {
623
  self::$_default_vary_val[ $name ] = $val;
624
  }
625
 
@@ -634,19 +616,18 @@ class Vary extends Instance {
634
  * @param integer $expire Expire time.
635
  * @param boolean $path False if use wp root path as cookie path
636
  */
637
- private static function _cookie($val = false, $expire = false, $path = false)
638
- {
639
  if ( ! $val ) {
640
- $expire = 1 ;
641
  }
642
 
643
  /**
644
  * Add HTTPS bypass in case clients use both HTTP and HTTPS version of site
645
  * @since 1.7
646
  */
647
- $is_ssl = Conf::val( Base::O_UTIL_NO_HTTPS_VARY ) ? false : is_ssl() ;
648
 
649
- setcookie( self::$_vary_name, $val, $expire, $path?: COOKIEPATH, COOKIE_DOMAIN, $is_ssl, true ) ;
650
  }
651
 
652
  }
128
  * @param array $comments The current comments to output
129
  * @return array The comments to output.
130
  */
131
+ public function check_commenter( $comments ) {
 
132
  /**
133
  * Hook to bypass pending comment check for comment related plugins compatibility
134
  * @since 2.9.5
135
  */
136
  if ( apply_filters( 'litespeed_vary_check_commenter_pending', true ) ) {
137
+ $pending = false;
138
  foreach ( $comments as $comment ) {
139
  if ( ! $comment->comment_approved ) {// current user has pending comment
140
+ $pending = true;
141
+ break;
142
  }
143
  }
144
 
145
  // No pending comments, don't need to add private cache
146
  if ( ! $pending ) {
147
+ $this->remove_commenter();
148
 
149
  // Remove commenter prefilled info if exists, for public cache
150
  foreach( $_COOKIE as $cookie_name => $cookie_value ) {
151
  if ( strlen( $cookie_name ) >= 15 && strpos( $cookie_name, 'comment_author_' ) === 0 ) {
152
+ unset( $_COOKIE[ $cookie_name ] );
153
  }
154
  }
155
 
156
+ return $comments;
157
  }
158
  }
159
 
160
  // Current user/visitor has pending comments
161
  // set vary=2 for next time vary lookup
162
+ $this->add_commenter();
163
 
164
  if ( Conf::val( Base::O_CACHE_COMMENTER ) ) {
165
+ Control::set_private( 'existing commenter' );
166
  }
167
  else {
168
+ Control::set_nocache( 'existing commenter' );
169
  }
170
 
171
+ return $comments;
172
  }
173
 
174
  /**
177
  * @since 1.1.3
178
  * @access public
179
  */
180
+ public static function has_vary() {
 
181
  if ( empty( $_COOKIE[ self::$_vary_name ] ) ) {
182
+ return false;
183
  }
184
+ return $_COOKIE[ self::$_vary_name ];
185
  }
186
 
187
  /**
191
  * @since 1.6.2 Removed static referral
192
  * @access public
193
  */
194
+ public function add_logged_in( $logged_in_cookie = false, $expire = false, $expiration = false, $uid = false ) {
195
+ Debug2::debug( '[Vary] add_logged_in' );
 
196
 
197
  /**
198
  * NOTE: Run before `$this->_update_default_vary()` to make vary changeable
199
  * @since 2.2.2
200
  */
201
+ self::can_ajax_vary();
202
 
203
  // If the cookie is lost somehow, set it
204
+ $this->_update_default_vary( $uid, $expire );
205
  }
206
 
207
  /**
211
  * @since 1.6.2 Removed static referral
212
  * @access public
213
  */
214
+ public function remove_logged_in() {
215
+ Debug2::debug( '[Vary] remove_logged_in' );
 
216
 
217
  /**
218
  * NOTE: Run before `$this->_update_default_vary()` to make vary changeable
219
  * @since 2.2.2
220
  */
221
+ self::can_ajax_vary();
222
 
223
  // Force update vary to remove login status
224
+ $this->_update_default_vary( -1 );
225
  }
226
 
227
  /**
231
  * @since 2.6 Changed to static
232
  * @access public
233
  */
234
+ public static function can_ajax_vary() {
235
+ Debug2::debug( '[Vary] _can_change_vary -> true' );
 
236
  self::$_can_change_vary = true;
237
  }
238
 
242
  * @since 1.6.2
243
  * @access private
244
  */
245
+ private function can_change_vary() {
 
246
  // Don't change for ajax due to ajax not sending webp header
247
  if ( Router::is_ajax() ) {
248
  if ( ! self::$_can_change_vary ) {
249
+ Debug2::debug( '[Vary] can_change_vary bypassed due to ajax call' );
250
+ return false;
251
  }
252
  }
253
 
256
  * @since 1.6.5
257
  */
258
  if ( $_SERVER["REQUEST_METHOD"] !== 'GET' && $_SERVER["REQUEST_METHOD"] !== 'POST' ) {
259
+ Debug2::debug( '[Vary] can_change_vary bypassed due to method not get/post' );
260
+ return false;
261
  }
262
 
263
  /**
265
  * @since 2.9.8 To enable woocommerce cart not empty warm up (@Taba)
266
  */
267
  if ( ! empty( $_SERVER[ 'HTTP_USER_AGENT' ] ) && strpos( $_SERVER[ 'HTTP_USER_AGENT' ], Crawler::FAST_USER_AGENT ) === 0 ) {
268
+ Debug2::debug( '[Vary] can_change_vary bypassed due to crawler' );
269
+ return false;
270
  }
271
 
272
  if ( ! apply_filters( 'litespeed_can_change_vary', true ) ) {
273
+ Debug2::debug( '[Vary] can_change_vary bypassed due to litespeed_can_change_vary hook' );
274
+ return false;
275
  }
276
 
277
+ return true;
278
  }
279
 
280
  /**
284
  * @since 1.6.6.1 Add ran check to make it only run once ( No run multiple times due to login process doesn't have valid uid )
285
  * @access private
286
  */
287
+ private function _update_default_vary( $uid = false, $expire = false ) {
 
288
  // Make sure header output only run once
289
  if ( ! defined( 'LITESPEED_DID_' . __FUNCTION__ ) ) {
290
+ define( 'LITESPEED_DID_' . __FUNCTION__, true );
291
  }
292
  else {
293
+ Debug2::debug2( "[Vary] _update_default_vary bypassed due to run already" );
294
+ return;
295
  }
296
 
297
  // If the cookie is lost somehow, set it
298
+ $vary = $this->finalize_default_vary( $uid );
299
+ $current_vary = self::has_vary();
300
  if ( $current_vary !== $vary && $current_vary !== 'commenter' && $this->can_change_vary() ) {
301
+ // $_COOKIE[ self::$_vary_name ] = $vary; // not needed
302
 
303
  // save it
304
  if ( ! $expire ) {
305
+ $expire = time() + 2 * DAY_IN_SECONDS;
306
  }
307
+ self::_cookie( $vary, $expire );
308
+ Debug2::debug( "[Vary] set_cookie ---> $vary" );
309
+ Control::set_nocache( 'changing default vary' . " $current_vary => $vary" );
310
  }
311
  }
312
 
316
  * @since 1.9.1
317
  * @access public
318
  */
319
+ public function get_vary_name() {
320
+ return self::$_vary_name;
 
321
  }
322
 
323
  /**
329
  * @param string $role The user role
330
  * @return int The set value if already set
331
  */
332
+ public function in_vary_group( $role ) {
333
+ $group = 0;
334
+ $vary_groups = Conf::val( Base::O_CACHE_VARY_GROUP );
 
335
  if ( array_key_exists( $role, $vary_groups ) ) {
336
+ $group = $vary_groups[ $role ];
337
  }
338
  elseif ( $role === 'administrator' ) {
339
+ $group = 99;
340
  }
341
 
342
  if ( $group ) {
343
+ Debug2::debug2( '[Vary] role in vary_group [group] ' . $group );
344
  }
345
 
346
+ return $group;
347
  }
348
 
349
  /**
367
  }
368
 
369
  // get user's group id
370
+ $role = Router::get_role( $uid );
371
 
372
  if ( $uid > 0 && $role ) {
373
+ $vary[ 'logged-in' ] = 1;
374
 
375
  // parse role group from settings
376
  if ( $role_group = $this->in_vary_group( $role ) ) {
377
+ $vary[ 'role' ] = $role_group;
378
  }
379
 
380
  // Get admin bar set
381
  // see @_get_admin_bar_pref()
382
+ $pref = get_user_option( 'show_admin_bar_front', $uid );
383
+ Debug2::debug2( '[Vary] show_admin_bar_front: ' . $pref );
384
+ $admin_bar = $pref === false || $pref === 'true';
385
 
386
  if ( $admin_bar ) {
387
+ $vary[ 'admin_bar' ] = 1;
388
+ Debug2::debug2( '[Vary] admin bar : true' );
389
  }
390
 
391
  }
392
  else {
393
  // Guest user
394
+ Debug2::debug( '[Vary] role id: failed, guest' );
395
 
396
  }
397
 
404
  $vary = apply_filters( 'litespeed_vary', $vary );
405
 
406
  if ( ! $vary ) {
407
+ return false;
408
  }
409
 
410
+ ksort( $vary );
411
+ $res = array();
412
  foreach ( $vary as $key => $val ) {
413
+ $res[] = $key . ':' . $val;
414
  }
415
 
416
+ $res = implode( ';', $res );
417
  if ( defined( 'LSCWP_LOG' ) ) {
418
+ return $res;
419
  }
420
  // Encrypt in production
421
+ return md5( Conf::val( Base::HASH ) . $res );
422
 
423
  }
424
 
430
  * @since 1.1.6
431
  * @access public
432
  */
433
+ public function append_commenter() {
434
+ $this->add_commenter( true );
 
435
  }
436
 
437
  /**
441
  * @access private
442
  * @param boolean $from_redirect If the request is from redirect page or not
443
  */
444
+ private function add_commenter( $from_redirect = false ) {
 
445
  // If the cookie is lost somehow, set it
446
  if ( self::has_vary() !== 'commenter' ) {
447
+ // $_COOKIE[ self::$_vary_name ] = 'commenter'; // not needed
448
 
449
  // save it
450
  // only set commenter status for current domain path
451
+ self::_cookie( 'commenter', time() + apply_filters( 'comment_cookie_lifetime', 30000000 ), self::_relative_path( $from_redirect ) );
452
+ Control::set_nocache( 'adding commenter status' );
453
  }
454
  }
455
 
459
  * @since 1.1.3
460
  * @access private
461
  */
462
+ private function remove_commenter() {
 
463
  if ( self::has_vary() === 'commenter' ) {
464
  // remove logged in status from global var
465
+ // unset( $_COOKIE[ self::$_vary_name ] ); // not needed
466
 
467
  // save it
468
+ self::_cookie( false, false, self::_relative_path() );
469
+ Control::set_nocache( 'removing commenter status' );
470
  }
471
  }
472
 
477
  * @access private
478
  * @param boolean $from_redirect If the request is from redirect page or not
479
  */
480
+ private static function _relative_path( $from_redirect = false ) {
481
+ $path = false;
482
+ $tag = $from_redirect ? 'HTTP_REFERER' : 'SCRIPT_URL';
 
483
  if ( ! empty( $_SERVER[ $tag ] ) ) {
484
+ $path = parse_url( $_SERVER[ $tag ] );
485
+ $path = ! empty( $path[ 'path' ] ) ? $path[ 'path' ] : false;
486
+ Debug2::debug( '[Vary] Cookie Vary path: ' . $path );
487
  }
488
+ return $path;
489
  }
490
 
491
  /**
499
  * @return mixed false if the user has the postpass cookie. Empty string
500
  * if the post is not password protected. Vary header otherwise.
501
  */
502
+ public static function finalize() {
503
+ return self::get_instance()->_finalize();
 
504
 
505
  }
506
 
507
+ private function _finalize() {
 
508
  // Finalize default vary
509
+ $this->_update_default_vary();
510
 
511
  /**
512
  * Non caccheable page can still set vary ( for logged in process )
513
  * @since 1.6.6.1
514
  */
515
  // if ( ! Control::is_cacheable() ) {
516
+ // Debug2::debug2( 'Vary: bypass finalize due to not cacheable' );
517
  // return false;
518
  // }
519
 
520
+ $tp_cookies = $this->_format_vary_cookies();
521
+ global $post;
522
  if ( ! empty($post->post_password) ) {
523
  if ( isset($_COOKIE['wp-postpass_' . COOKIEHASH]) ) {
524
+ Debug2::debug( '[Vary] finalize bypassed due to password protected vary ' );
525
  // If user has password cookie, do not cache
526
+ Control::set_nocache('password protected vary');
527
+ return;
528
  }
529
 
530
+ $tp_cookies[] = 'cookie=wp-postpass_' . COOKIEHASH;
531
  }
532
 
533
  if ( empty($tp_cookies) ) {
534
+ Debug2::debug2( '[Vary] no custimzed vary ' );
535
+ return;
536
  }
537
 
538
+ return self::X_HEADER . ': ' . implode(',', $tp_cookies);
539
 
540
  }
541
 
546
  * @access private
547
  * @return array An array of all vary cookies currently added.
548
  */
549
+ private function _format_vary_cookies() {
 
550
  /**
551
  * To add new varys, use hook `API::filter_vary_cookies()` before here
552
  */
553
+ do_action( 'litespeed_vary_add' );
554
 
555
  /**
556
  * Give a filter to manipulate vary
557
  * @since 2.7.1
558
  */
559
+ $cookies = apply_filters( 'litespeed_vary_cookies', self::$_vary_cookies );
560
  if ( $cookies !== self::$_vary_cookies ) {
561
+ Debug2::debug( '[Vary] vary changed by filter [Old] ' . var_export( self::$_vary_cookies, true ) . ' [New] ' . var_export( $cookies, true ) );
562
  }
563
 
564
  if ( ! empty( $cookies ) ) {
565
+ $cookies = array_filter( array_unique( $cookies ) );
566
  }
567
 
568
  if ( empty($cookies) ) {
569
+ return false;
570
  }
571
 
572
  foreach ($cookies as $key => $val) {
573
+ $cookies[$key] = 'cookie=' . $val;
574
  }
575
 
576
+ return $cookies;
577
  }
578
 
579
  /**
585
  * @access public
586
  * @param mixed $vary A string or array of vary cookies to add to the current list.
587
  */
588
+ public static function add( $vary ) {
 
589
  if ( ! is_array( $vary ) ) {
590
+ $vary = array( $vary );
591
  }
592
 
593
+ error_log( 'Deprecated since LSCWP 2.7.1! [Vary] Add new vary ' . var_export( $vary, true ) );
594
 
595
+ self::$_vary_cookies = array_merge(self::$_vary_cookies, $vary);
596
  }
597
 
598
  /**
601
  * @since 2.6
602
  * @access public
603
  */
604
+ public static function append( $name, $val ) {
 
605
  self::$_default_vary_val[ $name ] = $val;
606
  }
607
 
616
  * @param integer $expire Expire time.
617
  * @param boolean $path False if use wp root path as cookie path
618
  */
619
+ private static function _cookie($val = false, $expire = false, $path = false) {
 
620
  if ( ! $val ) {
621
+ $expire = 1;
622
  }
623
 
624
  /**
625
  * Add HTTPS bypass in case clients use both HTTP and HTTPS version of site
626
  * @since 1.7
627
  */
628
+ $is_ssl = Conf::val( Base::O_UTIL_NO_HTTPS_VARY ) ? false : is_ssl();
629
 
630
+ setcookie( self::$_vary_name, $val, $expire, $path?: COOKIEPATH, COOKIE_DOMAIN, $is_ssl, true );
631
  }
632
 
633
  }
thirdparty/yith-wishlist.cls.php CHANGED
@@ -3,19 +3,19 @@
3
  * The Third Party integration with the YITH WooCommerce Wishlist plugin.
4
  *
5
  * @since 1.1.0
6
- * @package LiteSpeed_Cache
7
- * @subpackage LiteSpeed_Cache/thirdparty
8
- * @author LiteSpeed Technologies <info@litespeedtech.com>
9
  */
10
- namespace LiteSpeed\Thirdparty ;
11
 
12
- defined( 'WPINC' ) || exit ;
 
 
 
 
 
 
 
 
13
 
14
- class Yith_Wishlist
15
- {
16
- const ESI_PARAM_ATTS = 'yith_wcwl_atts' ;
17
- const ESI_PARAM_POSTID = 'yith_wcwl_post_id' ;
18
- private static $atts = null ; // Not currently used. Depends on how YITH adds attributes
19
 
20
  /**
21
  * Detects if YITH WooCommerce Wishlist and WooCommerce are installed.
@@ -23,18 +23,17 @@ class Yith_Wishlist
23
  * @since 1.1.0
24
  * @access public
25
  */
26
- public static function detect()
27
- {
28
  if ( ! defined( 'WOOCOMMERCE_VERSION' ) || ! defined( 'YITH_WCWL' ) ) {
29
- return ;
30
  }
31
  if ( apply_filters( 'litespeed_esi_status', false ) ) {
32
  add_action( 'litespeed_tpl_normal', __CLASS__ . '::is_not_esi' );
33
  add_action( 'litespeed_esi_load-yith_wcwl_add', __CLASS__ . '::load_add_to_wishlist' );
34
 
35
  // hook to add/delete wishlist
36
- add_action( 'yith_wcwl_added_to_wishlist', __CLASS__ . '::purge' ) ;
37
- add_action( 'yith_wcwl_removed_from_wishlist', __CLASS__ . '::purge' ) ;
38
  }
39
  }
40
 
@@ -44,8 +43,7 @@ class Yith_Wishlist
44
  * @since 1.2.0
45
  * @access public
46
  */
47
- public static function purge()
48
- {
49
  do_action( 'litespeed_purge_esi', 'yith_wcwl_add' );
50
  }
51
 
@@ -58,32 +56,51 @@ class Yith_Wishlist
58
  * @since 1.1.0
59
  * @access public
60
  */
61
- public static function is_not_esi()
62
- {
63
- add_filter( 'yith_wcwl_add_to_wishlisth_button_html', __CLASS__ . '::sub_add_to_wishlist', 999 ) ;
 
 
 
 
 
 
 
 
 
 
64
 
 
65
  }
66
 
67
  /**
68
  * Hooked to the yith_wcwl_add_to_wishlisth_button_html filter.
69
  *
70
- * The add to wishlist button displays a different output when the item
71
- * is already in the wishlist/cart. For this reason, the button must be
72
- * an ESI block. This function replaces the normal html with the ESI
73
- * block.
74
  *
75
  * @since 1.1.0
76
  * @access public
77
- * @param $template unused
78
- * @return string The html for future callbacks to filter.
79
  */
80
- public static function sub_add_to_wishlist( $template )
81
- {
82
- global $post ;
83
  $params = array(
84
- self::ESI_PARAM_POSTID => $post->ID,
85
- ) ;
86
- return apply_filters( 'litespeed_esi_url', 'yith_wcwl_add', 'YITH ADD TO WISHLIST', $params );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87
  }
88
 
89
  /**
@@ -93,15 +110,12 @@ class Yith_Wishlist
93
  *
94
  * @since 1.1.0
95
  * @access public
96
- * @global $post, $wp_query
97
- * @param array $params The input ESI parameters.
98
  */
99
- public static function load_add_to_wishlist( $params )
100
- {
101
- global $post, $wp_query ;
102
- $post = get_post( $params[ self::ESI_PARAM_POSTID ] ) ;
103
- $wp_query->setup_postdata( $post ) ;
104
- echo \YITH_WCWL_Shortcode::add_to_wishlist( /*$params[self::ESI_PARAM_ATTS]*/array() ) ;
105
  do_action( 'litespeed_control_set_private', 'yith wishlist' );
106
  do_action( 'litespeed_vary_no' );
107
  }
3
  * The Third Party integration with the YITH WooCommerce Wishlist plugin.
4
  *
5
  * @since 1.1.0
 
 
 
6
  */
7
+ namespace LiteSpeed\Thirdparty;
8
 
9
+ defined( 'WPINC' ) || exit;
10
+
11
+ use \LiteSpeed\Tag;
12
+ use \LiteSpeed\Conf;
13
+ use \LiteSpeed\Base;
14
+
15
+ class Yith_Wishlist {
16
+ const ESI_PARAM_POSTID = 'yith_pid';
17
+ private static $_post_id;
18
 
 
 
 
 
 
19
 
20
  /**
21
  * Detects if YITH WooCommerce Wishlist and WooCommerce are installed.
23
  * @since 1.1.0
24
  * @access public
25
  */
26
+ public static function detect() {
 
27
  if ( ! defined( 'WOOCOMMERCE_VERSION' ) || ! defined( 'YITH_WCWL' ) ) {
28
+ return;
29
  }
30
  if ( apply_filters( 'litespeed_esi_status', false ) ) {
31
  add_action( 'litespeed_tpl_normal', __CLASS__ . '::is_not_esi' );
32
  add_action( 'litespeed_esi_load-yith_wcwl_add', __CLASS__ . '::load_add_to_wishlist' );
33
 
34
  // hook to add/delete wishlist
35
+ add_action( 'yith_wcwl_added_to_wishlist', __CLASS__ . '::purge' );
36
+ add_action( 'yith_wcwl_removed_from_wishlist', __CLASS__ . '::purge' );
37
  }
38
  }
39
 
43
  * @since 1.2.0
44
  * @access public
45
  */
46
+ public static function purge() {
 
47
  do_action( 'litespeed_purge_esi', 'yith_wcwl_add' );
48
  }
49
 
56
  * @since 1.1.0
57
  * @access public
58
  */
59
+ public static function is_not_esi() {
60
+ add_filter( 'yith_wcwl_add_to_wishlist_params', __CLASS__ . '::add_to_wishlist_params', 999, 2 );
61
+
62
+ add_filter( 'yith_wcwl_add_to_wishlisth_button_html', __CLASS__ . '::sub_add_to_wishlist', 999 );
63
+ }
64
+
65
+ /**
66
+ * Store the post id for later shortcode usage
67
+ *
68
+ * @since 3.4.1
69
+ */
70
+ public static function add_to_wishlist_params( $defaults, $atts ) {
71
+ self::$_post_id = ! empty( $atts[ 'product_id' ] ) ? $atts[ 'product_id' ] : $defaults[ 'product_id' ];
72
 
73
+ return $defaults;
74
  }
75
 
76
  /**
77
  * Hooked to the yith_wcwl_add_to_wishlisth_button_html filter.
78
  *
79
+ * The add to wishlist button displays a different output when the item is already in the wishlist/cart.
80
+ * For this reason, the button must be an ESI block. This function replaces the normal html with the ESI block.
 
 
81
  *
82
  * @since 1.1.0
83
  * @access public
 
 
84
  */
85
+ public static function sub_add_to_wishlist( $template ) {
 
 
86
  $params = array(
87
+ self::ESI_PARAM_POSTID => self::$_post_id,
88
+ );
89
+
90
+ $inline_tags = array(
91
+ '',
92
+ rtrim( Tag::TYPE_ESI, '.' ),
93
+ Tag::TYPE_ESI . 'yith_wcwl_add',
94
+ );
95
+ $inline_tags = implode( ',', array_map( function($val){ return 'public:' . LSWCP_TAG_PREFIX . '_' . $val; }, $inline_tags ) );
96
+ $inline_tags .= ',' . LSWCP_TAG_PREFIX . '_tag_priv';
97
+
98
+ $inline_params = array(
99
+ 'val' => $template,
100
+ 'tag' => $inline_tags,
101
+ 'control' => 'private,no-vary,max-age=' . Conf::val( Base::O_CACHE_TTL_PRIV ),
102
+ );
103
+ return apply_filters( 'litespeed_esi_url', 'yith_wcwl_add', 'YITH ADD TO WISHLIST', $params, 'private,no-vary', false, false, false, $inline_params );
104
  }
105
 
106
  /**
110
  *
111
  * @since 1.1.0
112
  * @access public
 
 
113
  */
114
+ public static function load_add_to_wishlist( $params ) {
115
+ // global $post, $wp_query;
116
+ // $post = get_post( $params[ self::ESI_PARAM_POSTID ] );
117
+ // $wp_query->setup_postdata( $post );
118
+ echo \YITH_WCWL_Shortcode::add_to_wishlist( array( 'product_id' => $params[ self::ESI_PARAM_POSTID ] ) );
 
119
  do_action( 'litespeed_control_set_private', 'yith wishlist' );
120
  do_action( 'litespeed_vary_no' );
121
  }