LiteSpeed Cache - Version 4.4.1

Version Description

  • Sep 16 2021 =
  • ESI Fixed ESI failure on non-cached pages caused by DONOTCACHEPAGE constant.
  • Page Optimize Fixed an issue where the minified CSS/JS file failed to update when the file was changed. (ceap80)
  • Page Optimize Fixed an issue where the combined CSS/JS file randomly returned a 404 error when visiting the same URL with different query strings. (Abe)
  • API Added litespeed_const_DONOTCACHEPAGE hook to control the cache-or-not result of the DONOTCACHEPAGE constant.
Download this release

Release Info

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

Code changes from version 4.4 to 4.4.1

assets/js/litespeed-cache-admin.js CHANGED
@@ -167,6 +167,7 @@ var _litespeed_dots ;
167
  xhr.setRequestHeader( 'X-WP-Nonce', litespeed_data.nonce ) ;
168
  },
169
  success: function( data ) {
 
170
  $( '#litespeed_server_ip' ).html( data ) ;
171
  }
172
  } ) ;
167
  xhr.setRequestHeader( 'X-WP-Nonce', litespeed_data.nonce ) ;
168
  },
169
  success: function( data ) {
170
+ console.log( '[litespeed] get server IP response: ' + data );
171
  $( '#litespeed_server_ip' ).html( data ) ;
172
  }
173
  } ) ;
data/esi.nonces.txt CHANGED
@@ -29,6 +29,9 @@
29
 
30
  ## Predefined list of ESI nonces:
31
 
 
 
 
32
  # CM Registration Pro
33
  cmreg_registration_nonce private
34
  role_nonce private
29
 
30
  ## Predefined list of ESI nonces:
31
 
32
+ # WordPress REST nonce
33
+ wp_rest
34
+
35
  # CM Registration Pro
36
  cmreg_registration_nonce private
37
  role_nonce private
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: 4.4
7
  * Author: LiteSpeed Technologies
8
  * Author URI: https://www.litespeedtech.com
9
  * License: GPLv3
@@ -33,7 +33,7 @@ if ( defined( 'LSCWP_V' ) ) {
33
  return;
34
  }
35
 
