LiteSpeed Cache - Version 3.4.2

Version Description

  • Sep 8 2020 =
  • CCSS Corrected the issue that wrongly appended non-CSS files to CSS in links before sending request.
  • 3rd YITH wishlist now sends a combined single sub request for all widgets contained in one page. (LSWS v5.4.9 build 3+ required)
  • ESI Added support for ESI combine feature.
  • GUI Dropped banner notification for missing domain key when domain key is not initialized.
  • Log When QC whitelist check fails, a detailed failure log is now appended.
Download this release

Release Info

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

Code changes from version 3.4.1 to 3.4.2

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.1
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.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
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.2
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.2' );
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
@@ -2,8 +2,8 @@
2
  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.1
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.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.
2
  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.1
6
+ Stable tag: 3.4.2
7
  License: GPLv3
8
  License URI: http://www.gnu.org/licenses/gpl.html
9
 
245
 
246
  == Changelog ==
247
 
248
+ = 3.4.2 - Sep 8 2020 =
249
+ * **CCSS** Corrected the issue that wrongly appended non-CSS files to CSS in links before sending request.
250
+ * **3rd** YITH wishlist now sends a combined single sub request for all widgets contained in one page. (LSWS v5.4.9 build 3+ required)
251
+ * **ESI** Added support for ESI combine feature.
252
+ * **GUI** Dropped banner notification for missing domain key when domain key is not initialized.
253
+ * **Log** When QC whitelist check fails, a detailed failure log is now appended.
254
+
255
  = 3.4.1 - Sep 2 2020 =
256
  * 🐞**CCSS** Fixed an issue where dynamically generated CSS failed with `TypeError: Cannot read property type of undefined`.
257
  * 🐞**Page Optimize** Fixed CSS optimization compatibility for CSS dynamically generated with PHP.