36
- ! defined( 'LSCWP_V' ) && define( 'LSCWP_V', '4.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: 4.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', '4.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, core web vitals, seo, speed, image optimize, compress, object cache, redis, memcached, database cleaner
4
  Requires at least: 4.0
5
  Tested up to: 5.7.2
6
- Stable tag: 4.4
7
  License: GPLv3
8
  License URI: http://www.gnu.org/licenses/gpl.html
9
 
@@ -249,6 +249,12 @@ The vast majority of plugins and themes are compatible with LiteSpeed Cache. The
249
 
250
  == Changelog ==
251
 
 
 
 
 
 
 
252
  = 4.4 - Sep 8 2021 =
253
  * 🌱**Crawler** Added the ability to enable or disable specific crawlers. (⭐ Contributed by Astrid Wang #PR390)
254
  * 🌱**UCSS** Added `UCSS Inline` option. (Ankit).
3
  Tags: caching, optimize, performance, pagespeed, core web vitals, seo, speed, image optimize, compress, object cache, redis, memcached, database cleaner
4
  Requires at least: 4.0
5
  Tested up to: 5.7.2
6
+ Stable tag: 4.4.1
7
  License: GPLv3
8
  License URI: http://www.gnu.org/licenses/gpl.html
9
 
249
 
250
  == Changelog ==
251
 
252
+ = 4.4.1 - Sep 16 2021 =
253
+ * 🐞**ESI** Fixed ESI failure on non-cached pages caused by `DONOTCACHEPAGE` constant.
254
+ * 🐞**Page Optimize** Fixed an issue where the minified CSS/JS file failed to update when the file was changed. (ceap80)
255
+ * 🐞**Page Optimize** Fixed an issue where the combined CSS/JS file randomly returned a 404 error when visiting the same URL with different query strings. (Abe)
256
+ * **API** Added `litespeed_const_DONOTCACHEPAGE` hook to control the cache-or-not result of the `DONOTCACHEPAGE` constant.
257
+
258
  = 4.4 - Sep 8 2021 =
259
  * 🌱**Crawler** Added the ability to enable or disable specific crawlers. (⭐ Contributed by Astrid Wang #PR390)
260
  * 🌱**UCSS** Added `UCSS Inline` option. (Ankit).
src/cloud.cls.php CHANGED
@@ -532,7 +532,7 @@ class Cloud extends Base {
532
  if ( $service_tag == self::SVC_IMG_OPTM . '-' . Img_Optm::TYPE_NEW_REQ ) {
533
  $timestamp_tag = 'last_request.';
534
  if ( $this->has_pkg( self::SVC_IMG_OPTM, self::BM_IMG_OPTM_PRIO ) ) {
535
- $expiration_req /= 3;
536
  }
537
  }
538
  else {
532
  if ( $service_tag == self::SVC_IMG_OPTM . '-' . Img_Optm::TYPE_NEW_REQ ) {
533
  $timestamp_tag = 'last_request.';
534
  if ( $this->has_pkg( self::SVC_IMG_OPTM, self::BM_IMG_OPTM_PRIO ) ) {
535
+ $expiration_req /= 10;
536
  }
537
  }
538
  else {
src/control.cls.php CHANGED
@@ -530,10 +530,16 @@ class Control extends Root {
530
  * @return string empty string if empty, otherwise the cache control header.
531
  */
532
  public function output() {
 
 
 
 
 
533
  $hdr = self::X_HEADER . ': ';
534
 
535
- if ( defined( 'DONOTCACHEPAGE' ) && DONOTCACHEPAGE ) {
536
- $hdr .= 'no-cache';
 
537
  return $hdr;
538
  }
539
 
@@ -541,9 +547,11 @@ class Control extends Root {
541
  if ( defined( 'LITESPEED_GUEST' ) && LITESPEED_GUEST ) {
542
  // If is POST, no cache
543
  if ( defined( 'LSCACHE_NO_CACHE' ) && LSCACHE_NO_CACHE ) {
 
544
  $hdr .= 'no-cache';
545
  }
546
  else if( $_SERVER[ 'REQUEST_METHOD' ] !== 'GET' ) {
 
547
  $hdr .= 'no-cache';
548
  }
549
  else {
@@ -551,14 +559,12 @@ class Control extends Root {
551
  $hdr .= ',max-age=' . $this->get_ttl();
552
  }
553
 
 
 
554
  return $hdr;
555
  }
556
 
557
- $esi_hdr = '';
558
  // Fix cli `uninstall --deactivate` fatal err
559
- if ( ESI::has_esi() ) {
560
- $esi_hdr = ',esi=on';
561
- }
562
 
563
  if ( ! self::is_cacheable() ) {
564
  $hdr .= 'no-cache' . $esi_hdr;
530
  * @return string empty string if empty, otherwise the cache control header.
531
  */
532
  public function output() {
533
+ $esi_hdr = '';
534
+ if ( ESI::has_esi() ) {
535
+ $esi_hdr = ',esi=on';
536
+ }
537
+
538
  $hdr = self::X_HEADER . ': ';
539
 
540
+ if ( defined( 'DONOTCACHEPAGE' ) && apply_filters( 'litespeed_const_DONOTCACHEPAGE', DONOTCACHEPAGE ) ) {
541
+ Debug2::debug( "[Ctrl] ❌ forced no cache [reason] DONOTCACHEPAGE const" );
542
+ $hdr .= 'no-cache' . $esi_hdr;
543
  return $hdr;
544
  }
545
 
547
  if ( defined( 'LITESPEED_GUEST' ) && LITESPEED_GUEST ) {
548
  // If is POST, no cache
549
  if ( defined( 'LSCACHE_NO_CACHE' ) && LSCACHE_NO_CACHE ) {
550
+ Debug2::debug( "[Ctrl] ❌ forced no cache [reason] LSCACHE_NO_CACHE const" );
551
  $hdr .= 'no-cache';
552
  }
553
  else if( $_SERVER[ 'REQUEST_METHOD' ] !== 'GET' ) {
554
+ Debug2::debug( "[Ctrl] ❌ forced no cache [reason] req not GET" );
555
  $hdr .= 'no-cache';
556
  }
557
  else {
559
  $hdr .= ',max-age=' . $this->get_ttl();
560
  }
561
 
562
+ $hdr .= $esi_hdr;
563
+
564
  return $hdr;
565
  }
566
 
 
567
  // Fix cli `uninstall --deactivate` fatal err
 
 
 
568
 
569
  if ( ! self::is_cacheable() ) {
570
  $hdr .= 'no-cache' . $esi_hdr;
src/core.cls.php CHANGED
@@ -84,6 +84,11 @@ class Core extends Root {
84
  Debug2::debug( '[Core] Purge Queue found&sent: ' . $purge_queue );
85
  Purge::delete_option( Purge::DB_QUEUE );
86
  }
 
 
 
 
 
87
 
88
  /**
89
  * Hook internal REST
84
  Debug2::debug( '[Core] Purge Queue found&sent: ' . $purge_queue );
85
  Purge::delete_option( Purge::DB_QUEUE );
86
  }
87
+ if ( $purge_queue = Purge::get_option( Purge::DB_QUEUE2 ) ) {
88
+ @header( $purge_queue );
89
+ Debug2::debug( '[Core] Purge2 Queue found&sent: ' . $purge_queue );
90
+ Purge::delete_option( Purge::DB_QUEUE2 );
91
+ }
92
 
93
  /**
94
  * Hook internal REST
src/css.cls.php CHANGED
@@ -224,7 +224,7 @@ class CSS extends Base {
224
  return false;
225
  }
226
 
227
- return $filepath_prefix . $filename . '.css';
228
  }
229
  }
230
 
224
  return false;
225
  }
226
 
227
+ return $filename . '.css';
228
  }
229
  }
230
 
src/data.cls.php CHANGED
@@ -482,12 +482,20 @@ class Data extends Root {
482
  $q = "UPDATE `$tb_url_file` SET filename=%s WHERE id=%d";
483
  $wpdb->query( $wpdb->prepare( $q, array( $filecon_md5, $file_row[ 'id' ] ) ) );
484
 
 
 
 
485
  // Check if has other records used this file or not
486
  $file_to_del = $path . '/' . $file_row[ 'filename' ] . '.' . ( $file_type == 'js' ? 'js' : 'css' );
 
487
  $q = "SELECT id FROM `$tb_url_file` WHERE filename = %s LIMIT 1";
488
  if ( file_exists( $file_to_del ) && ! $wpdb->get_var( $wpdb->prepare( $q, $file_row[ 'filename' ] ) ) ) {
489
  // Safe to delete
490
  Debug2::debug( '[Data] Delete no more used file ' . $file_to_del );
 
 
 
 
491
  unlink( $file_to_del );
492
  }
493
  }
482
  $q = "UPDATE `$tb_url_file` SET filename=%s WHERE id=%d";
483
  $wpdb->query( $wpdb->prepare( $q, array( $filecon_md5, $file_row[ 'id' ] ) ) );
484
 
485
+ // Purge this URL to avoid cache copy of same URL w/ diff QS
486
+ Purge::purge_url( $request_url, true );
487
+
488
  // Check if has other records used this file or not
489
  $file_to_del = $path . '/' . $file_row[ 'filename' ] . '.' . ( $file_type == 'js' ? 'js' : 'css' );
490
+
491
  $q = "SELECT id FROM `$tb_url_file` WHERE filename = %s LIMIT 1";
492
  if ( file_exists( $file_to_del ) && ! $wpdb->get_var( $wpdb->prepare( $q, $file_row[ 'filename' ] ) ) ) {
493
  // Safe to delete
494
  Debug2::debug( '[Data] Delete no more used file ' . $file_to_del );
495
+
496
+ // Clear related lscache first to avoid cache copy of same URL w/ diff QS
497
+ // Purge::add( Tag::TYPE_MIN . '.' . $file_row[ 'filename' ] . '.' . $file_type );
498
+
499
  unlink( $file_to_del );
500
  }
501
  }
src/optimize.cls.php CHANGED
@@ -289,9 +289,10 @@ class Optimize extends Base {
289
  if ( $this->cfg_css_comb ) {
290
  // Check if has inline UCSS enabled or not
291
  if ( ( defined( 'LITESPEED_GUEST_OPTM' ) || $this->conf( self::O_OPTM_UCSS ) ) && $this->conf( self::O_OPTM_UCSS_INLINE ) ) {
292
- $final_file_path = $this->cls( 'CSS' )->load_ucss( $this->_request_url, true );
293
- if ( $final_file_path ) {
294
- $this->_ucss = File::read( LITESPEED_STATIC_DIR . $final_file_path );
 
295
 
296
  // Drop all css
297
  $this->content = str_replace( $html_list, '', $this->content );
@@ -757,10 +758,9 @@ class Optimize extends Base {
757
  $static_file = LITESPEED_STATIC_DIR . '/' . $filename;
758
  File::save( $static_file, $content, true );
759
 
760
- // $qs_hash = substr( md5( $src ), -5 );
761
- // return LITESPEED_STATIC_URL . "/$filename?ver=$qs_hash";
762
- // $qs_hash is from src, same as $filename, redundant
763
- return LITESPEED_STATIC_URL . "/$filename";
764
  }
765
 
766
  /**
@@ -783,15 +783,21 @@ class Optimize extends Base {
783
  }
784
 
785
  $minify = $file_type === 'css' ? $this->cfg_css_min : $this->cfg_js_min;
786
- $file_path = $this->__optimizer->serve( $this->_request_url, $file_type, $minify, $src_list );
787
 
788
- if ( ! $file_path ) {
789
  return false; // Failed to generate
790
  }
791
 
 
 
 
 
 
792
  $qs_hash = substr( md5( self::get_option( self::ITEM_TIMESTAMP_PURGE_CSS ) ), -5 );
793
  // As filename is alreay realted to filecon md5, no need QS anymore
794
- return LITESPEED_STATIC_URL . $file_path . '?ver=' . $qs_hash;
 
795
  }
796
 
797
  /**
289
  if ( $this->cfg_css_comb ) {
290
  // Check if has inline UCSS enabled or not
291
  if ( ( defined( 'LITESPEED_GUEST_OPTM' ) || $this->conf( self::O_OPTM_UCSS ) ) && $this->conf( self::O_OPTM_UCSS_INLINE ) ) {
292
+ $filename = $this->cls( 'CSS' )->load_ucss( $this->_request_url, true );
293
+ if ( $filename ) {
294
+ $filepath_prefix = $this->_build_filepath_prefix( 'ucss' );
295
+ $this->_ucss = File::read( LITESPEED_STATIC_DIR . $filepath_prefix . $filename );
296
 
297
  // Drop all css
298
  $this->content = str_replace( $html_list, '', $this->content );
758
  $static_file = LITESPEED_STATIC_DIR . '/' . $filename;
759
  File::save( $static_file, $content, true );
760
 
761
+ // QS is required as $src may contains version info
762
+ $qs_hash = substr( md5( $src ), -5 );
763
+ return LITESPEED_STATIC_URL . "/$filename?ver=$qs_hash";
 
764
  }
765
 
766
  /**
783
  }
784
 
785
  $minify = $file_type === 'css' ? $this->cfg_css_min : $this->cfg_js_min;
786
+ $filename_info = $this->__optimizer->serve( $this->_request_url, $file_type, $minify, $src_list );
787
 
788
+ if ( ! $filename_info ) {
789
  return false; // Failed to generate
790
  }
791
 
792
+ list( $filename, $type ) = $filename_info;
793
+
794
+ // Add cache tag in case later file deleted to avoid lscache served stale non-existed files @since 4.4.1
795
+ Tag::add( Tag::TYPE_MIN . '.' . $filename );
796
+
797
  $qs_hash = substr( md5( self::get_option( self::ITEM_TIMESTAMP_PURGE_CSS ) ), -5 );
798
  // As filename is alreay realted to filecon md5, no need QS anymore
799
+ $filepath_prefix = $this->_build_filepath_prefix( $type );
800
+ return LITESPEED_STATIC_URL . $filepath_prefix . $filename . '?ver=' . $qs_hash;
801
  }
802
 
803
  /**
src/optimizer.cls.php CHANGED
@@ -66,19 +66,17 @@ class Optimizer extends Root {
66
  if ( $file_type == 'css' ) {
67
  $content = false;
68
  if ( defined( 'LITESPEED_GUEST_OPTM' ) || $this->conf( Base::O_OPTM_UCSS ) ) {
69
- $final_file_path = $this->cls( 'CSS' )->load_ucss( $request_url );
70
 
71
- if ( $final_file_path ) {
72
- return $final_file_path;
73
  }
74
  }
75
  }
76
 
77
  // Before generated, don't know the contented hash filename yet, so used url hash as tmp filename
78
- $file_path_prefix = '/' . $file_type . '/';
79
- if ( is_multisite() ) {
80
- $file_path_prefix .= get_current_blog_id() . '/';
81
- }
82
  $static_file = LITESPEED_STATIC_DIR . $file_path_prefix . ( is_404() ? '404' : md5( $request_url ) ) . '.' . $file_type;
83
 
84
  // Create tmp file to avoid conflict
@@ -127,7 +125,7 @@ class Optimizer extends Root {
127
  Debug2::debug2( "[Optmer] Save URL to file for [file_type] $file_type [file] $filecon_md5 [vary] $vary " );
128
  $this->cls( 'Data' )->save_url( is_404() ? '404' : $request_url, $vary, $file_type, $filecon_md5, dirname( $realfile ) );
129
 
130
- return $final_file_path;
131
  }
132
 
133
  /**
66
  if ( $file_type == 'css' ) {
67
  $content = false;
68
  if ( defined( 'LITESPEED_GUEST_OPTM' ) || $this->conf( Base::O_OPTM_UCSS ) ) {
69
+ $filename = $this->cls( 'CSS' )->load_ucss( $request_url );
70
 
71
+ if ( $filename ) {
72
+ return array( $filename, 'ucss' );
73
  }
74
  }
75
  }
76
 
77
  // Before generated, don't know the contented hash filename yet, so used url hash as tmp filename
78
+ $file_path_prefix = $this->_build_filepath_prefix( $file_type );
79
+
 
 
80
  $static_file = LITESPEED_STATIC_DIR . $file_path_prefix . ( is_404() ? '404' : md5( $request_url ) ) . '.' . $file_type;
81
 
82
  // Create tmp file to avoid conflict
125
  Debug2::debug2( "[Optmer] Save URL to file for [file_type] $file_type [file] $filecon_md5 [vary] $vary " );
126
  $this->cls( 'Data' )->save_url( is_404() ? '404' : $request_url, $vary, $file_type, $filecon_md5, dirname( $realfile ) );
127
 
128
+ return array( $filecon_md5 . '.' . $file_type, $file_type );
129
  }
130
 
131
  /**
src/purge.cls.php CHANGED
@@ -11,13 +11,16 @@ defined( 'WPINC' ) || exit;
11
 
12
  class Purge extends Base {
13
  protected $_pub_purge = array();
 
14
  protected $_priv_purge = array();
15
  protected $_purge_related = false;
16
  protected $_purge_single = false;
17
 
18
 
19
  const X_HEADER = 'X-LiteSpeed-Purge';
 
20
  const DB_QUEUE = 'queue';
 
21
 
22
  const TYPE_PURGE_ALL = 'purge_all';
23
  const TYPE_PURGE_ALL_LSCACHE = 'purge_all_lscache';
@@ -382,8 +385,8 @@ class Purge extends Base {
382
  * @access public
383
  * @param mixed $tags Tags to add to the list.
384
  */
385
- public static function add( $tags ) {
386
- self::cls()->_add( $tags );
387
  }
388
 
389
  /**
@@ -392,26 +395,32 @@ class Purge extends Base {
392
  * @since 2.2
393
  * @access private
394
  */
395
- private function _add( $tags ) {
396
  if ( ! is_array( $tags ) ) {
397
  $tags = array( $tags );
398
  }
399
 
400
  $tags = $this->_prepend_bid( $tags );
401
 
402
- if ( ! array_diff( $tags, $this->_pub_purge ) ) {
403
  return;
404
  }
405
 
406
- $this->_pub_purge = array_merge( $this->_pub_purge, $tags );
407
- $this->_pub_purge = array_unique( $this->_pub_purge );
408
- Debug2::debug( '[Purge] added ' . implode( ',', $tags ), 8 );
 
 
 
 
 
 
409
 
410
  // Send purge header immediately
411
- $curr_built = $this->_build();
412
  if ( defined( 'LITESPEED_DID_send_headers' ) || defined( 'LITESPEED_CLI' ) ) {
413
  // Can't send, already has output, need to save and wait for next run
414
- self::update_option( self::DB_QUEUE, $curr_built );
415
  Debug2::debug( '[Purge] Output existed, queue stored: ' . $curr_built );
416
  }
417
  else {
@@ -646,7 +655,7 @@ class Purge extends Base {
646
  * @since 1.0.7
647
  * @access public
648
  */
649
- public function purge_url( $url ) {
650
  $val = trim( $url );
651
  if ( empty( $val ) ) {
652
  return;
@@ -666,7 +675,7 @@ class Purge extends Base {
666
  return;
667
  }
668
 
669
- self::add( $hash );
670
 
671
  ! defined( 'LITESPEED_PURGE_SILENT' ) && Admin_Display::succeed( sprintf( __( 'Purge url %s', 'litespeed-cache' ), $val ) );
672
  }
@@ -884,12 +893,34 @@ class Purge extends Base {
884
  * @access private
885
  * @return string the built purge header
886
  */
887
- private function _build() {
888
- if ( empty( $this->_pub_purge ) && empty( $this->_priv_purge ) ) {
889
- return;
 
 
 
 
 
 
 
890
  }
891
 
892
  $purge_header = '';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
893
  $private_prefix = self::X_HEADER . ': private,';
894
 
895
  if ( ! empty( $this->_pub_purge ) ) {
11
 
12
  class Purge extends Base {
13
  protected $_pub_purge = array();
14
+ protected $_pub_purge2 = array();
15
  protected $_priv_purge = array();
16
  protected $_purge_related = false;
17
  protected $_purge_single = false;
18
 
19
 
20
  const X_HEADER = 'X-LiteSpeed-Purge';
21
+ const X_HEADER2 = 'X-LiteSpeed-Purge2';
22
  const DB_QUEUE = 'queue';
23
+ const DB_QUEUE2 = 'queue2';
24
 
25
  const TYPE_PURGE_ALL = 'purge_all';
26
  const TYPE_PURGE_ALL_LSCACHE = 'purge_all_lscache';
385
  * @access public
386
  * @param mixed $tags Tags to add to the list.
387
  */
388
+ public static function add( $tags, $purge2 = false ) {
389
+ self::cls()->_add( $tags, $purge2 );
390
  }
391
 
392
  /**
395
  * @since 2.2
396
  * @access private
397
  */
398
+ private function _add( $tags, $purge2 = false ) {
399
  if ( ! is_array( $tags ) ) {
400
  $tags = array( $tags );
401
  }
402
 
403
  $tags = $this->_prepend_bid( $tags );
404
 
405
+ if ( ! array_diff( $tags, $purge2 ? $this->_pub_purge2 : $this->_pub_purge ) ) {
406
  return;
407
  }
408
 
409
+ if ( $purge2 ) {
410
+ $this->_pub_purge2 = array_merge( $this->_pub_purge2, $tags );
411
+ $this->_pub_purge2 = array_unique( $this->_pub_purge2 );
412
+ }
413
+ else {
414
+ $this->_pub_purge = array_merge( $this->_pub_purge, $tags );
415
+ $this->_pub_purge = array_unique( $this->_pub_purge );
416
+ }
417
+ Debug2::debug( '[Purge] added ' . implode( ',', $tags ) . ( $purge2 ? ' [Purge2]' : '' ), 8 );
418
 
419
  // Send purge header immediately
420
+ $curr_built = $this->_build( $purge2 );
421
  if ( defined( 'LITESPEED_DID_send_headers' ) || defined( 'LITESPEED_CLI' ) ) {
422
  // Can't send, already has output, need to save and wait for next run
423
+ self::update_option( $purge2 ? self::DB_QUEUE2 : self::DB_QUEUE, $curr_built );
424
  Debug2::debug( '[Purge] Output existed, queue stored: ' . $curr_built );
425
  }
426
  else {
655
  * @since 1.0.7
656
  * @access public
657
  */
658
+ public function purge_url( $url, $purge2 = false ) {
659
  $val = trim( $url );
660
  if ( empty( $val ) ) {
661
  return;
675
  return;
676
  }
677
 
678
+ self::add( $hash, $purge2 );
679
 
680
  ! defined( 'LITESPEED_PURGE_SILENT' ) && Admin_Display::succeed( sprintf( __( 'Purge url %s', 'litespeed-cache' ), $val ) );
681
  }
893
  * @access private
894
  * @return string the built purge header
895
  */
896
+ private function _build( $purge2 = false ) {
897
+ if ( $purge2 ) {
898
+ if ( empty( $this->_pub_purge2 ) ) {
899
+ return;
900
+ }
901
+ }
902
+ else {
903
+ if ( empty( $this->_pub_purge ) && empty( $this->_priv_purge ) ) {
904
+ return;
905
+ }
906
  }
907
 
908
  $purge_header = '';
909
+
910
+ // Handle purge2 @since 4.4.1
911
+ if ( $purge2 ) {
912
+ $public_tags = $this->_append_prefix( $this->_pub_purge2 );
913
+ if ( empty( $public_tags ) ) {
914
+ return;
915
+ }
916
+ $purge_header = self::X_HEADER2 . ': public,';
917
+ if ( Control::is_stale() ) {
918
+ $purge_header .= 'stale,';
919
+ }
920
+ $purge_header .= implode( ',', $public_tags );
921
+ return $purge_header;
922
+ }
923
+
924
  $private_prefix = self::X_HEADER . ': private,';
925
 
926
  if ( ! empty( $this->_pub_purge ) ) {