src/api.cls.php CHANGED
@@ -118,6 +118,7 @@ class API extends Base {
118
  // Filter `litespeed_esi_params` // @previous API::hook_esi_param( $hook )
119
  // Action `litespeed_tpl_normal` // @previous API::hook_tpl_not_esi($hook) && Action `litespeed_is_not_esi_template`
120
  // Action `litespeed_esi_load-$block` // @usage add_action( 'litespeed_esi_load-' . $block, $hook ) // @previous API::hook_tpl_esi($block, $hook)
 
121
 
122
  /**
123
  * Vary
118
  // Filter `litespeed_esi_params` // @previous API::hook_esi_param( $hook )
119
  // Action `litespeed_tpl_normal` // @previous API::hook_tpl_not_esi($hook) && Action `litespeed_is_not_esi_template`
120
  // Action `litespeed_esi_load-$block` // @usage add_action( 'litespeed_esi_load-' . $block, $hook ) // @previous API::hook_tpl_esi($block, $hook)
121
+ add_action( 'litespeed_esi_combine', __NAMESPACE__ . '\ESI::combine' );
122
 
123
  /**
124
  * Vary
src/cloud.cls.php CHANGED
@@ -682,9 +682,9 @@ class Cloud extends Base {
682
  * @access public
683
  */
684
  public function show_promo() {
685
- if ( ! $this->_api_key ) {
686
- Admin_Display::error( Error::msg( 'lack_of_api_key' ), true );
687
- }
688
 
689
  if ( empty( $this->_summary[ 'promo' ] ) ) {
690
  return;
@@ -978,7 +978,7 @@ class Cloud extends Base {
978
  if ( is_wp_error( $response ) ) {
979
  $error_message = $response->get_error_message();
980
  Debug2::debug( '[CLoud] failed to get ip whitelist: ' . $error_message );
981
- throw new \Exception( 'Failed to fetch QUIC.cloud whitelist' );
982
  }
983
 
984
  $json = json_decode( $response[ 'body' ], true );
682
  * @access public
683
  */
684
  public function show_promo() {
685
+ // if ( ! $this->_api_key && ! defined( 'LITESPEED_DISMISS_DOMAIN_KEY' ) ) {
686
+ // Admin_Display::error( Error::msg( 'lack_of_api_key' ), true );
687
+ // }
688
 
689
  if ( empty( $this->_summary[ 'promo' ] ) ) {
690
  return;
978
  if ( is_wp_error( $response ) ) {
979
  $error_message = $response->get_error_message();
980
  Debug2::debug( '[CLoud] failed to get ip whitelist: ' . $error_message );
981
+ throw new \Exception( 'Failed to fetch QUIC.cloud whitelist ' . $error_message );
982
  }
983
 
984
  $json = json_decode( $response[ 'body' ], true );
src/core.cls.php CHANGED
@@ -416,13 +416,14 @@ class Core extends Instance {
416
  */
417
  if ( defined( 'LSCACHE_IS_ESI' ) ) {
418
  Debug2::debug( '[Core] ESI Start 👇' );
419
- if ( strlen( $buffer ) > 100 ) {
420
- Debug2::debug( trim( substr( $buffer, 0, 100 ) ) . '.....' );
421
  }
422
  else {
423
  Debug2::debug( $buffer );
424
  }
425
  Debug2::debug( '[Core] ESI End 👆' );
 
426
  }
427
 
428
  if ( apply_filters( 'litespeed_is_json', false ) ) {
416
  */
417
  if ( defined( 'LSCACHE_IS_ESI' ) ) {
418
  Debug2::debug( '[Core] ESI Start 👇' );
419
+ if ( strlen( $buffer ) > 500 ) {
420
+ Debug2::debug( trim( substr( $buffer, 0, 500 ) ) . '.....' );
421
  }
422
  else {
423
  Debug2::debug( $buffer );
424
  }
425
  Debug2::debug( '[Core] ESI End 👆' );
426
+ Debug2::debug( $buffer );
427
  }
428
 
429
  if ( apply_filters( 'litespeed_is_json', false ) ) {
src/css.cls.php CHANGED
@@ -217,11 +217,13 @@ class CSS extends Base {
217
  if ( strpos( $match[ 0 ], '<link' ) === 0 ) {
218
  $attrs = Utility::parse_attr( $match[ 1 ] );
219
 
220
- if ( ! empty( $attrs[ 'rel' ] ) ) {
221
- if ( $attrs[ 'rel' ] != 'stylesheet' ) {
222
- if ( $attrs[ 'rel' ] != 'preload' || empty( $attrs[ 'as' ] ) || $attrs[ 'as' ] != 'style' ) {
223
- continue;
224
- }
 
 
225
  }
226
  }
227
 
217
  if ( strpos( $match[ 0 ], '<link' ) === 0 ) {
218
  $attrs = Utility::parse_attr( $match[ 1 ] );
219
 
220
+ if ( empty( $attrs[ 'rel' ] ) ) {
221
+ continue;
222
+ }
223
+
224
+ if ( $attrs[ 'rel' ] != 'stylesheet' ) {
225
+ if ( $attrs[ 'rel' ] != 'preload' || empty( $attrs[ 'as' ] ) || $attrs[ 'as' ] != 'style' ) {
226
+ continue;
227
  }
228
  }
229
 
src/esi.cls.php CHANGED
@@ -9,28 +9,30 @@
9
  * @subpackage LiteSpeed/src
10
  * @author LiteSpeed Technologies <info@litespeedtech.com>
11
  */
12
- namespace LiteSpeed ;
13
 
14
- defined( 'WPINC' ) || exit ;
15
 
16
  class ESI extends Instance {
17
- protected static $_instance ;
18
 
19
- private static $has_esi = false ;
20
- private $esi_args = null ;
21
- private $_esi_preserve_list = array() ;
 
22
  private $_nonce_actions = array( -1 => '' ); // val is cache control
23
 
24
- const QS_ACTION = 'lsesi' ;
25
- const QS_PARAMS = 'esi' ;
 
26
 
27
- const PARAM_ARGS = 'args' ;
28
- const PARAM_ID = 'id' ;
29
- const PARAM_INSTANCE = 'instance' ;
30
- const PARAM_NAME = 'name' ;
31
 
32
- const WIDGET_O_ESIENABLE = 'widget_esi_enable' ;
33
- const WIDGET_O_TTL = 'widget_ttl' ;
34
 
35
  /**
36
  * Confructor of ESI
@@ -44,18 +46,18 @@ class ESI extends Instance {
44
  * @since 2.9.7.2
45
  */
46
  if ( Router::is_ajax() || ! Router::esi_enabled() ) {
47
- return ;
48
  }
49
 
50
  // Init ESI in `after_setup_theme` hook after detected if LITESPEED_DISABLE_ALL is ON or not
51
- add_action( 'litespeed_initing', array( $this, 'esi_init' ) ) ;
52
 
53
  /**
54
  * Overwrite wp_create_nonce func
55
  * @since 2.9.5
56
  */
57
  if ( ! is_admin() && ! function_exists( 'wp_create_nonce' ) ) {
58
- $this->_transform_nonce() ;
59
  }
60
  }
61
 
@@ -68,17 +70,17 @@ class ESI extends Instance {
68
  * @access public
69
  */
70
  public function esi_init() {
71
- add_action( 'template_include', array( $this, 'esi_template' ), 99999 ) ;
72
 
73
- add_action( 'load-widgets.php', __NAMESPACE__ . '\Purge::purge_widget' ) ;
74
- add_action( 'wp_update_comment_count', __NAMESPACE__ . '\Purge::purge_comment_widget' ) ;
75
 
76
  /**
77
  * Recover REQUEST_URI
78
  * @since 1.8.1
79
  */
80
  if ( ! empty( $_GET[ self::QS_ACTION ] ) ) {
81
- $this->_register_esi_actions() ;
82
  }
83
 
84
  /**
@@ -95,7 +97,7 @@ class ESI extends Instance {
95
  * @since 2.8.1 Check is_admin for Elementor compatibility #726013
96
  */
97
  if ( ! is_admin() ) {
98
- add_shortcode( 'esi', array( $this, 'shortcode' ) ) ;
99
  }
100
 
101
  }
@@ -147,8 +149,7 @@ class ESI extends Instance {
147
  *
148
  * @since 2.9.5
149
  */
150
- public function is_nonce_action( $action )
151
- {
152
  foreach ( $this->_nonce_actions as $k => $v ) {
153
  if ( strpos( $k, '*' ) !== false ) {
154
  if( preg_match( '#' . $k . '#iU', $action ) ) {
@@ -208,23 +209,22 @@ class ESI extends Instance {
208
  * @since 2.8
209
  * @access public
210
  */
211
- public function shortcode( $atts )
212
- {
213
  if ( empty( $atts[ 0 ] ) ) {
214
- Debug2::debug( '[ESI] ===shortcode wrong format', $atts ) ;
215
- return 'Wrong shortcode esi format' ;
216
  }
217
 
218
- $cache = 'public,no-vary' ;
219
  if ( ! empty( $atts[ 'cache' ] ) ) {
220
- $cache = $atts[ 'cache' ] ;
221
- unset( $atts[ 'cache' ] ) ;
222
  }
223
 
224
- do_action( 'litespeed_esi_shortcode-' . $atts[ 0 ] ) ;
225
 
226
  // Show ESI link
227
- return self::sub_esi_block( 'esi', 'esi-shortcode', $atts, $cache ) ;
228
  }
229
 
230
  /**
@@ -235,9 +235,8 @@ class ESI extends Instance {
235
  * @access public
236
  * @return string Esi On header if request has esi, empty string otherwise.
237
  */
238
- public static function has_esi()
239
- {
240
- return self::$has_esi ;
241
  }
242
 
243
  /**
@@ -246,9 +245,8 @@ class ESI extends Instance {
246
  * @since 1.1.3
247
  * @access public
248
  */
249
- public static function set_has_esi()
250
- {
251
- self::$has_esi = true ;
252
  }
253
 
254
  /**
@@ -259,20 +257,20 @@ class ESI extends Instance {
259
  * @access private
260
  */
261
  private function _register_esi_actions() {
262
- ! defined( 'LSCACHE_IS_ESI' ) && define( 'LSCACHE_IS_ESI', $_GET[ self::QS_ACTION ] ) ;// Reused this to ESI block ID
263
 
264
- ! empty( $_SERVER[ 'ESI_REFERER' ] ) && defined( 'LSCWP_LOG' ) && Debug2::debug( '[ESI] ESI_REFERER: ' . $_SERVER[ 'ESI_REFERER' ] ) ;
265
 
266
  /**
267
  * Only when ESI's parent is not REST, replace REQUEST_URI to avoid breaking WP5 editor REST call
268
  * @since 2.9.3
269
  */
270
  if ( ! empty( $_SERVER[ 'ESI_REFERER' ] ) && ! REST::get_instance()->is_rest( $_SERVER[ 'ESI_REFERER' ] ) ) {
271
- $_SERVER[ 'REQUEST_URI' ] = $_SERVER[ 'ESI_REFERER' ] ;
272
  }
273
 
274
  if ( ! empty( $_SERVER[ 'ESI_CONTENT_TYPE' ] ) && strpos( $_SERVER[ 'ESI_CONTENT_TYPE' ], 'application/json' ) === 0 ) {
275
- add_filter( 'litespeed_is_json', '__return_true' ) ;
276
  }
277
 
278
  /**
@@ -280,15 +278,17 @@ class ESI extends Instance {
280
  * NOTE: Not effective due to ESI req are all to `/` yet
281
  * @since 2.9.4
282
  */
283
- add_action( 'rest_api_init', array( $this, 'load_esi_block' ), 101 ) ;
284
 
285
  // Register ESI blocks
286
- add_action('litespeed_esi_load-widget', array($this, 'load_widget_block')) ;
287
- add_action('litespeed_esi_load-admin-bar', array($this, 'load_admin_bar_block')) ;
288
- add_action('litespeed_esi_load-comment-form', array($this, 'load_comment_form_block')) ;
289
 
290
  add_action('litespeed_esi_load-nonce', array( $this, 'load_nonce_block' ) );
291
- add_action('litespeed_esi_load-esi', array( $this, 'load_esi_shortcode' ) ) ;
 
 
292
  }
293
 
294
  /**
@@ -300,13 +300,12 @@ class ESI extends Instance {
300
  * @param string $template The template path filtered.
301
  * @return string The new template path.
302
  */
303
- public function esi_template( $template )
304
- {
305
  // Check if is an ESI request
306
  if ( defined( 'LSCACHE_IS_ESI' ) ) {
307
- Debug2::debug( '[ESI] calling template' ) ;
308
 
309
- return LSCWP_DIR . 'tpl/esi.tpl.php' ;
310
  }
311
  $this->_register_not_esi_actions();
312
  return $template;
@@ -319,31 +318,94 @@ class ESI extends Instance {
319
  * @since 1.1.3
320
  * @access private
321
  */
322
- private function _register_not_esi_actions()
323
- {
324
  do_action( 'litespeed_tpl_normal' );
325
 
326
  if ( ! Control::is_cacheable() ) {
327
- return ;
328
  }
329
 
330
  if ( Router::is_ajax() ) {
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() ) {
338
- remove_action('wp_footer', 'wp_admin_bar_render', 1000) ;
339
- add_action('wp_footer', array($this, 'sub_admin_bar_block'), 1000) ;
340
  }
341
 
342
  // Add comment forum esi for logged-in user or commenter
343
  if ( ! Router::is_ajax() && Vary::has_vary() ) {
344
- add_filter( 'comment_form_defaults', array( $this, 'register_comment_form_actions' ) ) ;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
345
  }
346
 
 
347
  }
348
 
349
  /**
@@ -364,93 +426,91 @@ class ESI extends Instance {
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
  }
369
 
370
  if ( $silence ) {
371
  // Don't add comment to esi block ( orignal for nonce used in tag property data-nonce='esi_block' )
372
- $params[ '_ls_silence' ] = true ;
373
  }
374
 
375
  if ( REST::get_instance()->is_rest() || REST::get_instance()->is_internal_rest() ) {
376
- $params[ 'is_json' ] = 1 ;
377
  }
378
 
379
  $params = apply_filters( 'litespeed_esi_params', $params, $block_id );
380
- $control = apply_filters('litespeed_esi_control', $control, $block_id ) ;
381
 
382
  if ( !is_array($params) || !is_string($control) ) {
383
- defined( 'LSCWP_LOG' ) && Debug2::debug( "[ESI] 🛑 Sub hooks returned Params: \n" . var_export($params, true) . "\ncache control: \n" . var_export($control, true) ) ;
384
 
385
- return false ;
386
  }
387
 
388
  // Build params for URL
389
  $appended_params = array(
390
  self::QS_ACTION => $block_id,
391
- ) ;
392
  if ( ! empty( $control ) ) {
393
- $appended_params[ '_control' ] = $control ;
394
  }
395
  if ( $params ) {
396
- $appended_params[ self::QS_PARAMS ] = base64_encode( json_encode( $params ) ) ;
397
  Debug2::debug2( '[ESI] param ', $params );
398
  }
399
 
400
  // Append hash
401
- $appended_params[ '_hash' ] = self::_gen_esi_md5( $appended_params ) ;
402
 
403
  /**
404
  * Escape potential chars
405
  * @since 2.9.4
406
  */
407
- $appended_params = array_map( 'urlencode', $appended_params ) ;
408
 
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
  }
429
  if ( $svar ) {
430
- $output .= " as-var='1'" ;
 
 
 
431
  }
432
- $output .= " />" ;
 
 
 
433
 
434
  if ( ! $silence ) {
435
- $output = "<!-- lscwp $wrapper -->$output<!-- lscwp $wrapper esi end -->" ;
436
  }
437
 
438
- Debug2::debug( "[ESI] 💕 [BLock_ID] $block_id \t[wrapper] $wrapper \t\t[Control] $control" ) ;
439
- Debug2::debug2( $output ) ;
440
 
441
- self::set_has_esi() ;
442
 
443
  // Convert to string to avoid html chars filter when using
444
  // Will reverse the buffer when output in self::finalize()
445
  if ( $preserved ) {
446
- $hash = md5( $output ) ;
447
- self::get_instance()->_esi_preserve_list[ $hash ] = $output ;
448
- Debug2::debug( "[ESI] Preserved to $hash" ) ;
449
 
450
- return $hash ;
451
  }
452
 
453
- return $output ;
454
  }
455
 
456
  /**
@@ -459,23 +519,22 @@ class ESI extends Instance {
459
  * @since 2.9.6
460
  * @access private
461
  */
462
- private static function _gen_esi_md5( $params )
463
- {
464
  $keys = array(
465
  self::QS_ACTION,
466
  '_control',
467
  self::QS_PARAMS,
468
- ) ;
469
 
470
- $str = '' ;
471
  foreach ( $keys as $v ) {
472
  if ( isset( $params[ $v ] ) && is_string( $params[ $v ] ) ) {
473
- $str .= $params[ $v ] ;
474
  }
475
  }
476
- Debug2::debug2( '[ESI] md5_string=' . $str ) ;
477
 
478
- return md5( Conf::val( Base::HASH ) . $str ) ;
479
  }
480
 
481
  /**
@@ -484,22 +543,29 @@ class ESI extends Instance {
484
  * @since 1.1.3
485
  * @access private
486
  */
487
- private function _parse_esi_param()
488
- {
489
- if ( ! isset($_REQUEST[self::QS_PARAMS]) ) {
490
- return false ;
 
 
 
491
  }
492
- $req_params = $_REQUEST[self::QS_PARAMS] ;
493
- $unencrypted = base64_decode($req_params) ;
 
 
 
 
494
  if ( $unencrypted === false ) {
495
- return false ;
496
  }
497
 
498
- Debug2::debug2( '[ESI] parms', $unencrypted ) ;
499
- // $unencoded = urldecode($unencrypted) ; no need to do this as $_GET is already parsed
500
- $params = json_decode( $unencrypted, true ) ;
501
 
502
- return $params ;
503
  }
504
 
505
  /**
@@ -508,30 +574,29 @@ class ESI extends Instance {
508
  * @since 1.1.3
509
  * @access public
510
  */
511
- public function load_esi_block()
512
- {
513
  /**
514
  * Validate if is a legal ESI req
515
  * @since 2.9.6
516
  */
517
  if ( empty( $_GET[ '_hash' ] ) || self::_gen_esi_md5( $_GET ) != $_GET[ '_hash' ] ) {
518
- Debug2::debug( '[ESI] ❌ Failed to validate _hash' ) ;
519
- return ;
520
  }
521
 
522
- $params = $this->_parse_esi_param() ;
523
 
524
  if ( defined( 'LSCWP_LOG' ) ) {
525
- $logInfo = '[ESI] ⭕ ' ;
526
  if( ! empty( $params[ self::PARAM_NAME ] ) ) {
527
- $logInfo .= ' Name: ' . $params[ self::PARAM_NAME ] . ' ----- ' ;
528
  }
529
- $logInfo .= ' [ID] ' . LSCACHE_IS_ESI ;
530
- Debug2::debug( $logInfo ) ;
531
  }
532
 
533
  if ( ! empty( $params[ '_ls_silence' ] ) ) {
534
- ! defined( 'LSCACHE_ESI_SILENCE' ) && define( 'LSCACHE_ESI_SILENCE', true ) ;
535
  }
536
 
537
  /**
@@ -539,11 +604,11 @@ class ESI extends Instance {
539
  * @since 2.9.4
540
  */
541
  if ( ! empty( $params[ 'is_json' ] ) ) {
542
- add_filter( 'litespeed_is_json', '__return_true' ) ;
543
  }
544
 
545
- Tag::add( rtrim( Tag::TYPE_ESI, '.' ) ) ;
546
- Tag::add( Tag::TYPE_ESI . LSCACHE_IS_ESI ) ;
547
 
548
  // Debug2::debug(var_export($params, true ));
549
 
@@ -553,17 +618,17 @@ class ESI extends Instance {
553
  * @since 2.2.3
554
  */
555
  if ( ! empty( $_GET[ '_control' ] ) ) {
556
- $control = explode( ',', $_GET[ '_control' ] ) ;
557
  if ( in_array( 'private', $control ) ) {
558
- Control::set_private() ;
559
  }
560
 
561
  if ( in_array( 'no-vary', $control ) ) {
562
- Control::set_no_vary() ;
563
  }
564
  }
565
 
566
- do_action('litespeed_esi_load-' . LSCACHE_IS_ESI, $params) ;
567
  }
568
 
569
  // The *_sub_* functions are helpers for the sub_* functions.
@@ -575,23 +640,22 @@ class ESI extends Instance {
575
  * @since 1.1.3
576
  * @access public
577
  */
578
- public static function widget_default_options($options, $widget)
579
- {
580
  if ( ! is_array($options) ) {
581
- return $options ;
582
  }
583
 
584
- $widget_name = get_class($widget) ;
585
  switch ($widget_name) {
586
  case 'WP_Widget_Recent_Posts' :
587
  case 'WP_Widget_Recent_Comments' :
588
- $options[self::WIDGET_O_ESIENABLE] = Base::VAL_OFF ;
589
- $options[self::WIDGET_O_TTL] = 86400 ;
590
- break ;
591
  default :
592
- break ;
593
  }
594
- return $options ;
595
  }
596
 
597
  /**
@@ -609,28 +673,28 @@ class ESI extends Instance {
609
  public function sub_widget_block( $instance, $widget, $args ) {
610
  // #210407
611
  if ( ! is_array( $instance ) ) {
612
- return $instance ;
613
  }
614
 
615
- $name = get_class( $widget ) ;
616
  if ( ! isset( $instance[ Base::OPTION_NAME ] ) ) {
617
- return $instance ;
618
  }
619
- $options = $instance[ Base::OPTION_NAME ] ;
620
  if ( ! isset( $options ) || ! $options[ self::WIDGET_O_ESIENABLE ] ) {
621
- defined( 'LSCWP_LOG' ) && Debug2::debug( 'ESI 0 ' . $name . ': '. ( ! isset( $options ) ? 'not set' : 'set off' ) ) ;
622
 
623
- return $instance ;
624
  }
625
 
626
- $esi_private = $options[ self::WIDGET_O_ESIENABLE ] == Base::VAL_ON2 ? 'private,' : '' ;
627
 
628
  $params = array(
629
  self::PARAM_NAME => $name,
630
  self::PARAM_ID => $widget->id,
631
  self::PARAM_INSTANCE => $instance,
632
  self::PARAM_ARGS => $args
633
- ) ;
634
 
635
  echo self::sub_esi_block( 'widget', 'widget ' . $name, $params, $esi_private . 'no-vary' );
636
 
@@ -645,20 +709,19 @@ class ESI extends Instance {
645
  * @since 1.1.3
646
  * @global type $wp_admin_bar
647
  */
648
- public function sub_admin_bar_block()
649
- {
650
- global $wp_admin_bar ;
651
 
652
  if ( ! is_admin_bar_showing() || ! is_object($wp_admin_bar) ) {
653
- return ;
654
  }
655
 
656
  // To make each admin bar ESI request different for `Edit` button different link
657
  $params = array(
658
  'ref' => $_SERVER[ 'REQUEST_URI' ],
659
- ) ;
660
 
661
- echo self::sub_esi_block( 'admin-bar', 'adminbar', $params ) ;
662
  }
663
 
664
  /**
@@ -669,29 +732,28 @@ class ESI extends Instance {
669
  * @global $wp_widget_factory
670
  * @param array $params Input parameters needed to correctly display widget
671
  */
672
- public function load_widget_block( $params )
673
- {
674
- // global $wp_widget_factory ;
675
- // $widget = $wp_widget_factory->widgets[ $params[ self::PARAM_NAME ] ] ;
676
- $option = $params[ self::PARAM_INSTANCE ] ;
677
- $option = $option[ Base::OPTION_NAME ] ;
678
 
679
  // Since we only reach here via esi, safe to assume setting exists.
680
- $ttl = $option[ self::WIDGET_O_TTL ] ;
681
- defined( 'LSCWP_LOG' ) && Debug2::debug( 'ESI widget render: name ' . $params[ self::PARAM_NAME ] . ', id ' . $params[ self::PARAM_ID ] . ', ttl ' . $ttl ) ;
682
  if ( $ttl == 0 ) {
683
- Control::set_nocache( 'ESI Widget time to live set to 0' ) ;
684
  }
685
  else {
686
- Control::set_custom_ttl( $ttl ) ;
687
 
688
  if ( $option[ self::WIDGET_O_ESIENABLE ] == Base::VAL_ON2 ) {
689
- Control::set_private() ;
690
  }
691
- Control::set_no_vary() ;
692
- Tag::add( Tag::TYPE_WIDGET . $params[ self::PARAM_ID ] ) ;
693
  }
694
- the_widget( $params[ self::PARAM_NAME ], $params[ self::PARAM_INSTANCE ], $params[ self::PARAM_ARGS ] ) ;
695
  }
696
 
697
  /**
@@ -700,32 +762,31 @@ class ESI extends Instance {
700
  * @access public
701
  * @since 1.1.3
702
  */
703
- public function load_admin_bar_block( $params )
704
- {
705
 
706
  if ( ! empty( $params[ 'ref' ] ) ) {
707
- $ref_qs = parse_url( $params[ 'ref' ], PHP_URL_QUERY ) ;
708
  if ( ! empty( $ref_qs ) ) {
709
- parse_str( $ref_qs, $ref_qs_arr ) ;
710
 
711
  if ( ! empty( $ref_qs_arr ) ) {
712
  foreach ( $ref_qs_arr as $k => $v ) {
713
- $_GET[ $k ] = $v ;
714
  }
715
  }
716
  }
717
  }
718
 
719
- wp_admin_bar_render() ;
720
  if ( ! Conf::val( Base::O_ESI_CACHE_ADMBAR ) ) {
721
- Control::set_nocache( 'build-in set to not cacheable' ) ;
722
  }
723
  else {
724
- Control::set_private() ;
725
- Control::set_no_vary() ;
726
  }
727
 
728
- defined( 'LSCWP_LOG' ) && Debug2::debug( 'ESI: adminbar ref: ' . $_SERVER[ 'REQUEST_URI' ] ) ;
729
  }
730
 
731
 
@@ -736,18 +797,17 @@ class ESI extends Instance {
736
  * @since 1.1.3
737
  * @param array $params Input parameters needed to correctly display comment form
738
  */
739
- public function load_comment_form_block( $params )
740
- {
741
- comment_form( $params[ self::PARAM_ARGS ], $params[ self::PARAM_ID ] ) ;
742
 
743
  if ( ! Conf::val( Base::O_ESI_CACHE_COMMFORM ) ) {
744
- Control::set_nocache( 'build-in set to not cacheable' ) ;
745
  }
746
  else {
747
  // by default comment form is public
748
  if ( Vary::has_vary() ) {
749
- Control::set_private() ;
750
- Control::set_no_vary() ;
751
  }
752
  }
753
  }
@@ -784,33 +844,32 @@ class ESI extends Instance {
784
  * @access public
785
  * @since 2.8
786
  */
787
- public function load_esi_shortcode( $params )
788
- {
789
  if ( isset( $params[ 'ttl' ] ) ) {
790
  if ( ! $params[ 'ttl' ] ) {
791
- Control::set_nocache( 'ESI shortcode att ttl=0' ) ;
792
  }
793
  else {
794
- Control::set_custom_ttl( $params[ 'ttl' ] ) ;
795
  }
796
- unset( $params[ 'ttl' ] ) ;
797
  }
798
 
799
  // Replace to original shortcode
800
- $shortcode = $params[ 0 ] ;
801
- $atts_ori = array() ;
802
  foreach ( $params as $k => $v ) {
803
  if ( $k === 0 ) {
804
- continue ;
805
  }
806
 
807
- $atts_ori[] = is_string( $k ) ? "$k='" . addslashes( $v ) . "'" : $v ;
808
  }
809
 
810
- Tag::add( Tag::TYPE_ESI . "esi.$shortcode" ) ;
811
 
812
  // Output original shortcode final content
813
- echo do_shortcode( "[$shortcode " . implode( ' ', $atts_ori ) . " ]" ) ;
814
  }
815
 
816
  /**
@@ -823,11 +882,11 @@ class ESI extends Instance {
823
  * @access public
824
  */
825
  public function register_comment_form_actions( $defaults ) {
826
- $this->esi_args = $defaults ;
827
- echo GUI::clean_wrapper_begin() ;
828
  add_filter( 'comment_form_submit_button', array( $this, 'sub_comment_form_btn' ), 1000, 2 ); // To save the params passed in
829
- add_action( 'comment_form', array( $this, 'sub_comment_form_block' ), 1000 ) ;
830
- return $defaults ;
831
  }
832
 
833
  /**
@@ -838,30 +897,30 @@ class ESI extends Instance {
838
  */
839
  public function sub_comment_form_btn( $unused, $args ) {
840
  if ( empty( $args ) || empty( $this->esi_args ) ) {
841
- Debug2::debug( 'comment form args empty?' ) ;
842
- return $unused ;
843
  }
844
- $esi_args = array() ;
845
 
846
  // compare current args with default ones
847
  foreach ( $args as $k => $v ) {
848
  if ( ! isset( $this->esi_args[ $k ] ) ) {
849
- $esi_args[ $k ] = $v ;
850
  }
851
  elseif ( is_array( $v ) ) {
852
- $diff = array_diff_assoc( $v, $this->esi_args[ $k ] ) ;
853
  if ( ! empty( $diff ) ) {
854
- $esi_args[ $k ] = $diff ;
855
  }
856
  }
857
  elseif ( $v !== $this->esi_args[ $k ] ) {
858
- $esi_args[ $k ] = $v ;
859
  }
860
  }
861
 
862
  $this->esi_args = $esi_args;
863
 
864
- return $unused ;
865
  }
866
 
867
  /**
@@ -873,15 +932,15 @@ class ESI extends Instance {
873
  * @since 1.1.3
874
  */
875
  public function sub_comment_form_block( $post_id ) {
876
- echo GUI::clean_wrapper_end() ;
877
  $params = array(
878
  self::PARAM_ID => $post_id,
879
  self::PARAM_ARGS => $this->esi_args,
880
- ) ;
881
 
882
- echo self::sub_esi_block( 'comment-form', 'comment form', $params ) ;
883
- echo GUI::clean_wrapper_begin() ;
884
- add_action( 'comment_form_after', array( $this, 'comment_form_sub_clean' ) ) ;
885
  }
886
 
887
  /**
@@ -891,9 +950,8 @@ class ESI extends Instance {
891
  * @since 1.1.3
892
  * @access public
893
  */
894
- public function comment_form_sub_clean()
895
- {
896
- echo GUI::clean_wrapper_end() ;
897
  }
898
 
899
  /**
@@ -905,6 +963,13 @@ class ESI extends Instance {
905
  public static function finalize( $buffer ) {
906
  $instance = self::get_instance();
907
 
 
 
 
 
 
 
 
908
  // Bypass if no preserved list to be replaced
909
  if ( ! $instance->_esi_preserve_list ) {
910
  return $buffer;
9
  * @subpackage LiteSpeed/src
10
  * @author LiteSpeed Technologies <info@litespeedtech.com>
11
  */
12
+ namespace LiteSpeed;
13
 
14
+ defined( 'WPINC' ) || exit;
15
 
16
  class ESI extends Instance {
17
+ protected static $_instance;
18
 
19
+ private static $has_esi = false;
20
+ private static $_combine_ids = array();
21
+ private $esi_args = null;
22
+ private $_esi_preserve_list = array();
23
  private $_nonce_actions = array( -1 => '' ); // val is cache control
24
 
25
+ const QS_ACTION = 'lsesi';
26
+ const QS_PARAMS = 'esi';
27
+ const COMBO = '__combo'; // ESI include combine='main' handler
28
 
29
+ const PARAM_ARGS = 'args';
30
+ const PARAM_ID = 'id';
31
+ const PARAM_INSTANCE = 'instance';
32
+ const PARAM_NAME = 'name';
33
 
34
+ const WIDGET_O_ESIENABLE = 'widget_esi_enable';
35
+ const WIDGET_O_TTL = 'widget_ttl';
36
 
37
  /**
38
  * Confructor of ESI
46
  * @since 2.9.7.2
47
  */
48
  if ( Router::is_ajax() || ! Router::esi_enabled() ) {
49
+ return;
50
  }
51
 
52
  // Init ESI in `after_setup_theme` hook after detected if LITESPEED_DISABLE_ALL is ON or not
53
+ add_action( 'litespeed_initing', array( $this, 'esi_init' ) );
54
 
55
  /**
56
  * Overwrite wp_create_nonce func
57
  * @since 2.9.5
58
  */
59
  if ( ! is_admin() && ! function_exists( 'wp_create_nonce' ) ) {
60
+ $this->_transform_nonce();
61
  }
62
  }
63
 
70
  * @access public
71
  */
72
  public function esi_init() {
73
+ add_action( 'template_include', array( $this, 'esi_template' ), 99999 );
74
 
75
+ add_action( 'load-widgets.php', __NAMESPACE__ . '\Purge::purge_widget' );
76
+ add_action( 'wp_update_comment_count', __NAMESPACE__ . '\Purge::purge_comment_widget' );
77
 
78
  /**
79
  * Recover REQUEST_URI
80
  * @since 1.8.1
81
  */
82
  if ( ! empty( $_GET[ self::QS_ACTION ] ) ) {
83
+ $this->_register_esi_actions();
84
  }
85
 
86
  /**
97
  * @since 2.8.1 Check is_admin for Elementor compatibility #726013
98
  */
99
  if ( ! is_admin() ) {
100
+ add_shortcode( 'esi', array( $this, 'shortcode' ) );
101
  }
102
 
103
  }
149
  *
150
  * @since 2.9.5
151
  */
152
+ public function is_nonce_action( $action ) {
 
153
  foreach ( $this->_nonce_actions as $k => $v ) {
154
  if ( strpos( $k, '*' ) !== false ) {
155
  if( preg_match( '#' . $k . '#iU', $action ) ) {
209
  * @since 2.8
210
  * @access public
211
  */
212
+ public function shortcode( $atts ) {
 
213
  if ( empty( $atts[ 0 ] ) ) {
214
+ Debug2::debug( '[ESI] ===shortcode wrong format', $atts );
215
+ return 'Wrong shortcode esi format';
216
  }
217
 
218
+ $cache = 'public,no-vary';
219
  if ( ! empty( $atts[ 'cache' ] ) ) {
220
+ $cache = $atts[ 'cache' ];
221
+ unset( $atts[ 'cache' ] );
222
  }
223
 
224
+ do_action( 'litespeed_esi_shortcode-' . $atts[ 0 ] );
225
 
226
  // Show ESI link
227
+ return self::sub_esi_block( 'esi', 'esi-shortcode', $atts, $cache );
228
  }
229
 
230
  /**
235
  * @access public
236
  * @return string Esi On header if request has esi, empty string otherwise.
237
  */
238
+ public static function has_esi() {
239
+ return self::$has_esi;
 
240
  }
241
 
242
  /**
245
  * @since 1.1.3
246
  * @access public
247
  */
248
+ public static function set_has_esi() {
249
+ self::$has_esi = true;
 
250
  }
251
 
252
  /**
257
  * @access private
258
  */
259
  private function _register_esi_actions() {
260
+ ! defined( 'LSCACHE_IS_ESI' ) && define( 'LSCACHE_IS_ESI', $_GET[ self::QS_ACTION ] );// Reused this to ESI block ID
261
 
262
+ ! empty( $_SERVER[ 'ESI_REFERER' ] ) && defined( 'LSCWP_LOG' ) && Debug2::debug( '[ESI] ESI_REFERER: ' . $_SERVER[ 'ESI_REFERER' ] );
263
 
264
  /**
265
  * Only when ESI's parent is not REST, replace REQUEST_URI to avoid breaking WP5 editor REST call
266
  * @since 2.9.3
267
  */
268
  if ( ! empty( $_SERVER[ 'ESI_REFERER' ] ) && ! REST::get_instance()->is_rest( $_SERVER[ 'ESI_REFERER' ] ) ) {
269
+ $_SERVER[ 'REQUEST_URI' ] = $_SERVER[ 'ESI_REFERER' ];
270
  }
271
 
272
  if ( ! empty( $_SERVER[ 'ESI_CONTENT_TYPE' ] ) && strpos( $_SERVER[ 'ESI_CONTENT_TYPE' ], 'application/json' ) === 0 ) {
273
+ add_filter( 'litespeed_is_json', '__return_true' );
274
  }
275
 
276
  /**
278
  * NOTE: Not effective due to ESI req are all to `/` yet
279
  * @since 2.9.4
280
  */
281
+ add_action( 'rest_api_init', array( $this, 'load_esi_block' ), 101 );
282
 
283
  // Register ESI blocks
284
+ add_action('litespeed_esi_load-widget', array($this, 'load_widget_block'));
285
+ add_action('litespeed_esi_load-admin-bar', array($this, 'load_admin_bar_block'));
286
+ add_action('litespeed_esi_load-comment-form', array($this, 'load_comment_form_block'));
287
 
288
  add_action('litespeed_esi_load-nonce', array( $this, 'load_nonce_block' ) );
289
+ add_action('litespeed_esi_load-esi', array( $this, 'load_esi_shortcode' ) );
290
+
291
+ add_action('litespeed_esi_load-' . self::COMBO, array( $this, 'load_combo' ) );
292
  }
293
 
294
  /**
300
  * @param string $template The template path filtered.
301
  * @return string The new template path.
302
  */
303
+ public function esi_template( $template ) {
 
304
  // Check if is an ESI request
305
  if ( defined( 'LSCACHE_IS_ESI' ) ) {
306
+ Debug2::debug( '[ESI] calling template' );
307
 
308
+ return LSCWP_DIR . 'tpl/esi.tpl.php';
309
  }
310
  $this->_register_not_esi_actions();
311
  return $template;
318
  * @since 1.1.3
319
  * @access private
320
  */
321
+ private function _register_not_esi_actions() {
 
322
  do_action( 'litespeed_tpl_normal' );
323
 
324
  if ( ! Control::is_cacheable() ) {
325
+ return;
326
  }
327
 
328
  if ( Router::is_ajax() ) {
329
+ return;
330
  }
331
 
332
  add_filter('widget_display_callback', array( $this, 'sub_widget_block' ), 0, 3);
333
 
334
  // Add admin_bar esi
335
  if ( Router::is_logged_in() ) {
336
+ remove_action('wp_footer', 'wp_admin_bar_render', 1000);
337
+ add_action('wp_footer', array($this, 'sub_admin_bar_block'), 1000);
338
  }
339
 
340
  // Add comment forum esi for logged-in user or commenter
341
  if ( ! Router::is_ajax() && Vary::has_vary() ) {
342
+ add_filter( 'comment_form_defaults', array( $this, 'register_comment_form_actions' ) );
343
+ }
344
+
345
+ }
346
+
347
+ /**
348
+ * Set an ESI to be combine='sub'
349
+ *
350
+ * @since 3.4.2
351
+ */
352
+ public static function combine( $block_id ) {
353
+ if ( ! isset( $_SERVER[ 'X-LSCACHE' ] ) || strpos( $_SERVER[ 'X-LSCACHE' ], 'combine' ) === false ) {
354
+ return;
355
+ }
356
+
357
+ if ( in_array( $block_id, self::$_combine_ids ) ) {
358
+ return;
359
+ }
360
+
361
+ self::$_combine_ids[] = $block_id;
362
+ }
363
+
364
+ /**
365
+ * Load combined ESI
366
+ *
367
+ * @since 3.4.2
368
+ */
369
+ public function load_combo() {
370
+ Control::set_nocache( 'ESI combine request' );
371
+
372
+ if ( empty( $_POST[ 'esi_include' ] ) ) {
373
+ return;
374
+ }
375
+
376
+ self::set_has_esi();
377
+
378
+ Debug2::debug( '[ESI] 🍔 Load combo', $_POST[ 'esi_include' ] );
379
+
380
+ $output = '';
381
+ foreach ( $_POST[ 'esi_include' ] as $url ) {
382
+ $qs = parse_url( htmlspecialchars_decode( $url ), PHP_URL_QUERY );
383
+ parse_str( $qs, $qs );
384
+ if ( empty( $qs[ self::QS_ACTION ] ) ) {
385
+ continue;
386
+ }
387
+ $esi_id = $qs[ self::QS_ACTION ];
388
+ $esi_param = ! empty( $qs[ self::QS_PARAMS ] ) ? $this->_parse_esi_param( $qs[ self::QS_PARAMS ] ) : false;
389
+ $inline_param = apply_filters( 'litespeed_esi_inline-' . $esi_id, array(), $esi_param ); // Returned array need to be [ val, control, tag ]
390
+ if ( $inline_param ) {
391
+ $output .= self::_build_inline( $url, $inline_param );
392
+ }
393
+ }
394
+
395
+ echo $output;
396
+ }
397
+
398
+ /**
399
+ * Build a whole inline segment
400
+ *
401
+ * @since 3.4.2
402
+ */
403
+ private static function _build_inline( $url, $inline_param ) {
404
+ if ( ! $url || empty( $inline_param[ 'val' ] ) || empty( $inline_param[ 'control' ] ) || empty( $inline_param[ 'tag' ] ) ) {
405
+ return '';
406
  }
407
 
408
+ return "<esi:inline name='$url' cache-control='" . $inline_param[ 'control' ] . "' cache-tag='" . $inline_param[ 'tag' ] . "'>" . $inline_param[ 'val' ] . "</esi:inline>";
409
  }
410
 
411
  /**
426
  */
427
  public static function sub_esi_block( $block_id, $wrapper, $params = array(), $control = 'private,no-vary', $silence = false, $preserved = false, $svar = false, $inline_param = array() ) {
428
  if ( empty($block_id) || ! is_array($params) || preg_match('/[^\w-]/', $block_id) ) {
429
+ return false;
430
  }
431
 
432
  if ( $silence ) {
433
  // Don't add comment to esi block ( orignal for nonce used in tag property data-nonce='esi_block' )
434
+ $params[ '_ls_silence' ] = true;
435
  }
436
 
437
  if ( REST::get_instance()->is_rest() || REST::get_instance()->is_internal_rest() ) {
438
+ $params[ 'is_json' ] = 1;
439
  }
440
 
441
  $params = apply_filters( 'litespeed_esi_params', $params, $block_id );
442
+ $control = apply_filters('litespeed_esi_control', $control, $block_id );
443
 
444
  if ( !is_array($params) || !is_string($control) ) {
445
+ defined( 'LSCWP_LOG' ) && Debug2::debug( "[ESI] 🛑 Sub hooks returned Params: \n" . var_export($params, true) . "\ncache control: \n" . var_export($control, true) );
446
 
447
+ return false;
448
  }
449
 
450
  // Build params for URL
451
  $appended_params = array(
452
  self::QS_ACTION => $block_id,
453
+ );
454
  if ( ! empty( $control ) ) {
455
+ $appended_params[ '_control' ] = $control;
456
  }
457
  if ( $params ) {
458
+ $appended_params[ self::QS_PARAMS ] = base64_encode( json_encode( $params ) );
459
  Debug2::debug2( '[ESI] param ', $params );
460
  }
461
 
462
  // Append hash
463
+ $appended_params[ '_hash' ] = self::_gen_esi_md5( $appended_params );
464
 
465
  /**
466
  * Escape potential chars
467
  * @since 2.9.4
468
  */
469
+ $appended_params = array_map( 'urlencode', $appended_params );
470
 
471
  // Generate ESI URL
472
+ $url = add_query_arg( $appended_params, trailingslashit( wp_make_link_relative( home_url() ) ) );
473
 
474
  $output = '';
475
+ if ( $inline_param ) {
476
+ $output .= self::_build_inline( $url, $inline_param );
 
 
 
 
 
 
 
 
477
  }
478
 
479
+ $output .= "<esi:include src='$url'";
480
  if ( ! empty( $control ) ) {
481
+ $output .= " cache-control='$control'";
482
  }
483
  if ( $svar ) {
484
+ $output .= " as-var='1'";
485
+ }
486
+ if ( in_array( $block_id, self::$_combine_ids ) ) {
487
+ $output .= " combine='sub'";
488
  }
489
+ if ( $block_id == self::COMBO && isset( $_SERVER[ 'X-LSCACHE' ] ) && strpos( $_SERVER[ 'X-LSCACHE' ], 'combine' ) !== false ) {
490
+ $output .= " combine='main'";
491
+ }
492
+ $output .= " />";
493
 
494
  if ( ! $silence ) {
495
+ $output = "<!-- lscwp $wrapper -->$output<!-- lscwp $wrapper esi end -->";
496
  }
497
 
498
+ Debug2::debug( "[ESI] 💕 [BLock_ID] $block_id \t[wrapper] $wrapper \t\t[Control] $control" );
499
+ Debug2::debug2( $output );
500
 
501
+ self::set_has_esi();
502
 
503
  // Convert to string to avoid html chars filter when using
504
  // Will reverse the buffer when output in self::finalize()
505
  if ( $preserved ) {
506
+ $hash = md5( $output );
507
+ self::get_instance()->_esi_preserve_list[ $hash ] = $output;
508
+ Debug2::debug( "[ESI] Preserved to $hash" );
509
 
510
+ return $hash;
511
  }
512
 
513
+ return $output;
514
  }
515
 
516
  /**
519
  * @since 2.9.6
520
  * @access private
521
  */
522
+ private static function _gen_esi_md5( $params ) {
 
523
  $keys = array(
524
  self::QS_ACTION,
525
  '_control',
526
  self::QS_PARAMS,
527
+ );
528
 
529
+ $str = '';
530
  foreach ( $keys as $v ) {
531
  if ( isset( $params[ $v ] ) && is_string( $params[ $v ] ) ) {
532
+ $str .= $params[ $v ];
533
  }
534
  }
535
+ Debug2::debug2( '[ESI] md5_string=' . $str );
536
 
537
+ return md5( Conf::val( Base::HASH ) . $str );
538
  }
539
 
540
  /**
543
  * @since 1.1.3
544
  * @access private
545
  */
546
+ private function _parse_esi_param( $qs_params = false ) {
547
+ $req_params = false;
548
+ if ( $qs_params ) {
549
+ $req_params = $qs_params;
550
+ }
551
+ elseif ( isset( $_REQUEST[ self::QS_PARAMS ] ) ) {
552
+ $req_params = $_REQUEST[ self::QS_PARAMS ];
553
  }
554
+
555
+ if ( ! $req_params ) {
556
+ return false;
557
+ }
558
+
559
+ $unencrypted = base64_decode( $req_params );
560
  if ( $unencrypted === false ) {
561
+ return false;
562
  }
563
 
564
+ Debug2::debug2( '[ESI] parms', $unencrypted );
565
+ // $unencoded = urldecode($unencrypted); no need to do this as $_GET is already parsed
566
+ $params = json_decode( $unencrypted, true );
567
 
568
+ return $params;
569
  }
570
 
571
  /**
574
  * @since 1.1.3
575
  * @access public
576
  */
577
+ public function load_esi_block() {
 
578
  /**
579
  * Validate if is a legal ESI req
580
  * @since 2.9.6
581
  */
582
  if ( empty( $_GET[ '_hash' ] ) || self::_gen_esi_md5( $_GET ) != $_GET[ '_hash' ] ) {
583
+ Debug2::debug( '[ESI] ❌ Failed to validate _hash' );
584
+ return;
585
  }
586
 
587
+ $params = $this->_parse_esi_param();
588
 
589
  if ( defined( 'LSCWP_LOG' ) ) {
590
+ $logInfo = '[ESI] ⭕ ';
591
  if( ! empty( $params[ self::PARAM_NAME ] ) ) {
592
+ $logInfo .= ' Name: ' . $params[ self::PARAM_NAME ] . ' ----- ';
593
  }
594
+ $logInfo .= ' [ID] ' . LSCACHE_IS_ESI;
595
+ Debug2::debug( $logInfo );
596
  }
597
 
598
  if ( ! empty( $params[ '_ls_silence' ] ) ) {
599
+ ! defined( 'LSCACHE_ESI_SILENCE' ) && define( 'LSCACHE_ESI_SILENCE', true );
600
  }
601
 
602
  /**
604
  * @since 2.9.4
605
  */
606
  if ( ! empty( $params[ 'is_json' ] ) ) {
607
+ add_filter( 'litespeed_is_json', '__return_true' );
608
  }
609
 
610
+ Tag::add( rtrim( Tag::TYPE_ESI, '.' ) );
611
+ Tag::add( Tag::TYPE_ESI . LSCACHE_IS_ESI );
612
 
613
  // Debug2::debug(var_export($params, true ));
614
 
618
  * @since 2.2.3
619
  */
620
  if ( ! empty( $_GET[ '_control' ] ) ) {
621
+ $control = explode( ',', $_GET[ '_control' ] );
622
  if ( in_array( 'private', $control ) ) {
623
+ Control::set_private();
624
  }
625
 
626
  if ( in_array( 'no-vary', $control ) ) {
627
+ Control::set_no_vary();
628
  }
629
  }
630
 
631
+ do_action('litespeed_esi_load-' . LSCACHE_IS_ESI, $params);
632
  }
633
 
634
  // The *_sub_* functions are helpers for the sub_* functions.
640
  * @since 1.1.3
641
  * @access public
642
  */
643
+ public static function widget_default_options($options, $widget) {
 
644
  if ( ! is_array($options) ) {
645
+ return $options;
646
  }
647
 
648
+ $widget_name = get_class($widget);
649
  switch ($widget_name) {
650
  case 'WP_Widget_Recent_Posts' :
651
  case 'WP_Widget_Recent_Comments' :
652
+ $options[self::WIDGET_O_ESIENABLE] = Base::VAL_OFF;
653
+ $options[self::WIDGET_O_TTL] = 86400;
654
+ break;
655
  default :
656
+ break;
657
  }
658
+ return $options;
659
  }
660
 
661
  /**
673
  public function sub_widget_block( $instance, $widget, $args ) {
674
  // #210407
675
  if ( ! is_array( $instance ) ) {
676
+ return $instance;
677
  }
678
 
679
+ $name = get_class( $widget );
680
  if ( ! isset( $instance[ Base::OPTION_NAME ] ) ) {
681
+ return $instance;
682
  }
683
+ $options = $instance[ Base::OPTION_NAME ];
684
  if ( ! isset( $options ) || ! $options[ self::WIDGET_O_ESIENABLE ] ) {
685
+ defined( 'LSCWP_LOG' ) && Debug2::debug( 'ESI 0 ' . $name . ': '. ( ! isset( $options ) ? 'not set' : 'set off' ) );
686
 
687
+ return $instance;
688
  }
689
 
690
+ $esi_private = $options[ self::WIDGET_O_ESIENABLE ] == Base::VAL_ON2 ? 'private,' : '';
691
 
692
  $params = array(
693
  self::PARAM_NAME => $name,
694
  self::PARAM_ID => $widget->id,
695
  self::PARAM_INSTANCE => $instance,
696
  self::PARAM_ARGS => $args
697
+ );
698
 
699
  echo self::sub_esi_block( 'widget', 'widget ' . $name, $params, $esi_private . 'no-vary' );
700
 
709
  * @since 1.1.3
710
  * @global type $wp_admin_bar
711
  */
712
+ public function sub_admin_bar_block() {
713
+ global $wp_admin_bar;
 
714
 
715
  if ( ! is_admin_bar_showing() || ! is_object($wp_admin_bar) ) {
716
+ return;
717
  }
718
 
719
  // To make each admin bar ESI request different for `Edit` button different link
720
  $params = array(
721
  'ref' => $_SERVER[ 'REQUEST_URI' ],
722
+ );
723
 
724
+ echo self::sub_esi_block( 'admin-bar', 'adminbar', $params );
725
  }
726
 
727
  /**
732
  * @global $wp_widget_factory
733
  * @param array $params Input parameters needed to correctly display widget
734
  */
735
+ public function load_widget_block( $params ) {
736
+ // global $wp_widget_factory;
737
+ // $widget = $wp_widget_factory->widgets[ $params[ self::PARAM_NAME ] ];
738
+ $option = $params[ self::PARAM_INSTANCE ];
739
+ $option = $option[ Base::OPTION_NAME ];
 
740
 
741
  // Since we only reach here via esi, safe to assume setting exists.
742
+ $ttl = $option[ self::WIDGET_O_TTL ];
743
+ defined( 'LSCWP_LOG' ) && Debug2::debug( 'ESI widget render: name ' . $params[ self::PARAM_NAME ] . ', id ' . $params[ self::PARAM_ID ] . ', ttl ' . $ttl );
744
  if ( $ttl == 0 ) {
745
+ Control::set_nocache( 'ESI Widget time to live set to 0' );
746
  }
747
  else {
748
+ Control::set_custom_ttl( $ttl );
749
 
750
  if ( $option[ self::WIDGET_O_ESIENABLE ] == Base::VAL_ON2 ) {
751
+ Control::set_private();
752
  }
753
+ Control::set_no_vary();
754
+ Tag::add( Tag::TYPE_WIDGET . $params[ self::PARAM_ID ] );
755
  }
756
+ the_widget( $params[ self::PARAM_NAME ], $params[ self::PARAM_INSTANCE ], $params[ self::PARAM_ARGS ] );
757
  }
758
 
759
  /**
762
  * @access public
763
  * @since 1.1.3
764
  */
765
+ public function load_admin_bar_block( $params ) {
 
766
 
767
  if ( ! empty( $params[ 'ref' ] ) ) {
768
+ $ref_qs = parse_url( $params[ 'ref' ], PHP_URL_QUERY );
769
  if ( ! empty( $ref_qs ) ) {
770
+ parse_str( $ref_qs, $ref_qs_arr );
771
 
772
  if ( ! empty( $ref_qs_arr ) ) {
773
  foreach ( $ref_qs_arr as $k => $v ) {
774
+ $_GET[ $k ] = $v;
775
  }
776
  }
777
  }
778
  }
779
 
780
+ wp_admin_bar_render();
781
  if ( ! Conf::val( Base::O_ESI_CACHE_ADMBAR ) ) {
782
+ Control::set_nocache( 'build-in set to not cacheable' );
783
  }
784
  else {
785
+ Control::set_private();
786
+ Control::set_no_vary();
787
  }
788
 
789
+ defined( 'LSCWP_LOG' ) && Debug2::debug( 'ESI: adminbar ref: ' . $_SERVER[ 'REQUEST_URI' ] );
790
  }
791
 
792
 
797
  * @since 1.1.3
798
  * @param array $params Input parameters needed to correctly display comment form
799
  */
800
+ public function load_comment_form_block( $params ) {
801
+ comment_form( $params[ self::PARAM_ARGS ], $params[ self::PARAM_ID ] );
 
802
 
803
  if ( ! Conf::val( Base::O_ESI_CACHE_COMMFORM ) ) {
804
+ Control::set_nocache( 'build-in set to not cacheable' );
805
  }
806
  else {
807
  // by default comment form is public
808
  if ( Vary::has_vary() ) {
809
+ Control::set_private();
810
+ Control::set_no_vary();
811
  }
812
  }
813
  }
844
  * @access public
845
  * @since 2.8
846
  */
847
+ public function load_esi_shortcode( $params ) {
 
848
  if ( isset( $params[ 'ttl' ] ) ) {
849
  if ( ! $params[ 'ttl' ] ) {
850
+ Control::set_nocache( 'ESI shortcode att ttl=0' );
851
  }
852
  else {
853
+ Control::set_custom_ttl( $params[ 'ttl' ] );
854
  }
855
+ unset( $params[ 'ttl' ] );
856
  }
857
 
858
  // Replace to original shortcode
859
+ $shortcode = $params[ 0 ];
860
+ $atts_ori = array();
861
  foreach ( $params as $k => $v ) {
862
  if ( $k === 0 ) {
863
+ continue;
864
  }
865
 
866
+ $atts_ori[] = is_string( $k ) ? "$k='" . addslashes( $v ) . "'" : $v;
867
  }
868
 
869
+ Tag::add( Tag::TYPE_ESI . "esi.$shortcode" );
870
 
871
  // Output original shortcode final content
872
+ echo do_shortcode( "[$shortcode " . implode( ' ', $atts_ori ) . " ]" );
873
  }
874
 
875
  /**
882
  * @access public
883
  */
884
  public function register_comment_form_actions( $defaults ) {
885
+ $this->esi_args = $defaults;
886
+ echo GUI::clean_wrapper_begin();
887
  add_filter( 'comment_form_submit_button', array( $this, 'sub_comment_form_btn' ), 1000, 2 ); // To save the params passed in
888
+ add_action( 'comment_form', array( $this, 'sub_comment_form_block' ), 1000 );
889
+ return $defaults;
890
  }
891
 
892
  /**
897
  */
898
  public function sub_comment_form_btn( $unused, $args ) {
899
  if ( empty( $args ) || empty( $this->esi_args ) ) {
900
+ Debug2::debug( 'comment form args empty?' );
901
+ return $unused;
902
  }
903
+ $esi_args = array();
904
 
905
  // compare current args with default ones
906
  foreach ( $args as $k => $v ) {
907
  if ( ! isset( $this->esi_args[ $k ] ) ) {
908
+ $esi_args[ $k ] = $v;
909
  }
910
  elseif ( is_array( $v ) ) {
911
+ $diff = array_diff_assoc( $v, $this->esi_args[ $k ] );
912
  if ( ! empty( $diff ) ) {
913
+ $esi_args[ $k ] = $diff;
914
  }
915
  }
916
  elseif ( $v !== $this->esi_args[ $k ] ) {
917
+ $esi_args[ $k ] = $v;
918
  }
919
  }
920
 
921
  $this->esi_args = $esi_args;
922
 
923
+ return $unused;
924
  }
925
 
926
  /**
932
  * @since 1.1.3
933
  */
934
  public function sub_comment_form_block( $post_id ) {
935
+ echo GUI::clean_wrapper_end();
936
  $params = array(
937
  self::PARAM_ID => $post_id,
938
  self::PARAM_ARGS => $this->esi_args,
939
+ );
940
 
941
+ echo self::sub_esi_block( 'comment-form', 'comment form', $params );
942
+ echo GUI::clean_wrapper_begin();
943
+ add_action( 'comment_form_after', array( $this, 'comment_form_sub_clean' ) );
944
  }
945
 
946
  /**
950
  * @since 1.1.3
951
  * @access public
952
  */
953
+ public function comment_form_sub_clean() {
954
+ echo GUI::clean_wrapper_end();
 
955
  }
956
 
957
  /**
963
  public static function finalize( $buffer ) {
964
  $instance = self::get_instance();
965
 
966
+ // Prepend combo esi block
967
+ if ( self::$_combine_ids ) {
968
+ Debug2::debug( '[ESI] 🍔 Enabled combo' );
969
+ $esi_block = self::sub_esi_block( self::COMBO, '__COMBINE_MAIN__', array(), 'no-cache', true );
970
+ $buffer = $esi_block . $buffer;
971
+ }
972
+
973
  // Bypass if no preserved list to be replaced
974
  if ( ! $instance->_esi_preserve_list ) {
975
  return $buffer;
thirdparty/yith-wishlist.cls.php CHANGED
@@ -30,6 +30,7 @@ class Yith_Wishlist {
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' );
@@ -95,11 +96,14 @@ class Yith_Wishlist {
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
 
@@ -120,4 +124,33 @@ class Yith_Wishlist {
120
  do_action( 'litespeed_vary_no' );
121
  }
122
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
123
  }
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
+ add_filter( 'litespeed_esi_inline-yith_wcwl_add', __CLASS__ . '::inline_add_to_wishlist', 20, 2 );
34
 
35
  // hook to add/delete wishlist
36
  add_action( 'yith_wcwl_added_to_wishlist', __CLASS__ . '::purge' );
96
  $inline_tags = implode( ',', array_map( function($val){ return 'public:' . LSWCP_TAG_PREFIX . '_' . $val; }, $inline_tags ) );
97
  $inline_tags .= ',' . LSWCP_TAG_PREFIX . '_tag_priv';
98
 
99
+ do_action( 'litespeed_esi_combine', 'yith_wcwl_add' );
100
+
101
  $inline_params = array(
102
  'val' => $template,
103
  'tag' => $inline_tags,
104
  'control' => 'private,no-vary,max-age=' . Conf::val( Base::O_CACHE_TTL_PRIV ),
105
  );
106
+
107
  return apply_filters( 'litespeed_esi_url', 'yith_wcwl_add', 'YITH ADD TO WISHLIST', $params, 'private,no-vary', false, false, false, $inline_params );
108
  }
109
 
124
  do_action( 'litespeed_vary_no' );
125
  }
126
 
127
+ /**
128
+ * Generate ESI inline value
129
+ *
130
+ * @since 3.4.2
131
+ */
132
+ public static function inline_add_to_wishlist( $res, $params ) {
133
+ if ( ! is_array( $res ) ) {
134
+ $res = array();
135
+ }
136
+
137
+ $pid = $params[ self::ESI_PARAM_POSTID ];
138
+
139
+ $res[ 'val' ] = \YITH_WCWL_Shortcode::add_to_wishlist( array( 'product_id' => $pid ) );
140
+
141
+ $res[ 'control' ] = 'private,no-vary,max-age=' . Conf::val( Base::O_CACHE_TTL_PRIV );
142
+
143
+ $inline_tags = array(
144
+ '',
145
+ rtrim( Tag::TYPE_ESI, '.' ),
146
+ Tag::TYPE_ESI . 'yith_wcwl_add',
147
+ );
148
+ $inline_tags = implode( ',', array_map( function( $val ) { return 'public:' . LSWCP_TAG_PREFIX . '_' . $val; }, $inline_tags ) );
149
+ $inline_tags .= ',' . LSWCP_TAG_PREFIX . '_tag_priv';
150
+
151
+ $res[ 'tag' ] = $inline_tags;
152
+
153
+ return $res;
154
+ }
155
+
156
  }