Cache Enabler – WordPress Cache - Version 1.8.0

Version Description

  • Update advanced-cache.php drop-in file handling to improve reliability and compatibility (#283 and #260)
  • Update settings file to be deleted before the home option is updated to prevent a leftover settings file (#279)
  • Update cache_enabler_bypass_cache filter hook default value to allow a complete override (#277)
  • Update cache size transient to be in real time (#269 and #237)
  • Update cache expiry time to always be a non-negative integer (#265)
  • Update WP-CLI clear subcommand (#261)
  • Update required WordPress version from 5.1 to 5.5 (#260)
  • Update plugin upgrade process to improve reliability and compatibility (#260)
  • Update getting the cache file path to improve creating cache files (#256)
  • Update HTML5 doctype check to be less strict (#254)
  • Update permalink structure handling (#263 and #251)
  • Update requirements check to improve notices shown (#260 and #249)
  • Update cache clearing structure to enhance the automatic cache clearing actions (#247)
  • Add WP-Cron event to clear the expired cache on an hourly basis (#281, #268, and #237)
  • Add new cache clearing structure for option actions (#280 and #272)
  • Add cache engine restart support (#278 and #271)
  • Add constants.php file to plugin directory to allow constant overrides (#260)
  • Add wildcard cache clearing support (#246)
  • Add Brotli compression support (#243 @nlemoine)
  • Add new cache clearing structure for term actions (#234 @davelit)
  • Add cache iterator to improve cache object handling (#237)
  • Fix WebP URL conversion edge case (#275)
  • Deprecate cache_enabler_clear_site_cache_by_blog_id and cache_enabler_clear_page_cache_by_post_id action hooks in favor of replacements (#274 and #247)
Download this release

Release Info

Developer keycdn
Plugin Icon 128x128 Cache Enabler – WordPress Cache
Version 1.8.0
Comparing to
See all releases

Code changes from version 1.7.2 to 1.8.0

advanced-cache.php CHANGED
@@ -1,40 +1,32 @@
1
  <?php
2
  /**
3
- * Cache Enabler advanced cache
 
 
 
 
4
  *
5
  * @since 1.2.0
6
- * @change 1.7.0
7
  */
8
 
9
  if ( ! defined( 'ABSPATH' ) ) {
10
  exit;
11
  }
12
 
13
- /**
14
- * set the CACHE_ENABLER_DIR constant without trailing slash in your wp-config.php file if the plugin resides
15
- * somewhere other than path/to/wp-content/plugins/cache-enabler
16
- */
17
- if ( defined( 'CACHE_ENABLER_DIR' ) ) {
18
- $cache_enabler_dir = CACHE_ENABLER_DIR;
19
- } else {
20
- $cache_enabler_dir = ( ( defined( 'WP_PLUGIN_DIR' ) ) ? WP_PLUGIN_DIR : WP_CONTENT_DIR . '/plugins' ) . '/cache-enabler';
21
- }
22
-
23
- $cache_enabler_engine_file = $cache_enabler_dir . '/inc/cache_enabler_engine.class.php';
24
- $cache_enabler_disk_file = $cache_enabler_dir . '/inc/cache_enabler_disk.class.php';
25
 
26
- if ( file_exists( $cache_enabler_engine_file ) && file_exists( $cache_enabler_disk_file ) ) {
27
- require_once $cache_enabler_engine_file;
28
- require_once $cache_enabler_disk_file;
29
- }
30
 
31
- if ( class_exists( 'Cache_Enabler_Engine' ) ) {
32
- $cache_enabler_engine_started = Cache_Enabler_Engine::start();
33
 
34
- if ( $cache_enabler_engine_started ) {
35
- $cache_enabler_cache_delivered = Cache_Enabler_Engine::deliver_cache();
 
36
 
37
- if ( ! $cache_enabler_cache_delivered ) {
38
  Cache_Enabler_Engine::start_buffering();
39
  }
40
  }
1
  <?php
2
  /**
3
+ * The advanced-cache.php drop-in file for Cache Enabler.
4
+ *
5
+ * The advanced-cache.php creation method uses this during the disk setup and
6
+ * requirements check. You can copy this file to the wp-content directory and
7
+ * edit the $cache_enabler_constants_file value as needed.
8
  *
9
  * @since 1.2.0
10
+ * @change 1.8.0
11
  */
12
 
13
  if ( ! defined( 'ABSPATH' ) ) {
14
  exit;
15
  }
16
 
17
+ $cache_enabler_constants_file = '/your/path/to/wp-content/plugins/cache-enabler/constants.php';
 
 
 
 
 
 
 
 
 
 
 
18
 
19
+ if ( file_exists( $cache_enabler_constants_file ) ) {
20
+ require $cache_enabler_constants_file;
 
 
21
 
22
+ $cache_enabler_engine_file = CACHE_ENABLER_DIR . '/inc/cache_enabler_engine.class.php';
23
+ $cache_enabler_disk_file = CACHE_ENABLER_DIR . '/inc/cache_enabler_disk.class.php';
24
 
25
+ if ( file_exists( $cache_enabler_engine_file ) && file_exists( $cache_enabler_disk_file ) ) {
26
+ require_once $cache_enabler_engine_file;
27
+ require_once $cache_enabler_disk_file;
28
 
29
+ if ( Cache_Enabler_Engine::start() && ! Cache_Enabler_Engine::deliver_cache() ) {
30
  Cache_Enabler_Engine::start_buffering();
31
  }
32
  }
cache-enabler.php CHANGED
@@ -6,7 +6,7 @@ Description: Simple and fast WordPress caching plugin.
6
  Author: KeyCDN
7
  Author URI: https://www.keycdn.com
8
  License: GPLv2 or later
9
- Version: 1.7.2
10
  */
11
 
12
  /*
@@ -31,38 +31,17 @@ if ( ! defined( 'ABSPATH' ) ) {
31
  exit;
32
  }
33
 
34
- // constants
35
- define( 'CACHE_ENABLER_VERSION', '1.7.2' );
36
- define( 'CACHE_ENABLER_MIN_PHP', '5.6' );
37
- define( 'CACHE_ENABLER_MIN_WP', '5.1' );
38
- define( 'CACHE_ENABLER_FILE', __FILE__ );
39
- define( 'CACHE_ENABLER_BASE', plugin_basename( __FILE__ ) );
40
 
41
- if ( ! defined( 'CACHE_ENABLER_DIR' ) ) {
42
- define( 'CACHE_ENABLER_DIR', __DIR__ );
43
- }
44
-
45
- // deprecated constants (1.7.0)
46
- define( 'CE_VERSION', CACHE_ENABLER_VERSION );
47
- define( 'CE_MIN_PHP', CACHE_ENABLER_MIN_PHP );
48
- define( 'CE_MIN_WP', CACHE_ENABLER_MIN_WP );
49
- define( 'CE_FILE', CACHE_ENABLER_FILE );
50
- define( 'CE_BASE', CACHE_ENABLER_BASE );
51
- define( 'CE_DIR', CACHE_ENABLER_DIR );
52
-
53
- // hooks
54
  add_action( 'plugins_loaded', array( 'Cache_Enabler', 'init' ) );
55
- register_activation_hook( __FILE__, array( 'Cache_Enabler', 'on_activation' ) );
56
- register_deactivation_hook( __FILE__, array( 'Cache_Enabler', 'on_deactivation' ) );
57
- register_uninstall_hook( __FILE__, array( 'Cache_Enabler', 'on_uninstall' ) );
58
 
59
- // register autoload
60
  spl_autoload_register( 'cache_enabler_autoload' );
61
 
62
- // load required classes
63
  function cache_enabler_autoload( $class_name ) {
64
- // check if classes were loaded in advanced-cache.php
65
- if ( in_array( $class_name, array( 'Cache_Enabler', 'Cache_Enabler_Engine', 'Cache_Enabler_Disk' ), true ) && ! class_exists( $class_name ) ) {
66
  require_once sprintf(
67
  '%s/inc/%s.class.php',
68
  CACHE_ENABLER_DIR,
@@ -71,7 +50,7 @@ function cache_enabler_autoload( $class_name ) {
71
  }
72
  }
73
 
74
- // load WP-CLI command
75
  if ( defined( 'WP_CLI' ) && WP_CLI && class_exists( 'WP_CLI' ) ) {
76
  require_once CACHE_ENABLER_DIR . '/inc/cache_enabler_cli.class.php';
 
77
  }
6
  Author: KeyCDN
7
  Author URI: https://www.keycdn.com
8
  License: GPLv2 or later
9
+ Version: 1.8.0
10
  */
11
 
12
  /*
31
  exit;
32
  }
33
 
34
+ require __DIR__ . '/constants.php';
 
 
 
 
 
35
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
  add_action( 'plugins_loaded', array( 'Cache_Enabler', 'init' ) );
37
+ register_activation_hook( CACHE_ENABLER_FILE, array( 'Cache_Enabler', 'on_activation' ) );
38
+ register_deactivation_hook( CACHE_ENABLER_FILE, array( 'Cache_Enabler', 'on_deactivation' ) );
39
+ register_uninstall_hook( CACHE_ENABLER_FILE, array( 'Cache_Enabler', 'on_uninstall' ) );
40
 
 
41
  spl_autoload_register( 'cache_enabler_autoload' );
42
 
 
43
  function cache_enabler_autoload( $class_name ) {
44
+ if ( in_array( $class_name, array( 'Cache_Enabler', 'Cache_Enabler_Engine', 'Cache_Enabler_Disk' ), true ) ) {
 
45
  require_once sprintf(
46
  '%s/inc/%s.class.php',
47
  CACHE_ENABLER_DIR,
50
  }
51
  }
52
 
 
53
  if ( defined( 'WP_CLI' ) && WP_CLI && class_exists( 'WP_CLI' ) ) {
54
  require_once CACHE_ENABLER_DIR . '/inc/cache_enabler_cli.class.php';
55
+ WP_CLI::add_command( 'cache-enabler', 'Cache_Enabler_CLI' );
56
  }
constants.php ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Plugin constants.
4
+ *
5
+ * @since 1.8.0
6
+ */
7
+
8
+ $cache_enabler_constants = array(
9
+ 'CACHE_ENABLER_VERSION' => '1.8.0',
10
+ 'CACHE_ENABLER_MIN_PHP' => '5.6',
11
+ 'CACHE_ENABLER_MIN_WP' => '5.5',
12
+ 'CACHE_ENABLER_DIR' => __DIR__,
13
+ 'CACHE_ENABLER_FILE' => __DIR__ . '/cache-enabler.php',
14
+ 'CACHE_ENABLER_BASE' => ( function_exists( 'wp_normalize_path' ) ) ? plugin_basename( __DIR__ . '/cache-enabler.php' ) : null,
15
+ 'CACHE_ENABLER_CACHE_DIR' => WP_CONTENT_DIR . '/cache/cache-enabler', // Without a trailing slash.
16
+ 'CACHE_ENABLER_SETTINGS_DIR' => WP_CONTENT_DIR . '/settings/cache-enabler', // Without a trailing slash.
17
+ 'CACHE_ENABLER_INDEX_FILE' => ABSPATH . 'index.php',
18
+ );
19
+
20
+ foreach ( $cache_enabler_constants as $cache_enabler_constant_name => $cache_enabler_constant_value ) {
21
+ if ( ! defined( $cache_enabler_constant_name ) && $cache_enabler_constant_value !== null ) {
22
+ define( $cache_enabler_constant_name, $cache_enabler_constant_value );
23
+ }
24
+ }
25
+
26
+ // Deprecated in 1.7.0.
27
+ if ( defined( 'CACHE_ENABLER_BASE' ) ) {
28
+ define( 'CE_VERSION', CACHE_ENABLER_VERSION );
29
+ define( 'CE_MIN_PHP', CACHE_ENABLER_MIN_PHP );
30
+ define( 'CE_MIN_WP', CACHE_ENABLER_MIN_WP );
31
+ define( 'CE_DIR', CACHE_ENABLER_DIR );
32
+ define( 'CE_FILE', CACHE_ENABLER_FILE );
33
+ define( 'CE_BASE', CACHE_ENABLER_BASE );
34
+ }
inc/cache_enabler.class.php CHANGED
@@ -1,6 +1,6 @@
1
  <?php
2
  /**
3
- * Cache Enabler base
4
  *
5
  * @since 1.0.0
6
  */
@@ -10,593 +10,934 @@ if ( ! defined( 'ABSPATH' ) ) {
10
  }
11
 
12
  final class Cache_Enabler {
13
-
14
  /**
15
- * initialize plugin
16
  *
17
- * @since 1.0.0
18
- * @change 1.5.0
19
  */
20
-
21
  public static function init() {
22
 
23
  new self();
24
  }
25
 
26
-
27
  /**
28
- * settings from database (deprecated)
29
  *
30
  * @since 1.0.0
31
  * @deprecated 1.5.0
32
  */
33
-
34
  public static $options;
35
 
36
-
37
  /**
38
- * fire page cache cleared hook
39
- *
40
- * @since 1.6.0
41
- * @change 1.6.0
42
  *
43
- * @var boolean
 
44
  */
45
-
46
  public static $fire_page_cache_cleared_hook = true;
47
 
48
-
49
  /**
50
- * constructor
 
 
51
  *
52
  * @since 1.0.0
53
- * @change 1.7.0
54
  */
55
-
56
  public function __construct() {
57
 
58
- // init hooks
59
  add_action( 'init', array( 'Cache_Enabler_Engine', 'start' ) );
60
  add_action( 'init', array( __CLASS__, 'process_clear_cache_request' ) );
61
  add_action( 'init', array( __CLASS__, 'register_textdomain' ) );
 
 
 
 
 
 
62
 
63
- // public clear cache hooks
64
  add_action( 'cache_enabler_clear_complete_cache', array( __CLASS__, 'clear_complete_cache' ) );
65
  add_action( 'cache_enabler_clear_site_cache', array( __CLASS__, 'clear_site_cache' ) );
66
- add_action( 'cache_enabler_clear_site_cache_by_blog_id', array( __CLASS__, 'clear_site_cache_by_blog_id' ) );
67
- add_action( 'cache_enabler_clear_page_cache_by_post_id', array( __CLASS__, 'clear_page_cache_by_post_id' ) );
68
  add_action( 'cache_enabler_clear_page_cache_by_url', array( __CLASS__, 'clear_page_cache_by_url' ) );
69
- add_action( 'ce_clear_cache', array( __CLASS__, 'clear_complete_cache' ) ); // deprecated in 1.6.0
70
- add_action( 'ce_clear_post_cache', array( __CLASS__, 'clear_page_cache_by_post_id' ) ); // deprecated in 1.6.0
 
 
71
 
72
- // system clear cache hooks
73
- add_action( '_core_updated_successfully', array( __CLASS__, 'clear_complete_cache' ) );
74
  add_action( 'upgrader_process_complete', array( __CLASS__, 'on_upgrade' ), 10, 2 );
75
- add_action( 'switch_theme', array( __CLASS__, 'clear_site_cache' ) );
76
- add_action( 'permalink_structure_changed', array( __CLASS__, 'clear_site_cache' ) );
77
  add_action( 'activated_plugin', array( __CLASS__, 'on_plugin_activation_deactivation' ), 10, 2 );
78
  add_action( 'deactivated_plugin', array( __CLASS__, 'on_plugin_activation_deactivation' ), 10, 2 );
79
  add_action( 'save_post', array( __CLASS__, 'on_save_trash_post' ) );
80
- add_action( 'wp_trash_post', array( __CLASS__, 'on_save_trash_post' ) );
81
  add_action( 'pre_post_update', array( __CLASS__, 'on_pre_post_update' ), 10, 2 );
 
82
  add_action( 'comment_post', array( __CLASS__, 'on_comment_post' ), 99, 2 );
83
  add_action( 'edit_comment', array( __CLASS__, 'on_edit_comment' ), 10, 2 );
84
  add_action( 'transition_comment_status', array( __CLASS__, 'on_transition_comment_status' ), 10, 3 );
85
-
86
- // third party clear cache hooks
 
 
 
 
 
 
 
87
  add_action( 'autoptimize_action_cachepurged', array( __CLASS__, 'clear_complete_cache' ) );
88
  add_action( 'woocommerce_product_set_stock', array( __CLASS__, 'on_woocommerce_stock_update' ) );
89
  add_action( 'woocommerce_variation_set_stock', array( __CLASS__, 'on_woocommerce_stock_update' ) );
90
  add_action( 'woocommerce_product_set_stock_status', array( __CLASS__, 'on_woocommerce_stock_update' ) );
91
  add_action( 'woocommerce_variation_set_stock_status', array( __CLASS__, 'on_woocommerce_stock_update' ) );
92
 
93
- // multisite hooks
 
 
 
 
 
94
  add_action( 'wp_initialize_site', array( __CLASS__, 'install_later' ) );
95
  add_action( 'wp_uninitialize_site', array( __CLASS__, 'uninstall_later' ) );
96
 
97
- // settings hooks
98
- add_action( 'permalink_structure_changed', array( __CLASS__, 'update_backend' ) );
99
- add_action( 'add_option_cache_enabler', array( __CLASS__, 'on_update_backend' ), 10, 2 );
100
- add_action( 'update_option_cache_enabler', array( __CLASS__, 'on_update_backend' ), 10, 2 );
101
-
102
- // admin bar hook
103
  add_action( 'admin_bar_menu', array( __CLASS__, 'add_admin_bar_items' ), 90 );
104
 
105
- // admin interface hooks
106
  if ( is_admin() ) {
107
- // settings
108
  add_action( 'admin_init', array( __CLASS__, 'register_settings' ) );
109
  add_action( 'admin_menu', array( __CLASS__, 'add_settings_page' ) );
110
  add_action( 'admin_enqueue_scripts', array( __CLASS__, 'add_admin_resources' ) );
111
- // dashboard
112
  add_filter( 'dashboard_glance_items', array( __CLASS__, 'add_dashboard_cache_size' ) );
113
  add_filter( 'plugin_action_links_' . CACHE_ENABLER_BASE, array( __CLASS__, 'add_plugin_action_links' ) );
114
  add_filter( 'plugin_row_meta', array( __CLASS__, 'add_plugin_row_meta' ), 10, 2 );
115
- // notices
116
  add_action( 'admin_notices', array( __CLASS__, 'requirements_check' ) );
117
  add_action( 'admin_notices', array( __CLASS__, 'cache_cleared_notice' ) );
118
  add_action( 'network_admin_notices', array( __CLASS__, 'cache_cleared_notice' ) );
119
  }
120
  }
121
 
122
-
123
  /**
124
- * activation hook
 
 
 
 
 
125
  *
126
  * @since 1.0.0
127
  * @change 1.6.0
128
  *
129
- * @param boolean $network_wide network activated
130
  */
131
-
132
  public static function on_activation( $network_wide ) {
133
 
134
- // add backend requirements, triggering the settings file(s) to be created
135
  self::each_site( $network_wide, 'self::update_backend' );
136
 
137
- // configure system files
138
  Cache_Enabler_Disk::setup();
139
  }
140
 
141
-
142
  /**
143
- * upgrade hook
144
  *
145
- * @since 1.2.3
146
- * @change 1.7.0
 
 
 
147
  *
148
- * @param WP_Upgrader $obj upgrade instance
149
- * @param array $data update data
150
  */
151
-
152
- public static function on_upgrade( $obj, $data ) {
153
 
154
  if ( $data['action'] !== 'update' ) {
155
  return;
156
  }
157
 
158
- // updated themes
 
 
 
159
  if ( $data['type'] === 'theme' && isset( $data['themes'] ) ) {
160
  $updated_themes = (array) $data['themes'];
161
  $sites_themes = self::each_site( is_multisite(), 'wp_get_theme' );
162
 
163
- // check each site
164
  foreach ( $sites_themes as $blog_id => $site_theme ) {
165
- // if the active or parent theme has been updated clear site cache
166
  if ( in_array( $site_theme->stylesheet, $updated_themes, true ) || in_array( $site_theme->template, $updated_themes, true ) ) {
167
- self::clear_site_cache_by_blog_id( $blog_id );
168
  }
169
  }
170
  }
171
 
172
- // updated plugins
173
  if ( $data['type'] === 'plugin' && isset( $data['plugins'] ) ) {
174
  $updated_plugins = (array) $data['plugins'];
 
175
 
176
- // check if Cache Enabler has been updated
177
- if ( in_array( CACHE_ENABLER_BASE, $updated_plugins, true ) ) {
178
- self::on_cache_enabler_update();
179
- // check all updated plugins otherwise
180
  } else {
181
- $network_plugins = ( is_multisite() ) ? array_flip( (array) get_site_option( 'active_sitewide_plugins', array() ) ) : array();
182
-
183
- // if a network activated plugin has been updated clear complete cache
184
- if ( ! empty( array_intersect( $updated_plugins, $network_plugins ) ) ) {
185
- self::clear_complete_cache();
186
- // check each site otherwise
187
- } else {
188
- $sites_plugins = self::each_site( is_multisite(), 'get_option', array( 'active_plugins', array() ) );
189
-
190
- foreach ( $sites_plugins as $blog_id => $site_plugins ) {
191
- // if an activated plugin has been updated clear site cache
192
- if ( ! empty( array_intersect( $updated_plugins, (array) $site_plugins ) ) ) {
193
- self::clear_site_cache_by_blog_id( $blog_id );
194
- }
195
  }
196
  }
197
  }
198
  }
199
  }
200
 
201
-
202
  /**
203
- * Cache Enabler update actions
204
  *
205
- * @since 1.4.0
206
- * @change 1.7.0
207
  */
208
-
209
  public static function on_cache_enabler_update() {
210
 
211
- // clean system files
212
  self::each_site( is_multisite(), 'Cache_Enabler_Disk::clean' );
213
 
214
- // configure system files
215
  Cache_Enabler_Disk::setup();
216
 
217
- // clear complete cache
218
  self::clear_complete_cache();
219
  }
220
 
221
-
222
  /**
223
- * deactivation hook
 
 
 
 
 
224
  *
225
  * @since 1.0.0
226
- * @change 1.6.0
227
  *
228
- * @param boolean $network_wide network deactivated
229
  */
230
-
231
  public static function on_deactivation( $network_wide ) {
232
 
233
- // clean system files
234
  self::each_site( $network_wide, 'Cache_Enabler_Disk::clean' );
235
-
236
- // clear site(s) cache
237
- self::each_site( $network_wide, 'self::clear_site_cache' );
238
  }
239
 
240
-
241
  /**
242
- * uninstall hook
 
 
 
 
243
  *
244
  * @since 1.0.0
245
  * @change 1.6.0
246
  */
247
-
248
  public static function on_uninstall() {
249
 
250
- // uninstall backend requirements
251
  self::each_site( is_multisite(), 'self::uninstall_backend' );
252
  }
253
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
254
 
255
  /**
256
- * install on new site in multisite network
 
 
 
 
257
  *
258
  * @since 1.0.0
259
- * @change 1.7.0
260
  *
261
- * @param WP_Site $new_site new site instance
262
  */
263
-
264
  public static function install_later( $new_site ) {
265
 
266
- // check if network activated
267
  if ( ! is_plugin_active_for_network( CACHE_ENABLER_BASE ) ) {
268
  return;
269
  }
270
 
271
- // switch to new site
272
- switch_to_blog( (int) $new_site->blog_id );
273
-
274
- // add backend requirements, triggering the settings file to be created
275
  self::update_backend();
276
-
277
- // restore current blog from before new site
278
- restore_current_blog();
279
  }
280
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
281
 
282
  /**
283
- * add or update backend requirements
 
 
 
 
 
284
  *
285
  * @since 1.5.0
286
- * @change 1.7.0
287
  *
288
- * @return array $new_option_value new or current database option value
289
  */
290
-
291
  public static function update_backend() {
292
 
293
- // delete user(s) meta key from deleted publishing action (1.5.0)
294
- delete_metadata( 'user', 0, '_clear_post_cache_on_update', '', true );
295
 
296
- // maybe rename old database option (1.5.0)
297
- $old_option_value = get_option( 'cache-enabler' );
298
- if ( $old_option_value !== false ) {
299
  delete_option( 'cache-enabler' );
300
- add_option( 'cache_enabler', $old_option_value );
301
  }
302
 
303
- // get defined settings, fall back to empty array if not found
304
- $old_option_value = get_option( 'cache_enabler', array() );
305
-
306
- // maybe convert old settings to new settings
307
- $old_option_value = self::convert_settings( $old_option_value );
308
 
309
- // update default system settings
310
- $old_option_value = wp_parse_args( self::get_default_settings( 'system' ), $old_option_value );
311
 
312
- // merge defined settings into default settings
313
- $new_option_value = wp_parse_args( $old_option_value, self::get_default_settings() );
 
314
 
315
- // validate settings
316
- $new_option_value = self::validate_settings( $new_option_value );
317
 
318
- // add or update database option
319
- update_option( 'cache_enabler', $new_option_value );
 
 
 
 
 
 
 
 
320
 
321
- // create settings file if action has not been registered for hook yet, like when in activation hook
322
- if ( has_action( 'update_option_cache_enabler', array( __CLASS__, 'on_update_backend' ) ) === false ) {
323
- Cache_Enabler_Disk::create_settings_file( $new_option_value );
324
- }
325
 
326
- return $new_option_value;
327
  }
328
 
329
-
330
  /**
331
- * add or update database option hook
 
 
 
332
  *
333
  * @since 1.5.0
334
- * @change 1.5.0
335
  *
336
- * @param mixed $option old database option value or name of the option to add
337
- * @param mixed $new_option_value new database option value
338
  */
 
339
 
340
- public static function on_update_backend( $option, $new_option_value ) {
341
 
342
- Cache_Enabler_Disk::create_settings_file( $new_option_value );
343
  }
344
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
345
 
346
  /**
347
- * uninstall on deleted site in multisite network
348
  *
349
- * @since 1.0.0
350
- * @change 1.5.0
351
  *
352
- * @param WP_Site $old_site old site instance
 
 
 
 
353
  */
 
354
 
355
- public static function uninstall_later( $old_site ) {
 
 
356
 
357
- $delete_cache_size_transient = false;
 
358
 
359
- // clean system files
360
- Cache_Enabler_Disk::clean();
 
 
361
 
362
- // clear site cache of deleted site
363
- self::clear_site_cache_by_blog_id( (int) $old_site->blog_id, $delete_cache_size_transient );
 
 
 
 
 
 
 
 
 
364
  }
365
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
366
 
367
  /**
368
- * uninstall backend requirements
 
 
 
 
 
369
  *
370
  * @since 1.0.0
371
- * @change 1.4.0
 
 
372
  */
 
 
 
 
 
 
373
 
 
 
 
 
 
 
374
  private static function uninstall_backend() {
375
 
376
- // delete database option
377
  delete_option( 'cache_enabler' );
 
378
  }
379
 
380
-
381
  /**
382
- * enter each site
383
  *
384
- * @since 1.5.0
385
- * @change 1.7.0
 
 
386
  *
387
- * @param boolean $network whether or not each site in network
388
- * @param string $callback callback function
389
- * @param array $callback_params callback function parameters
390
- * @return array $callback_return returned value(s) from callback function
 
 
 
 
 
391
  */
 
392
 
393
- private static function each_site( $network, $callback, $callback_params = array() ) {
394
-
395
- $callback_return = array();
 
396
 
397
- if ( $network ) {
398
- $blog_ids = self::get_blog_ids();
399
 
400
- // switch to each site in network
401
- foreach ( $blog_ids as $blog_id ) {
402
- switch_to_blog( $blog_id );
403
  $callback_return[ $blog_id ] = call_user_func_array( $callback, $callback_params );
404
- restore_current_blog();
405
  }
406
- } else {
407
- $blog_id = 1;
408
- $callback_return[ $blog_id ] = call_user_func_array( $callback, $callback_params );
 
409
  }
410
 
411
  return $callback_return;
412
  }
413
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
414
 
415
  /**
416
- * plugin activation and deactivation hooks
 
 
417
  *
418
  * @since 1.4.0
419
  * @change 1.6.0
420
  */
421
-
422
  public static function on_plugin_activation_deactivation() {
423
 
424
- // if setting enabled clear site cache on any plugin activation or deactivation
425
  if ( Cache_Enabler_Engine::$settings['clear_site_cache_on_changed_plugin'] ) {
426
  self::clear_site_cache();
427
  }
428
  }
429
 
430
-
431
  /**
432
- * get settings from database
433
  *
434
- * @since 1.0.0
435
- * @change 1.7.0
 
 
 
 
 
436
  *
437
- * @return array $settings current settings from database
 
 
 
438
  */
 
439
 
440
- public static function get_settings() {
441
-
442
- // get database option value
443
  $settings = get_option( 'cache_enabler' );
444
 
445
- // if database option does not exist or settings are outdated
446
  if ( $settings === false || ! isset( $settings['version'] ) || $settings['version'] !== CACHE_ENABLER_VERSION ) {
447
- $settings = self::update_backend();
 
 
 
448
  }
449
 
450
  return $settings;
451
  }
452
 
453
-
454
  /**
455
- * get blog IDs
456
  *
457
- * @since 1.0.0
458
- * @change 1.7.0
459
  *
460
- * @return array $blog_ids blog IDs
 
461
  */
 
462
 
463
- private static function get_blog_ids() {
 
 
 
 
 
 
464
 
465
- $blog_ids = array( 1 );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
466
 
467
  if ( is_multisite() ) {
468
  global $wpdb;
469
-
470
  $blog_ids = array_map( 'absint', $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs" ) );
 
 
471
  }
472
 
473
  return $blog_ids;
474
  }
475
 
476
-
477
  /**
478
- * get blog path
 
 
 
 
 
479
  *
480
  * @since 1.6.0
481
- * @change 1.6.0
482
  *
483
- * @return string $blog_path blog path from site address URL, empty otherwise
 
484
  */
485
-
486
  public static function get_blog_path() {
487
 
488
- $site_url_path = parse_url( home_url(), PHP_URL_PATH );
489
- $site_url_path = rtrim( $site_url_path, '/' );
490
- $site_url_path_pieces = explode( '/', $site_url_path );
491
 
492
- // get last piece in case installation is in a subdirectory
493
- $blog_path = ( ! empty( end( $site_url_path_pieces ) ) ) ? '/' . end( $site_url_path_pieces ) . '/' : '';
494
 
495
  return $blog_path;
496
  }
497
 
498
-
499
  /**
500
- * get blog paths
501
  *
502
- * @since 1.4.0
503
- * @change 1.6.0
504
  *
505
- * @return array $blog_paths blog paths
506
  */
 
507
 
508
- public static function get_blog_paths() {
 
 
 
 
 
 
509
 
510
- $blog_paths = array( '/' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
511
 
512
  if ( is_multisite() ) {
513
  global $wpdb;
514
  $blog_paths = $wpdb->get_col( "SELECT path FROM $wpdb->blogs" );
 
 
515
  }
516
 
517
  return $blog_paths;
518
  }
519
 
520
-
521
  /**
522
- * get permalink structure
523
  *
524
- * @since 1.5.0
525
- * @change 1.5.0
526
  *
527
- * @return string permalink structure
528
  */
 
 
 
 
 
 
529
 
 
 
 
 
 
 
530
  private static function get_permalink_structure() {
531
 
532
- // get permalink structure
533
  $permalink_structure = get_option( 'permalink_structure' );
534
 
535
- // permalink structure is custom and has a trailing slash
536
  if ( $permalink_structure && preg_match( '/\/$/', $permalink_structure ) ) {
537
  return 'has_trailing_slash';
538
  }
539
 
540
- // permalink structure is custom and does not have a trailing slash
541
  if ( $permalink_structure && ! preg_match( '/\/$/', $permalink_structure ) ) {
542
  return 'no_trailing_slash';
543
  }
544
 
545
- // permalink structure is not custom
546
  if ( empty( $permalink_structure ) ) {
547
  return 'plain';
548
  }
549
  }
550
 
551
-
552
  /**
553
- * get cache size from database or disk
554
  *
555
- * @since 1.0.0
556
- * @change 1.7.0
557
  *
558
- * @return integer $cache_size cache size in bytes
559
  */
 
560
 
561
- public static function get_cache_size() {
562
-
563
- $cache_size = get_transient( self::get_cache_size_transient_name() );
564
 
565
- if ( ! $cache_size ) {
566
- $cache_size = Cache_Enabler_Disk::get_cache_size();
567
- set_transient( self::get_cache_size_transient_name(), $cache_size, MINUTE_IN_SECONDS * 15 );
568
- }
569
-
570
- return $cache_size;
571
  }
572
 
573
-
574
  /**
575
- * get the cache size transient name
576
  *
577
- * @since 1.5.0
578
- * @change 1.6.0
579
  *
580
- * @return string $transient_name transient name
 
 
 
581
  */
 
582
 
583
- private static function get_cache_size_transient_name() {
584
 
585
- $transient_name = 'cache_enabler_cache_size';
 
 
 
586
 
587
- return $transient_name;
588
- }
589
 
 
 
590
 
591
  /**
592
- * get the cache cleared transient name used for the clear notice
593
  *
594
- * @since 1.5.0
595
- * @change 1.5.0
596
  *
597
- * @return string $transient_name transient name
598
  */
599
-
600
  private static function get_cache_cleared_transient_name() {
601
 
602
  $transient_name = 'cache_enabler_cache_cleared_' . get_current_user_id();
@@ -604,22 +945,21 @@ final class Cache_Enabler {
604
  return $transient_name;
605
  }
606
 
607
-
608
  /**
609
- * get default settings
610
  *
611
- * @since 1.0.0
612
- * @change 1.7.0
613
  *
614
- * @param string $settings_type default `system` settings
615
- * @return array $system_default_settings|$default_settings only default system settings or all default settings
616
  */
617
-
618
  private static function get_default_settings( $settings_type = null ) {
619
 
620
  $system_default_settings = array(
621
- 'version' => (string) CACHE_ENABLER_VERSION,
622
- 'permalink_structure' => (string) self::get_permalink_structure(),
 
623
  );
624
 
625
  if ( $settings_type === 'system' ) {
@@ -631,6 +971,8 @@ final class Cache_Enabler {
631
  'cache_expiry_time' => 0,
632
  'clear_site_cache_on_saved_post' => 0,
633
  'clear_site_cache_on_saved_comment' => 0,
 
 
634
  'clear_site_cache_on_changed_plugin' => 0,
635
  'convert_image_urls_to_webp' => 0,
636
  'mobile_cache' => 0,
@@ -643,41 +985,63 @@ final class Cache_Enabler {
643
  'excluded_cookies' => '',
644
  );
645
 
646
- // merge default settings
647
  $default_settings = wp_parse_args( $user_default_settings, $system_default_settings );
648
 
649
  return $default_settings;
650
  }
651
 
652
-
653
  /**
654
- * convert settings to new structure
655
  *
656
- * @since 1.5.0
657
- * @change 1.6.1
658
  *
659
- * @param array $settings settings
660
- * @return array $settings converted settings if applicable, unchanged otherwise
661
  */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
662
 
663
- private static function convert_settings( $settings ) {
 
 
 
 
 
 
 
 
 
 
 
664
 
665
- // check if there are any settings to convert
666
  if ( empty( $settings ) ) {
667
  return $settings;
668
  }
669
 
670
- // updated settings
671
  if ( isset( $settings['expires'] ) && $settings['expires'] > 0 ) {
672
  $settings['cache_expires'] = 1;
673
  }
674
 
 
675
  if ( isset( $settings['minify_html'] ) && $settings['minify_html'] === 2 ) {
676
  $settings['minify_html'] = 1;
677
  $settings['minify_inline_css_js'] = 1;
678
  }
679
 
680
- // renamed or removed settings
681
  $settings_names = array(
682
  // 1.4.0
683
  'excl_regexp' => 'excluded_page_paths',
@@ -685,7 +1049,7 @@ final class Cache_Enabler {
685
  // 1.5.0
686
  'expires' => 'cache_expiry_time',
687
  'new_post' => 'clear_site_cache_on_saved_post',
688
- 'update_product_stock' => '', // deprecated
689
  'new_comment' => 'clear_site_cache_on_saved_comment',
690
  'clear_on_upgrade' => 'clear_site_cache_on_changed_plugin',
691
  'webp' => 'convert_image_urls_to_webp',
@@ -693,7 +1057,7 @@ final class Cache_Enabler {
693
  'excl_ids' => 'excluded_post_ids',
694
  'excl_paths' => 'excluded_page_paths',
695
  'excl_cookies' => 'excluded_cookies',
696
- 'incl_parameters' => '', // deprecated
697
 
698
  // 1.6.0
699
  'clear_complete_cache_on_saved_post' => 'clear_site_cache_on_saved_post',
@@ -717,25 +1081,23 @@ final class Cache_Enabler {
717
  return $settings;
718
  }
719
 
720
-
721
  /**
722
- * add plugin action links in the plugins list table
723
  *
724
- * @since 1.0.0
 
 
725
  * @change 1.7.0
726
  *
727
- * @param array $action_links action links
728
- * @return array $action_links updated action links if applicable, unchanged otherwise
729
  */
730
-
731
  public static function add_plugin_action_links( $action_links ) {
732
 
733
- // check user role
734
  if ( ! current_user_can( 'manage_options' ) ) {
735
  return $action_links;
736
  }
737
 
738
- // prepend action link
739
  array_unshift( $action_links, sprintf(
740
  '<a href="%s">%s</a>',
741
  admin_url( 'options-general.php?page=cache-enabler' ),
@@ -745,26 +1107,25 @@ final class Cache_Enabler {
745
  return $action_links;
746
  }
747
 
748
-
749
  /**
750
- * add plugin metadata in the plugins list table
751
  *
752
- * @since 1.0.0
 
 
753
  * @change 1.7.2
754
  *
755
- * @param array $plugin_meta plugin metadata, including the version, author, author URI, and plugin URI
756
- * @param string $plugin_file path to the plugin file relative to the plugins directory
757
- * @return array $plugin_meta updated action links if applicable, unchanged otherwise
 
758
  */
759
-
760
  public static function add_plugin_row_meta( $plugin_meta, $plugin_file ) {
761
 
762
- // check if Cache Enabler row
763
  if ( $plugin_file !== CACHE_ENABLER_BASE ) {
764
  return $plugin_meta;
765
  }
766
 
767
- // append metadata
768
  $plugin_meta = wp_parse_args(
769
  array(
770
  '<a href="https://www.keycdn.com/support/wordpress-cache-enabler-plugin" target="_blank" rel="nofollow noopener">' . esc_html__( 'Documentation', 'cache-enabler' ) . '</a>',
@@ -775,62 +1136,53 @@ final class Cache_Enabler {
775
  return $plugin_meta;
776
  }
777
 
778
-
779
  /**
780
- * add dashboard cache size count
781
  *
782
- * @since 1.0.0
783
- * @change 1.5.0
 
 
784
  *
785
- * @param array $items initial array with dashboard items
786
- * @return array $items merged array with dashboard items
787
  */
 
788
 
789
- public static function add_dashboard_cache_size( $items = array() ) {
790
-
791
- // check user role
792
  if ( ! current_user_can( 'manage_options' ) ) {
793
  return $items;
794
  }
795
 
796
- // get cache size
797
  $cache_size = self::get_cache_size();
798
 
799
- // display items
800
- $items = array(
801
- sprintf(
802
- '<a href="%s" title="%s">%s %s</a>',
803
- admin_url( 'options-general.php?page=cache-enabler' ),
804
- esc_html__( 'Refreshes every 15 minutes', 'cache-enabler' ),
805
- ( empty( $cache_size ) ) ? esc_html__( 'Empty', 'cache-enabler' ) : size_format( $cache_size ),
806
- esc_html__( 'Cache Size', 'cache-enabler' )
807
- )
808
  );
809
 
810
  return $items;
811
  }
812
 
813
-
814
  /**
815
- * add admin bar items
816
  *
817
- * @since 1.0.0
818
- * @change 1.6.0
819
  *
820
- * @param object $wp_admin_bar menu properties
 
 
821
  */
822
-
823
  public static function add_admin_bar_items( $wp_admin_bar ) {
824
 
825
- // check user role
826
  if ( ! self::user_can_clear_cache() ) {
827
  return;
828
  }
829
 
830
- // set clear cache button title
831
  $title = ( is_multisite() && is_network_admin() ) ? esc_html__( 'Clear Network Cache', 'cache-enabler' ) : esc_html__( 'Clear Site Cache', 'cache-enabler' );
832
 
833
- // add "Clear Network Cache" or "Clear Site Cache" button in admin bar
834
  $wp_admin_bar->add_menu(
835
  array(
836
  'id' => 'cache_enabler_clear_cache',
@@ -844,7 +1196,6 @@ final class Cache_Enabler {
844
  )
845
  );
846
 
847
- // add "Clear Page Cache" button in admin bar
848
  if ( ! is_admin() ) {
849
  $wp_admin_bar->add_menu(
850
  array(
@@ -861,30 +1212,30 @@ final class Cache_Enabler {
861
  }
862
  }
863
 
864
-
865
  /**
866
- * enqueue styles and scripts
 
 
867
  *
868
  * @since 1.0.0
869
  * @change 1.7.0
870
  */
871
-
872
  public static function add_admin_resources( $hook ) {
873
 
874
- // settings page
875
  if ( $hook === 'settings_page_cache-enabler' ) {
876
  wp_enqueue_style( 'cache-enabler-settings', plugins_url( 'css/settings.min.css', CACHE_ENABLER_FILE ), array(), CACHE_ENABLER_VERSION );
877
  }
878
  }
879
 
880
-
881
  /**
882
- * add settings page
883
  *
884
- * @since 1.0.0
885
- * @change 1.0.0
 
 
 
886
  */
887
-
888
  public static function add_settings_page() {
889
 
890
  add_options_page(
@@ -896,86 +1247,79 @@ final class Cache_Enabler {
896
  );
897
  }
898
 
899
-
900
  /**
901
- * check if user can clear cache
902
  *
903
  * @since 1.6.0
904
- * @change 1.6.0
905
  *
906
- * @return boolean true if user can clear cache, false otherwise
907
  */
908
-
909
  private static function user_can_clear_cache() {
910
 
911
- if ( apply_filters( 'cache_enabler_user_can_clear_cache', current_user_can( 'manage_options' ) ) ) {
912
- return true;
913
- }
914
-
915
- if ( apply_filters_deprecated( 'user_can_clear_cache', array( current_user_can( 'manage_options' ) ), '1.6.0', 'cache_enabler_user_can_clear_cache' ) ) {
916
- return true;
917
- }
918
-
919
- return false;
 
 
 
920
  }
921
 
922
-
923
  /**
924
- * process clear cache request
925
  *
926
- * @since 1.0.0
927
- * @change 1.7.0
 
 
 
928
  */
929
-
930
  public static function process_clear_cache_request() {
931
 
932
- // check if clear cache request
933
  if ( empty( $_GET['_cache'] ) || empty( $_GET['_action'] ) || $_GET['_cache'] !== 'cache-enabler' || ( $_GET['_action'] !== 'clear' && $_GET['_action'] !== 'clearurl' ) ) {
934
  return;
935
  }
936
 
937
- // validate nonce
938
  if ( empty( $_GET['_wpnonce'] ) || ! wp_verify_nonce( $_GET['_wpnonce'], 'cache_enabler_clear_cache_nonce' ) ) {
939
  return;
940
  }
941
 
942
- // check user role
943
  if ( ! self::user_can_clear_cache() ) {
944
  return;
945
  }
946
 
947
- // clear page cache
948
  if ( $_GET['_action'] === 'clearurl' ) {
949
- $clear_url = parse_url( home_url(), PHP_URL_SCHEME ) . '://' . Cache_Enabler_Engine::$request_headers['Host'] . $_SERVER['REQUEST_URI'];
950
- self::clear_page_cache_by_url( $clear_url );
951
- // clear site(s) cache
952
  } elseif ( $_GET['_action'] === 'clear' ) {
953
- self::each_site( ( is_multisite() && is_network_admin() ), 'self::clear_site_cache' );
954
  }
955
 
956
- // redirect to same page
957
  wp_safe_redirect( remove_query_arg( array( '_cache', '_action', '_wpnonce' ) ) );
958
 
959
- // set transient for clear notice
960
  if ( is_admin() ) {
961
  set_transient( self::get_cache_cleared_transient_name(), 1 );
962
  }
963
 
964
- // clear cache request completed
965
  exit;
966
  }
967
 
968
-
969
  /**
970
- * admin notice after cache has been cleared
971
  *
972
- * @since 1.0.0
 
 
973
  * @change 1.7.0
974
  */
975
-
976
  public static function cache_cleared_notice() {
977
 
978
- // check user role
979
  if ( ! self::user_can_clear_cache() ) {
980
  return;
981
  }
@@ -990,464 +1334,910 @@ final class Cache_Enabler {
990
  }
991
  }
992
 
993
-
994
  /**
995
- * save or trash post hook
 
 
 
996
  *
997
  * @since 1.5.0
998
  * @change 1.7.0
999
  *
1000
- * @param integer $post_id post ID
1001
  */
1002
-
1003
  public static function on_save_trash_post( $post_id ) {
1004
 
1005
  $post_status = get_post_status( $post_id );
1006
 
1007
- // if any published post type has been created, updated, or about to be trashed
1008
  if ( $post_status === 'publish' ) {
1009
  self::clear_cache_on_post_save( $post_id );
1010
  }
1011
  }
1012
 
1013
-
1014
  /**
1015
- * pre post update hook
1016
  *
1017
- * @since 1.7.0
1018
- * @change 1.7.0
1019
  *
1020
- * @param integer $post_id post ID
1021
- * @param array $post_data unslashed post data
 
 
1022
  */
1023
-
1024
  public static function on_pre_post_update( $post_id, $post_data ) {
1025
 
1026
  $old_post_status = get_post_status( $post_id );
1027
  $new_post_status = $post_data['post_status'];
1028
 
1029
- // if any published post type is about to be updated but not trashed
1030
  if ( $old_post_status === 'publish' && $new_post_status !== 'trash' ) {
1031
  self::clear_cache_on_post_save( $post_id );
1032
  }
1033
  }
1034
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1035
 
1036
  /**
1037
- * comment post hook
1038
  *
1039
- * @since 1.2.0
1040
- * @change 1.6.0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1041
  *
1042
- * @param integer $comment_id comment ID
1043
- * @param integer|string $comment_approved comment approval status
 
 
 
 
1044
  */
 
1045
 
1046
- public static function on_comment_post( $comment_id, $comment_approved ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1047
 
1048
- // if new approved comment is posted
1049
- if ( $comment_approved === 1 ) {
1050
- // if setting enabled clear site cache
1051
- if ( Cache_Enabler_Engine::$settings['clear_site_cache_on_saved_comment'] ) {
1052
- self::clear_site_cache();
1053
- // clear page cache otherwise
1054
- } else {
1055
- self::clear_page_cache_by_post_id( get_comment( $comment_id )->comment_post_ID );
1056
  }
1057
  }
1058
  }
1059
 
1060
-
1061
  /**
1062
- * edit comment hook
1063
  *
1064
- * @since 1.0.0
1065
- * @change 1.6.1
1066
  *
1067
- * @param integer $comment_id comment ID
1068
- * @param array $comment_data comment data
1069
  */
 
1070
 
1071
- public static function on_edit_comment( $comment_id, $comment_data ) {
1072
 
1073
- $comment_approved = (int) $comment_data['comment_approved'];
 
1074
 
1075
- // if approved comment is edited
1076
- if ( $comment_approved === 1 ) {
1077
- // if setting enabled clear site cache
1078
- if ( Cache_Enabler_Engine::$settings['clear_site_cache_on_saved_comment'] ) {
1079
- self::clear_site_cache();
1080
- // clear page cache otherwise
1081
- } else {
1082
- self::clear_page_cache_by_post_id( get_comment( $comment_id )->comment_post_ID );
1083
  }
1084
  }
1085
  }
1086
 
1087
-
1088
  /**
1089
- * transition comment status hook
1090
  *
1091
- * @since 1.0.0
1092
- * @change 1.6.1
1093
  *
1094
- * @param integer|string $new_status new comment status
1095
- * @param integer|string $old_status old comment status
1096
- * @param WP_Comment $comment comment instance
1097
  */
 
1098
 
1099
- public static function on_transition_comment_status( $new_status, $old_status, $comment ) {
1100
 
1101
- // if comment status has changed from or to approved
1102
- if ( $old_status === 'approved' || $new_status === 'approved' ) {
1103
- // if setting enabled clear site cache
1104
- if ( Cache_Enabler_Engine::$settings['clear_site_cache_on_saved_comment'] ) {
1105
- self::clear_site_cache();
1106
- // clear page cache otherwise
1107
- } else {
1108
- self::clear_page_cache_by_post_id( $comment->comment_post_ID );
1109
- }
1110
  }
1111
  }
1112
 
1113
-
1114
  /**
1115
- * WooCommerce stock hooks
1116
  *
1117
- * @since 1.3.0
1118
- * @change 1.6.1
1119
  *
1120
- * @param integer|WC_Product $product product ID or product instance
1121
  */
 
1122
 
1123
- public static function on_woocommerce_stock_update( $product ) {
1124
 
1125
- // get product ID
1126
- if ( is_int( $product ) ) {
1127
- $product_id = $product;
1128
- } else {
1129
- $product_id = $product->get_id();
1130
- }
 
1131
 
1132
- self::clear_cache_on_post_save( $product_id );
 
 
 
 
 
1133
  }
1134
 
1135
-
1136
  /**
1137
- * clear complete cache
1138
  *
1139
- * @since 1.0.0
1140
- * @change 1.6.0
1141
  */
 
1142
 
1143
- public static function clear_complete_cache() {
 
1144
 
1145
- // clear site(s) cache
1146
- self::each_site( is_multisite(), 'self::clear_site_cache' );
 
 
 
 
 
1147
 
1148
- // delete cache size transient(s)
1149
- self::each_site( is_multisite(), 'delete_transient', array( self::get_cache_size_transient_name() ) );
1150
  }
1151
 
1152
-
1153
  /**
1154
- * clear complete cache (deprecated)
1155
  *
1156
- * @since 1.0.0
1157
- * @deprecated 1.5.0
1158
  */
 
1159
 
1160
- public static function clear_total_cache() {
1161
-
1162
- self::clear_complete_cache();
1163
  }
1164
 
1165
-
1166
  /**
1167
- * clear site cache
1168
  *
1169
- * @since 1.6.0
1170
- * @change 1.6.0
 
 
1171
  */
 
1172
 
1173
- public static function clear_site_cache() {
1174
 
1175
- self::clear_site_cache_by_blog_id( get_current_blog_id() );
1176
- }
 
 
 
 
1177
 
 
 
 
 
 
1178
 
1179
  /**
1180
- * clear cached pages that might have changed from any new or updated post
1181
  *
1182
- * @since 1.5.0
1183
- * @change 1.5.0
1184
  *
1185
- * @param WP_Post $post post instance
 
1186
  */
 
1187
 
1188
- public static function clear_associated_cache( $post ) {
1189
-
1190
- // clear post type archives
1191
- self::clear_post_type_archives_cache( $post->post_type );
1192
 
1193
- // clear taxonomies archives
1194
- self::clear_taxonomies_archives_cache_by_post_id( $post->ID );
1195
 
1196
- if ( $post->post_type === 'post' ) {
1197
- // clear author archives
1198
- self::clear_author_archives_cache_by_user_id( $post->post_author );
1199
- // clear date archives
1200
- self::clear_date_archives_cache_by_post_id( $post->ID );
1201
  }
1202
  }
1203
 
1204
-
1205
  /**
1206
- * clear post type archives page cache
1207
  *
1208
- * @since 1.5.0
1209
- * @change 1.5.0
1210
  *
1211
- * @param string $post_type post type
 
1212
  */
 
1213
 
1214
- public static function clear_post_type_archives_cache( $post_type ) {
1215
 
1216
- // get post type archives URL
1217
- $post_type_archives_url = get_post_type_archive_link( $post_type );
1218
 
1219
- // if post type archives URL exists clear post type archives page and its pagination page(s) cache
1220
- if ( ! empty( $post_type_archives_url ) ) {
1221
- self::clear_page_cache_by_url( $post_type_archives_url, 'pagination' );
1222
  }
1223
  }
1224
 
1225
-
1226
  /**
1227
- * clear taxonomies archives pages cache by post ID
1228
  *
1229
- * @since 1.5.0
1230
- * @change 1.7.0
1231
  *
1232
- * @param integer $post_id post ID
 
1233
  */
 
1234
 
1235
- public static function clear_taxonomies_archives_cache_by_post_id( $post_id ) {
1236
-
1237
- // get taxonomies
1238
- $taxonomies = get_taxonomies();
 
1239
 
1240
- foreach ( $taxonomies as $taxonomy ) {
1241
- if ( wp_count_terms( $taxonomy ) > 0 ) {
1242
- // get terms attached to post
1243
- $term_ids = wp_get_post_terms( $post_id, $taxonomy, array( 'fields' => 'ids' ) );
1244
 
1245
- foreach ( $term_ids as $term_id ) {
1246
- // get term archives URL
1247
- $term_archives_url = get_term_link( (int) $term_id, $taxonomy );
1248
 
1249
- // if term archives URL exists and does not have a query string clear taxonomy archives page and its pagination page(s) cache
1250
- if ( ! is_wp_error( $term_archives_url ) && strpos( $term_archives_url, '?' ) === false ) {
1251
- self::clear_page_cache_by_url( $term_archives_url, 'pagination' );
1252
- }
1253
- }
1254
  }
1255
  }
1256
  }
1257
 
1258
-
1259
  /**
1260
- * clear author archives page cache by user ID
1261
  *
1262
- * @since 1.5.0
1263
- * @change 1.5.0
1264
  *
1265
- * @param integer $user_id user ID of the author
 
 
1266
  */
 
1267
 
1268
- public static function clear_author_archives_cache_by_user_id( $user_id ) {
 
 
 
 
 
 
 
1269
 
1270
- // get author archives URL
1271
- $author_username = get_the_author_meta( 'user_login', $user_id );
1272
- $author_base = $GLOBALS['wp_rewrite']->author_base;
1273
- $author_archives_url = home_url( '/' ) . $author_base . '/' . $author_username;
1274
 
1275
- // clear author archives page and its pagination page(s) cache
1276
- self::clear_page_cache_by_url( $author_archives_url, 'pagination' );
1277
  }
1278
 
 
 
 
 
 
 
 
 
 
 
1279
 
1280
  /**
1281
- * clear date archives pages cache
1282
  *
1283
- * @since 1.5.0
1284
- * @change 1.5.0
1285
  *
1286
- * @param integer $post_id post ID
 
 
1287
  */
 
1288
 
1289
- public static function clear_date_archives_cache_by_post_id( $post_id ) {
1290
 
1291
- // get post dates
1292
- $post_date_day = get_the_date( 'd', $post_id );
1293
- $post_date_month = get_the_date( 'm', $post_id );
1294
- $post_date_year = get_the_date( 'Y', $post_id );
1295
 
1296
- // get post dates archives URLs
1297
- $date_archives_day_url = get_day_link( $post_date_year, $post_date_month, $post_date_day );
1298
- $date_archives_month_url = get_month_link( $post_date_year, $post_date_month );
1299
- $date_archives_year_url = get_year_link( $post_date_year );
1300
 
1301
- // clear date archives pages and their pagination pages cache
1302
- self::clear_page_cache_by_url( $date_archives_day_url, 'pagination' );
1303
- self::clear_page_cache_by_url( $date_archives_month_url, 'pagination' );
1304
- self::clear_page_cache_by_url( $date_archives_year_url, 'pagination' );
1305
  }
1306
 
1307
-
1308
  /**
1309
- * clear page cache by post ID
1310
  *
1311
- * @since 1.0.0
1312
- * @change 1.7.0
1313
  *
1314
- * @param integer|string $post_id post ID
1315
- * @param string $clear_type clear the `pagination` cache or all `subpages` cache instead of only the `page` cache
 
1316
  */
 
1317
 
1318
- public static function clear_page_cache_by_post_id( $post_id, $clear_type = 'page' ) {
1319
-
1320
- // validate integer
1321
- $post_id = (int) $post_id;
1322
 
1323
- // get page URL
1324
- $page_url = ( $post_id ) ? get_permalink( $post_id ) : '';
 
 
1325
 
1326
- // if page URL exists and does not have a query string (e.g. guid) clear page cache
1327
- if ( ! empty( $page_url ) && strpos( $page_url, '?' ) === false ) {
1328
- self::clear_page_cache_by_url( $page_url, $clear_type );
1329
  }
1330
  }
1331
 
1332
-
1333
  /**
1334
- * clear page cache by URL
1335
  *
1336
- * @since 1.0.0
1337
- * @change 1.6.0
1338
  *
1339
- * @param string $clear_url full URL to potentially cached page
1340
- * @param string $clear_type clear the `pagination` cache or all `subpages` cache instead of only the `page` cache
 
 
 
 
1341
  */
 
1342
 
1343
- public static function clear_page_cache_by_url( $clear_url, $clear_type = 'page' ) {
1344
 
1345
- Cache_Enabler_Disk::clear_cache( $clear_url, $clear_type );
1346
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1347
 
 
 
 
 
 
 
1348
 
1349
  /**
1350
- * clear site cache by blog ID
1351
  *
1352
- * @since 1.4.0
1353
- * @change 1.7.0
 
 
1354
  *
1355
- * @param integer|string $blog_id blog ID
1356
- * @param boolean $delete_cache_size_transient whether or not the cache size transient should be deleted
 
1357
  */
 
1358
 
1359
- public static function clear_site_cache_by_blog_id( $blog_id, $delete_cache_size_transient = true ) {
1360
-
1361
- // validate integer
1362
- $blog_id = (int) $blog_id;
1363
 
1364
- // check if blog ID exists
1365
- if ( ! in_array( $blog_id, self::get_blog_ids(), true ) ) {
1366
  return;
1367
  }
1368
 
1369
- // ensure site cache being cleared is current blog
1370
- if ( is_multisite() ) {
1371
- switch_to_blog( $blog_id );
1372
- }
 
 
 
 
 
 
1373
 
1374
- // disable page cache cleared hook
1375
- self::$fire_page_cache_cleared_hook = false;
1376
 
1377
- // get site URL
1378
- $site_url = home_url();
 
 
1379
 
1380
- // get site objects
1381
- $site_objects = Cache_Enabler_Disk::get_site_objects( $site_url );
1382
 
1383
- // clear all first level pages and subpages cache
1384
- foreach ( $site_objects as $site_object ) {
1385
- self::clear_page_cache_by_url( trailingslashit( $site_url ) . $site_object, 'subpages' );
1386
- }
1387
 
1388
- // clear home page cache
1389
- self::clear_page_cache_by_url( $site_url );
 
 
1390
 
1391
- // delete cache size transient
1392
- if ( $delete_cache_size_transient ) {
1393
- delete_transient( self::get_cache_size_transient_name() );
1394
  }
 
1395
 
1396
- // restore current blog from before site cache being cleared
1397
- if ( is_multisite() ) {
1398
- restore_current_blog();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1399
  }
 
 
1400
  }
1401
 
 
 
 
 
 
 
 
 
 
 
1402
 
1403
  /**
1404
- * clear cache when any post type has been created, updated, or trashed
1405
  *
1406
  * @since 1.5.0
1407
- * @change 1.7.0
1408
  *
1409
- * @param integer|WP_Post $post post ID or post instance
1410
  */
1411
-
1412
  public static function clear_cache_on_post_save( $post ) {
1413
 
1414
- if ( is_int( $post ) ) {
1415
- $post_id = $post;
1416
- $post = get_post( $post_id );
 
 
 
 
 
 
 
 
 
 
 
 
1417
 
1418
- if ( ! is_object( $post ) ) {
1419
- return;
1420
- }
1421
- } elseif ( is_object( $post ) ) {
1422
- $post_id = $post->ID;
1423
  }
 
1424
 
1425
- // if setting enabled clear site cache
1426
- if ( Cache_Enabler_Engine::$settings['clear_site_cache_on_saved_post'] ) {
 
 
 
 
 
 
 
 
 
1427
  self::clear_site_cache();
1428
- // clear page and/or associated cache otherwise
1429
  } else {
1430
- self::clear_page_cache_by_post_id( $post_id );
1431
- self::clear_associated_cache( $post );
1432
  }
1433
  }
1434
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1435
 
1436
  /**
1437
- * check plugin requirements
1438
  *
1439
- * @since 1.1.0
1440
- * @change 1.7.0
 
 
 
1441
  */
 
 
 
 
 
 
 
 
 
 
 
1442
 
 
 
 
 
 
 
 
 
1443
  public static function requirements_check() {
1444
 
1445
- // check user role
1446
  if ( ! current_user_can( 'manage_options' ) ) {
1447
  return;
1448
  }
1449
 
1450
- // check PHP version
1451
  if ( version_compare( PHP_VERSION, CACHE_ENABLER_MIN_PHP, '<' ) ) {
1452
  printf(
1453
  '<div class="notice notice-error"><p>%s</p></div>',
@@ -1460,7 +2250,7 @@ final class Cache_Enabler {
1460
  );
1461
  }
1462
 
1463
- // check WordPress version
1464
  if ( version_compare( $GLOBALS['wp_version'], CACHE_ENABLER_MIN_WP . 'alpha', '<' ) ) {
1465
  printf(
1466
  '<div class="notice notice-error"><p>%s</p></div>',
@@ -1473,23 +2263,38 @@ final class Cache_Enabler {
1473
  );
1474
  }
1475
 
1476
- // check advanced-cache.php drop-in
1477
- if ( ! file_exists( WP_CONTENT_DIR . '/advanced-cache.php' ) ) {
1478
  printf(
1479
  '<div class="notice notice-warning"><p>%s</p></div>',
1480
  sprintf(
1481
  // translators: 1. Cache Enabler 2. advanced-cache.php 3. wp-content/plugins/cache-enabler 4. wp-content
1482
- esc_html__( '%1$s requires the %2$s drop-in. Please deactivate and then activate the plugin to automatically copy this file or manually copy it from the %3$s directory to the %4$s directory.', 'cache-enabler' ),
1483
  '<strong>Cache Enabler</strong>',
1484
  '<code>advanced-cache.php</code>',
1485
- '<code>wp-content/plugins/cache-enabler</code>',
1486
- '<code>wp-content</code>'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1487
  )
1488
  );
1489
  }
1490
 
1491
- // check permalink structure
1492
- if ( Cache_Enabler_Engine::$settings['permalink_structure'] === 'plain' && current_user_can( 'manage_options' ) ) {
1493
  printf(
1494
  '<div class="notice notice-warning"><p>%s</p></div>',
1495
  sprintf(
@@ -1505,26 +2310,30 @@ final class Cache_Enabler {
1505
  );
1506
  }
1507
 
1508
- // check file permissions
1509
- if ( file_exists( dirname( Cache_Enabler_Disk::$cache_dir ) ) && ! is_writable( dirname( Cache_Enabler_Disk::$cache_dir ) ) ) {
1510
- printf(
1511
- '<div class="notice notice-warning"><p>%s</p></div>',
1512
- sprintf(
1513
- // translators: 1. Cache Enabler 2. 755 3. wp-content/cache 4. file permissions
1514
- esc_html__( '%1$s requires write permissions %2$s in the %3$s directory. Please change the %4$s.', 'cache-enabler' ),
1515
- '<strong>Cache Enabler</strong>',
1516
- '<code>755</code>',
1517
- '<code>wp-content/cache</code>',
1518
  sprintf(
1519
- '<a href="%s" target="_blank" rel="nofollow noopener">%s</a>',
1520
- 'https://wordpress.org/support/article/changing-file-permissions/',
1521
- esc_html__( 'file permissions', 'cache-enabler' )
 
 
 
 
 
 
 
1522
  )
1523
- )
1524
- );
1525
  }
1526
 
1527
- // check Autoptimize HTML optimization
1528
  if ( defined( 'AUTOPTIMIZE_PLUGIN_DIR' ) && Cache_Enabler_Engine::$settings['minify_html'] && get_option( 'autoptimize_html', '' ) !== '' ) {
1529
  printf(
1530
  '<div class="notice notice-warning"><p>%s</p></div>',
@@ -1542,44 +2351,76 @@ final class Cache_Enabler {
1542
  }
1543
  }
1544
 
1545
-
1546
  /**
1547
- * register textdomain
1548
  *
1549
- * @since 1.0.0
1550
- * @change 1.0.0
1551
  */
1552
-
1553
  public static function register_textdomain() {
1554
 
1555
- // load translated strings
1556
  load_plugin_textdomain( 'cache-enabler', false, 'cache-enabler/lang' );
1557
  }
1558
 
1559
-
1560
  /**
1561
- * register settings
1562
  *
1563
  * @since 1.0.0
1564
  * @change 1.5.0
1565
  */
1566
-
1567
  public static function register_settings() {
1568
 
1569
  register_setting( 'cache_enabler', 'cache_enabler', array( __CLASS__, 'validate_settings' ) );
1570
  }
1571
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1572
 
1573
  /**
1574
- * validate regex
1575
  *
1576
  * @since 1.2.3
1577
  * @change 1.5.0
1578
  *
1579
- * @param string $regex string containing regex
1580
- * @return string $validated_regex string containing regex or empty string if input is invalid
1581
  */
1582
-
1583
  public static function validate_regex( $regex ) {
1584
 
1585
  if ( ! empty( $regex ) ) {
@@ -1599,24 +2440,24 @@ final class Cache_Enabler {
1599
  return '';
1600
  }
1601
 
1602
-
1603
  /**
1604
- * validate settings
1605
  *
1606
  * @since 1.0.0
1607
- * @change 1.7.0
1608
  *
1609
- * @param array $settings user defined settings
1610
- * @return array $validated_settings validated settings
1611
  */
1612
-
1613
  public static function validate_settings( $settings ) {
1614
 
1615
  $validated_settings = array(
1616
  'cache_expires' => (int) ( ! empty( $settings['cache_expires'] ) ),
1617
- 'cache_expiry_time' => (int) $settings['cache_expiry_time'],
1618
  'clear_site_cache_on_saved_post' => (int) ( ! empty( $settings['clear_site_cache_on_saved_post'] ) ),
1619
  'clear_site_cache_on_saved_comment' => (int) ( ! empty( $settings['clear_site_cache_on_saved_comment'] ) ),
 
 
1620
  'clear_site_cache_on_changed_plugin' => (int) ( ! empty( $settings['clear_site_cache_on_changed_plugin'] ) ),
1621
  'convert_image_urls_to_webp' => (int) ( ! empty( $settings['convert_image_urls_to_webp'] ) ),
1622
  'mobile_cache' => (int) ( ! empty( $settings['mobile_cache'] ) ),
@@ -1629,10 +2470,8 @@ final class Cache_Enabler {
1629
  'excluded_cookies' => (string) self::validate_regex( $settings['excluded_cookies'] ),
1630
  );
1631
 
1632
- // add default system settings
1633
  $validated_settings = wp_parse_args( $validated_settings, self::get_default_settings( 'system' ) );
1634
 
1635
- // check if site cache should be cleared
1636
  if ( ! empty( $settings['clear_site_cache_on_saved_settings'] ) ) {
1637
  self::clear_site_cache();
1638
  set_transient( self::get_cache_cleared_transient_name(), 1 );
@@ -1641,14 +2480,12 @@ final class Cache_Enabler {
1641
  return $validated_settings;
1642
  }
1643
 
1644
-
1645
  /**
1646
- * settings page
1647
  *
1648
  * @since 1.0.0
1649
- * @change 1.7.0
1650
  */
1651
-
1652
  public static function settings_page() {
1653
 
1654
  ?>
@@ -1712,14 +2549,28 @@ final class Cache_Enabler {
1712
  <p class="subheading"><?php esc_html_e( 'Clearing', 'cache-enabler' ); ?></p>
1713
  <label for="cache_enabler_clear_site_cache_on_saved_post">
1714
  <input name="cache_enabler[clear_site_cache_on_saved_post]" type="checkbox" id="cache_enabler_clear_site_cache_on_saved_post" value="1" <?php checked( '1', Cache_Enabler_Engine::$settings['clear_site_cache_on_saved_post'] ); ?> />
1715
- <?php esc_html_e( 'Clear the site cache if any post type has been published, updated, or trashed (instead of only the page and/or associated cache).', 'cache-enabler' ); ?>
1716
  </label>
1717
 
1718
  <br />
1719
 
1720
  <label for="cache_enabler_clear_site_cache_on_saved_comment">
1721
  <input name="cache_enabler[clear_site_cache_on_saved_comment]" type="checkbox" id="cache_enabler_clear_site_cache_on_saved_comment" value="1" <?php checked( '1', Cache_Enabler_Engine::$settings['clear_site_cache_on_saved_comment'] ); ?> />
1722
- <?php esc_html_e( 'Clear the site cache if a comment has been posted, updated, spammed, or trashed (instead of only the page cache).', 'cache-enabler' ); ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1723
  </label>
1724
 
1725
  <br />
@@ -1731,13 +2582,13 @@ final class Cache_Enabler {
1731
 
1732
  <br />
1733
 
1734
- <p class="subheading"><?php esc_html_e( 'Variants', 'cache-enabler' ); ?></p>
1735
  <label for="cache_enabler_convert_image_urls_to_webp">
1736
  <input name="cache_enabler[convert_image_urls_to_webp]" type="checkbox" id="cache_enabler_convert_image_urls_to_webp" value="1" <?php checked( '1', Cache_Enabler_Engine::$settings['convert_image_urls_to_webp'] ); ?> />
1737
  <?php
1738
  printf(
1739
  // translators: %s: Optimus
1740
- esc_html__( 'Create an additional cached version for WebP image support. Convert your images to WebP with %s.', 'cache-enabler' ),
1741
  '<a href="https://optimus.io" target="_blank" rel="nofollow noopener">Optimus</a>'
1742
  );
1743
  ?>
@@ -1747,14 +2598,14 @@ final class Cache_Enabler {
1747
 
1748
  <label for="cache_enabler_mobile_cache">
1749
  <input name="cache_enabler[mobile_cache]" type="checkbox" id="cache_enabler_mobile_cache" value="1" <?php checked( '1', Cache_Enabler_Engine::$settings['mobile_cache'] ); ?> />
1750
- <?php esc_html_e( 'Create an additional cached version for mobile devices.', 'cache-enabler' ); ?>
1751
  </label>
1752
 
1753
  <br />
1754
 
1755
  <label for="cache_enabler_compress_cache">
1756
  <input name="cache_enabler[compress_cache]" type="checkbox" id="cache_enabler_compress_cache" value="1" <?php checked( '1', Cache_Enabler_Engine::$settings['compress_cache'] ); ?> />
1757
- <?php esc_html_e( 'Pre-compress cached pages with Gzip.', 'cache-enabler' ); ?>
1758
  </label>
1759
 
1760
  <br />
1
  <?php
2
  /**
3
+ * Class used for handling base plugin operations.
4
  *
5
  * @since 1.0.0
6
  */
10
  }
11
 
12
  final class Cache_Enabler {
 
13
  /**
14
+ * Initialize the plugin.
15
  *
16
+ * @since 1.5.0
 
17
  */
 
18
  public static function init() {
19
 
20
  new self();
21
  }
22
 
 
23
  /**
24
+ * Settings from the database (deprecated).
25
  *
26
  * @since 1.0.0
27
  * @deprecated 1.5.0
28
  */
 
29
  public static $options;
30
 
 
31
  /**
32
+ * Fire the page cache cleared hook (deprecated).
 
 
 
33
  *
34
+ * @since 1.6.0
35
+ * @deprecated 1.8.0
36
  */
 
37
  public static $fire_page_cache_cleared_hook = true;
38
 
 
39
  /**
40
+ * Constructor.
41
+ *
42
+ * This is called by self::init() and sets up the plugin.
43
  *
44
  * @since 1.0.0
45
+ * @change 1.8.0
46
  */
 
47
  public function __construct() {
48
 
49
+ // Init hooks.
50
  add_action( 'init', array( 'Cache_Enabler_Engine', 'start' ) );
51
  add_action( 'init', array( __CLASS__, 'process_clear_cache_request' ) );
52
  add_action( 'init', array( __CLASS__, 'register_textdomain' ) );
53
+ add_action( 'init', array( __CLASS__, 'schedule_events' ) );
54
+
55
+ // Option hooks.
56
+ add_action( 'add_option', array( __CLASS__, 'on_add_option' ), 10, 2 );
57
+ add_action( 'update_option', array( __CLASS__, 'on_update_option' ), 10, 3 );
58
+ add_action( 'updated_option', array( __CLASS__, 'on_updated_option' ), 10, 3 );
59
 
60
+ // Public clear cache hooks.
61
  add_action( 'cache_enabler_clear_complete_cache', array( __CLASS__, 'clear_complete_cache' ) );
62
  add_action( 'cache_enabler_clear_site_cache', array( __CLASS__, 'clear_site_cache' ) );
63
+ add_action( 'cache_enabler_clear_expired_cache', array( __CLASS__, 'clear_expired_cache' ) );
64
+ add_action( 'cache_enabler_clear_page_cache_by_post', array( __CLASS__, 'clear_page_cache_by_post' ) );
65
  add_action( 'cache_enabler_clear_page_cache_by_url', array( __CLASS__, 'clear_page_cache_by_url' ) );
66
+ add_action( 'cache_enabler_clear_site_cache_by_blog_id', array( __CLASS__, 'clear_page_cache_by_site' ) ); // Deprecated in 1.8.0.
67
+ add_action( 'cache_enabler_clear_page_cache_by_post_id', array( __CLASS__, 'clear_page_cache_by_post' ) ); // Deprecated in 1.8.0.
68
+ add_action( 'ce_clear_cache', array( __CLASS__, 'clear_complete_cache' ) ); // Deprecated in 1.6.0.
69
+ add_action( 'ce_clear_post_cache', array( __CLASS__, 'clear_page_cache_by_post' ) ); // Deprecated in 1.6.0.
70
 
71
+ // System clear cache hooks.
 
72
  add_action( 'upgrader_process_complete', array( __CLASS__, 'on_upgrade' ), 10, 2 );
 
 
73
  add_action( 'activated_plugin', array( __CLASS__, 'on_plugin_activation_deactivation' ), 10, 2 );
74
  add_action( 'deactivated_plugin', array( __CLASS__, 'on_plugin_activation_deactivation' ), 10, 2 );
75
  add_action( 'save_post', array( __CLASS__, 'on_save_trash_post' ) );
 
76
  add_action( 'pre_post_update', array( __CLASS__, 'on_pre_post_update' ), 10, 2 );
77
+ add_action( 'wp_trash_post', array( __CLASS__, 'on_save_trash_post' ) );
78
  add_action( 'comment_post', array( __CLASS__, 'on_comment_post' ), 99, 2 );
79
  add_action( 'edit_comment', array( __CLASS__, 'on_edit_comment' ), 10, 2 );
80
  add_action( 'transition_comment_status', array( __CLASS__, 'on_transition_comment_status' ), 10, 3 );
81
+ add_action( 'saved_term', array( __CLASS__, 'on_saved_delete_term' ), 10, 3 );
82
+ add_action( 'edit_terms', array( __CLASS__, 'on_edit_terms' ), 10, 2 );
83
+ add_action( 'delete_term', array( __CLASS__, 'on_saved_delete_term' ), 10, 3 );
84
+ add_action( 'user_register', array( __CLASS__, 'on_register_update_delete_user' ) );
85
+ add_action( 'profile_update', array( __CLASS__, 'on_register_update_delete_user' ) );
86
+ add_action( 'delete_user', array( __CLASS__, 'on_register_update_delete_user' ) );
87
+ add_action( 'deleted_user', array( __CLASS__, 'on_deleted_user' ), 10, 2 );
88
+
89
+ // Third party clear cache hooks.
90
  add_action( 'autoptimize_action_cachepurged', array( __CLASS__, 'clear_complete_cache' ) );
91
  add_action( 'woocommerce_product_set_stock', array( __CLASS__, 'on_woocommerce_stock_update' ) );
92
  add_action( 'woocommerce_variation_set_stock', array( __CLASS__, 'on_woocommerce_stock_update' ) );
93
  add_action( 'woocommerce_product_set_stock_status', array( __CLASS__, 'on_woocommerce_stock_update' ) );
94
  add_action( 'woocommerce_variation_set_stock_status', array( __CLASS__, 'on_woocommerce_stock_update' ) );
95
 
96
+ // System cache created/cleared hooks.
97
+ add_action( 'cache_enabler_page_cache_created', array( __CLASS__, 'on_cache_created_cleared' ), 10, 3 );
98
+ add_action( 'cache_enabler_site_cache_cleared', array( __CLASS__, 'on_cache_created_cleared' ), 10, 3 );
99
+ add_action( 'cache_enabler_page_cache_cleared', array( __CLASS__, 'on_cache_created_cleared' ), 10, 3 );
100
+
101
+ // Multisite hooks.
102
  add_action( 'wp_initialize_site', array( __CLASS__, 'install_later' ) );
103
  add_action( 'wp_uninitialize_site', array( __CLASS__, 'uninstall_later' ) );
104
 
105
+ // Admin bar hook.
 
 
 
 
 
106
  add_action( 'admin_bar_menu', array( __CLASS__, 'add_admin_bar_items' ), 90 );
107
 
108
+ // Admin interface hooks.
109
  if ( is_admin() ) {
 
110
  add_action( 'admin_init', array( __CLASS__, 'register_settings' ) );
111
  add_action( 'admin_menu', array( __CLASS__, 'add_settings_page' ) );
112
  add_action( 'admin_enqueue_scripts', array( __CLASS__, 'add_admin_resources' ) );
 
113
  add_filter( 'dashboard_glance_items', array( __CLASS__, 'add_dashboard_cache_size' ) );
114
  add_filter( 'plugin_action_links_' . CACHE_ENABLER_BASE, array( __CLASS__, 'add_plugin_action_links' ) );
115
  add_filter( 'plugin_row_meta', array( __CLASS__, 'add_plugin_row_meta' ), 10, 2 );
 
116
  add_action( 'admin_notices', array( __CLASS__, 'requirements_check' ) );
117
  add_action( 'admin_notices', array( __CLASS__, 'cache_cleared_notice' ) );
118
  add_action( 'network_admin_notices', array( __CLASS__, 'cache_cleared_notice' ) );
119
  }
120
  }
121
 
 
122
  /**
123
+ * When the plugin is activated.
124
+ *
125
+ * This runs on the 'activate_cache-enabler/cache-enabler.php' action. It adds or
126
+ * updates the 'cache_enabler' option in the database for each site Cache Enabler
127
+ * is activated on, creates the advanced-cache.php file in the wp-content
128
+ * directory, and then maybe sets the WP_CACHE constant in the wp-config.php file.
129
  *
130
  * @since 1.0.0
131
  * @change 1.6.0
132
  *
133
+ * @param bool $network_wide True if the plugin was network activated, false otherwise.
134
  */
 
135
  public static function on_activation( $network_wide ) {
136
 
 
137
  self::each_site( $network_wide, 'self::update_backend' );
138
 
 
139
  Cache_Enabler_Disk::setup();
140
  }
141
 
 
142
  /**
143
+ * When the upgrader process is complete.
144
  *
145
+ * This runs on the 'upgrader_process_complete' action. It clears the cache when
146
+ * the core, themes, or plugins are updated.
147
+ *
148
+ * @since 1.4.0
149
+ * @change 1.8.0
150
  *
151
+ * @param WP_Upgrader $upgrader Upgrader instance.
152
+ * @param array $data Array of bulk item update data.
153
  */
154
+ public static function on_upgrade( $upgrader, $data ) {
 
155
 
156
  if ( $data['action'] !== 'update' ) {
157
  return;
158
  }
159
 
160
+ if ( $data['type'] === 'core' ) {
161
+ self::clear_complete_cache();
162
+ }
163
+
164
  if ( $data['type'] === 'theme' && isset( $data['themes'] ) ) {
165
  $updated_themes = (array) $data['themes'];
166
  $sites_themes = self::each_site( is_multisite(), 'wp_get_theme' );
167
 
 
168
  foreach ( $sites_themes as $blog_id => $site_theme ) {
169
+ // Clear the site cache if the active or parent theme has been updated.
170
  if ( in_array( $site_theme->stylesheet, $updated_themes, true ) || in_array( $site_theme->template, $updated_themes, true ) ) {
171
+ self::clear_page_cache_by_site( $blog_id );
172
  }
173
  }
174
  }
175
 
 
176
  if ( $data['type'] === 'plugin' && isset( $data['plugins'] ) ) {
177
  $updated_plugins = (array) $data['plugins'];
178
+ $network_plugins = is_multisite() ? array_flip( (array) get_site_option( 'active_sitewide_plugins', array() ) ) : array();
179
 
180
+ // Clear the complete cache if a network activated plugin has been updated.
181
+ if ( ! empty( array_intersect( $updated_plugins, $network_plugins ) ) ) {
182
+ self::clear_complete_cache();
 
183
  } else {
184
+ $sites_plugins = self::each_site( is_multisite(), 'get_option', array( 'active_plugins', array() ) );
185
+
186
+ foreach ( $sites_plugins as $blog_id => $site_plugins ) {
187
+ // Clear the site cache if an activated plugin has been updated.
188
+ if ( ! empty( array_intersect( $updated_plugins, (array) $site_plugins ) ) ) {
189
+ self::clear_page_cache_by_site( $blog_id );
 
 
 
 
 
 
 
 
190
  }
191
  }
192
  }
193
  }
194
  }
195
 
 
196
  /**
197
+ * When Cache Enabler is updated (deprecated).
198
  *
199
+ * @since 1.4.0
200
+ * @deprecated 1.8.0
201
  */
 
202
  public static function on_cache_enabler_update() {
203
 
 
204
  self::each_site( is_multisite(), 'Cache_Enabler_Disk::clean' );
205
 
 
206
  Cache_Enabler_Disk::setup();
207
 
 
208
  self::clear_complete_cache();
209
  }
210
 
 
211
  /**
212
+ * When the plugin is deactivated.
213
+ *
214
+ * This runs on the 'deactivate_cache-enabler/cache-enabler.php' action. It
215
+ * deletes the settings and advanced-cache.php files and then maybe unsets the
216
+ * WP_CACHE constant in the wp-config.php file. The site cache is then cleared and
217
+ * the WP-Cron events unscheduled.
218
  *
219
  * @since 1.0.0
220
+ * @change 1.8.0
221
  *
222
+ * @param bool $network_wide True if the plugin was network deactivated, false otherwise.
223
  */
 
224
  public static function on_deactivation( $network_wide ) {
225
 
 
226
  self::each_site( $network_wide, 'Cache_Enabler_Disk::clean' );
227
+ self::each_site( $network_wide, 'self::clear_site_cache', array(), true );
228
+ self::each_site( $network_wide, 'self::unschedule_events' );
 
229
  }
230
 
 
231
  /**
232
+ * When the plugin is uninstalled.
233
+ *
234
+ * This runs on the 'uninstall_cache-enabler/cache-enabler.php' action. It deletes
235
+ * the 'cache_enabler' option and 'cache_enabler_cache_size' transient from the
236
+ * database for each site.
237
  *
238
  * @since 1.0.0
239
  * @change 1.6.0
240
  */
 
241
  public static function on_uninstall() {
242
 
 
243
  self::each_site( is_multisite(), 'self::uninstall_backend' );
244
  }
245
 
246
+ /**
247
+ * When the cache is created or cleared.
248
+ *
249
+ * This runs on the 'cache_enabler_page_cache_created',
250
+ * 'cache_enabler_site_cache_cleared', and 'cache_enabler_page_cache_cleared'
251
+ * actions. It keeps the 'cache_enabler_cache_size' transient up to date.
252
+ *
253
+ * @since 1.8.0
254
+ *
255
+ * @param string $url Site or post URL.
256
+ * @param int $id Blog or post ID
257
+ * @param array $index Index of the cache created or cleared.
258
+ */
259
+ public static function on_cache_created_cleared( $url, $id, $index ) {
260
+
261
+ if ( is_multisite() && ! wp_is_site_initialized( get_current_blog_id() ) ) {
262
+ return;
263
+ }
264
+
265
+ $current_cache_size = get_transient( 'cache_enabler_cache_size' );
266
+
267
+ if ( $current_cache_size === false ) {
268
+ self::get_cache_size();
269
+ } else {
270
+ if ( count( $index ) > 1 ) {
271
+ // Prevent incorrect cache size being built just in case cache cleared index is not entire site.
272
+ delete_transient( 'cache_enabler_cache_size' );
273
+ } else {
274
+ // The changed cache size is negative when the cache is cleared.
275
+ $changed_cache_size = array_sum( current( $index )['versions'] );
276
+ $new_cache_size = $current_cache_size + $changed_cache_size;
277
+ $new_cache_size = ( $new_cache_size >= 0 ) ? $new_cache_size : 0;
278
+
279
+ set_transient( 'cache_enabler_cache_size', $new_cache_size, DAY_IN_SECONDS );
280
+ }
281
+ }
282
+ }
283
 
284
  /**
285
+ * When a site's initialization routine should be executed.
286
+ *
287
+ * This runs on the 'wp_initialize_site' action. If the plugin is network
288
+ * activated the 'cache_enabler' option will be added to the new site's database,
289
+ * triggering the new site's settings file to be created.
290
  *
291
  * @since 1.0.0
292
+ * @change 1.8.0
293
  *
294
+ * @param WP_Site $new_site New site instance.
295
  */
 
296
  public static function install_later( $new_site ) {
297
 
 
298
  if ( ! is_plugin_active_for_network( CACHE_ENABLER_BASE ) ) {
299
  return;
300
  }
301
 
302
+ self::switch_to_blog( (int) $new_site->blog_id );
 
 
 
303
  self::update_backend();
304
+ self::restore_current_blog();
 
 
305
  }
306
 
307
+ /**
308
+ * Update the disk and backend requirements.
309
+ *
310
+ * This update process begins by first deleting the settings and
311
+ * advanced-cache.php files and then maybe unsets the WP_CACHE constant in the
312
+ * wp-config.php file. A new advanced-cache.php file is then created and the
313
+ * WP_CACHE constant is maybe set. Next, the 'cache_enabler' option is updated in
314
+ * the database for each site Cache Enabler is considered active, which triggers a
315
+ * new settings file to be created each time that occurs. Lastly, the complete
316
+ * cache is cleared.
317
+ *
318
+ * @since 1.8.0
319
+ */
320
+ public static function update() {
321
+
322
+ self::update_disk();
323
+ self::each_site( is_multisite(), 'self::update_backend' );
324
+ self::clear_complete_cache();
325
+ }
326
 
327
  /**
328
+ * Add or update backend requirements.
329
+ *
330
+ * This adds or updates the 'cache_enabler' option in the database, which triggers
331
+ * the creation of the settings file. It will call self::on_update_backend() when
332
+ * the plugin actions have not been registered as hooks yet, like when the plugin
333
+ * is activated, but in this case even if the backend was not truly updated.
334
  *
335
  * @since 1.5.0
336
+ * @change 1.8.0
337
  *
338
+ * @return array The new or current option value.
339
  */
 
340
  public static function update_backend() {
341
 
342
+ delete_metadata( 'user', 0, '_clear_post_cache_on_update', '', true ); // < 1.5.0
 
343
 
344
+ $old_value = get_option( 'cache-enabler' ); // < 1.5.0
345
+ if ( $old_value !== false ) {
 
346
  delete_option( 'cache-enabler' );
347
+ add_option( 'cache_enabler', $old_value );
348
  }
349
 
350
+ $old_value = get_option( 'cache_enabler', array() );
351
+ $value = self::upgrade_settings( $old_value );
352
+ $value = wp_parse_args( self::get_default_settings( 'system' ), $value );
353
+ $value = wp_parse_args( $value, self::get_default_settings() );
354
+ $value = self::validate_settings( $value );
355
 
356
+ update_option( 'cache_enabler', $value );
 
357
 
358
+ if ( has_action( 'update_option', array( __CLASS__, 'on_update_option' ) ) === false ) {
359
+ self::on_update_backend( 'cache_enabler', $value );
360
+ }
361
 
362
+ return $value;
363
+ }
364
 
365
+ /**
366
+ * Update the disk requirements.
367
+ *
368
+ * This deletes the settings and advanced-cache.php files and then maybe unsets
369
+ * the WP_CACHE constant in the wp-config.php file. A new advanced-cache.php file
370
+ * is then created and the WP_CACHE constant is maybe set.
371
+ *
372
+ * @since 1.8.0
373
+ */
374
+ public static function update_disk() {
375
 
376
+ self::each_site( is_multisite(), 'Cache_Enabler_Disk::clean' );
 
 
 
377
 
378
+ Cache_Enabler_Disk::setup();
379
  }
380
 
 
381
  /**
382
+ * When the backend is about to be updated.
383
+ *
384
+ * This runs when the 'cache_enabler' option is about to be added or updated in
385
+ * the database.
386
  *
387
  * @since 1.5.0
388
+ * @change 1.8.0
389
  *
390
+ * @param string $option Name of the option (for legacy reasons).
391
+ * @param array $value The new option value.
392
  */
393
+ public static function on_update_backend( $option, $value ) {
394
 
395
+ Cache_Enabler_Disk::create_settings_file( $value );
396
 
397
+ self::unschedule_events();
398
  }
399
 
400
+ /**
401
+ * Before an option is added.
402
+ *
403
+ * This runs on the 'add_option' action.
404
+ *
405
+ * @since 1.8.0
406
+ *
407
+ * @param string $option Name of the option to add.
408
+ * @param mixed $value Value of the option.
409
+ */
410
+ public static function on_add_option( $option, $value ) {
411
+
412
+ if ( $option === 'cache_enabler' ) {
413
+ self::on_update_backend( $option, $value );
414
+ }
415
+ }
416
 
417
  /**
418
+ * Before an option value is updated.
419
  *
420
+ * This runs on the 'update_option' action.
 
421
  *
422
+ * @since 1.8.0
423
+ *
424
+ * @param string $option Name of the option to update.
425
+ * @param mixed $old_value The old option value.
426
+ * @param mixed $value The new option value.
427
  */
428
+ public static function on_update_option( $option, $old_value, $value ) {
429
 
430
+ $options = array(
431
+ // wp-admin/options-general.php?page=cache-enabler
432
+ 'cache_enabler',
433
 
434
+ // wp-admin/options-general.php
435
+ 'home',
436
 
437
+ // wp-admin/options-reading.php
438
+ 'page_on_front',
439
+ 'page_for_posts',
440
+ );
441
 
442
+ if ( in_array( $option, $options, true ) ) {
443
+ if ( $option === 'cache_enabler' ) {
444
+ self::on_update_backend( $option, $value );
445
+ } else {
446
+ self::clear_cache_on_option_save( $option, $old_value, $value );
447
+ }
448
+
449
+ if ( $option === 'home' ) {
450
+ Cache_Enabler_Disk::delete_settings_file();
451
+ }
452
+ }
453
  }
454
 
455
+ /**
456
+ * After the value of an option has been successfully updated.
457
+ *
458
+ * This runs on the 'updated_option' action.
459
+ *
460
+ * @since 1.8.0
461
+ *
462
+ * @param string $option Name of the updated option.
463
+ * @param mixed $old_value The old option value.
464
+ * @param mixed $value The new option value.
465
+ */
466
+ public static function on_updated_option( $option, $old_value, $value ) {
467
+
468
+ $options = array(
469
+ // wp-admin/options-general.php
470
+ 'blogname',
471
+ 'blogdescription',
472
+ 'WPLANG',
473
+ 'timezone_string',
474
+ 'gmt_offset',
475
+ 'date_format',
476
+ 'time_format',
477
+ 'start_of_week',
478
+
479
+ // wp-admin/options-reading.php
480
+ 'page_on_front',
481
+ 'page_for_posts',
482
+ 'posts_per_page',
483
+ 'blog_public',
484
+
485
+ // wp-admin/options-discussion.php
486
+ 'require_name_email',
487
+ 'comment_registration',
488
+ 'close_comments_for_old_posts',
489
+ 'show_comments_cookies_opt_in',
490
+ 'thread_comments',
491
+ 'thread_comments_depth',
492
+ 'page_comments',
493
+ 'comments_per_page',
494
+ 'default_comments_page',
495
+ 'comment_order',
496
+ 'show_avatars',
497
+ 'avatar_rating',
498
+ 'avatar_default',
499
+
500
+ // wp-admin/options-permalink.php
501
+ 'permalink_structure',
502
+ 'category_base',
503
+ 'tag_base',
504
+
505
+ // wp-admin/themes.php
506
+ 'template',
507
+ 'stylesheet',
508
+
509
+ // wp-admin/widgets.php
510
+ 'sidebars_widgets',
511
+ 'widget_*',
512
+
513
+ // wp-admin/customize.php
514
+ 'site_icon',
515
+ );
516
+
517
+ if ( strpos( $option, 'widget_' ) === 0 ) {
518
+ $option = 'widget_*';
519
+ }
520
+
521
+ if ( in_array( $option, $options, true ) ) {
522
+ self::clear_cache_on_option_save( $option, $old_value, $value );
523
+
524
+ if ( $option === 'permalink_structure' ) {
525
+ self::update_backend();
526
+ }
527
+ }
528
+ }
529
 
530
  /**
531
+ * When a site's uninitialization routine should be executed.
532
+ *
533
+ * This runs on the 'wp_uninitialize_site' action. This deletes the settings file
534
+ * and then clears the site cache for the deleted site. The advanced-cache.php
535
+ * file will also be deleted and the WP_CACHE constant maybe unset if it was the
536
+ * only site that had Cache Enabler activated.
537
  *
538
  * @since 1.0.0
539
+ * @change 1.8.0
540
+ *
541
+ * @param WP_Site $old_site Deleted site instance.
542
  */
543
+ public static function uninstall_later( $old_site ) {
544
+
545
+ Cache_Enabler_Disk::clean();
546
+
547
+ self::clear_page_cache_by_site( (int) $old_site->blog_id );
548
+ }
549
 
550
+ /**
551
+ * Uninstall backend requirements.
552
+ *
553
+ * @since 1.5.0
554
+ * @change 1.8.0
555
+ */
556
  private static function uninstall_backend() {
557
 
 
558
  delete_option( 'cache_enabler' );
559
+ delete_transient( 'cache_enabler_cache_size' );
560
  }
561
 
 
562
  /**
563
+ * Enter each site and call a callback with an array of parameters.
564
  *
565
+ * This assumes that the callback function exists on the site being entered. It
566
+ * will not perform the callback or restart the cache engine on sites that do not
567
+ * have Cache Enabler active, unless it is a must-use plugin or it is being
568
+ * activated or uninstalled.
569
  *
570
+ * @since 1.5.0
571
+ * @since 1.8.0 The `$restart_engine` parameter was added.
572
+ * @change 1.8.0
573
+ *
574
+ * @param bool $sites Whether to enter all sites or the current site.
575
+ * @param string $callback Callback function.
576
+ * @param array $callback_params (Optional) Callback function parameters. Default empty array.
577
+ * @param bool $restart_engine (Optional) Whether to restart the cache engine. Default false.
578
+ * @return array An array of callback returns with blog IDs as the keys.
579
  */
580
+ private static function each_site( $sites, $callback, $callback_params = array(), $restart_engine = false ) {
581
 
582
+ $blog_ids = $sites ? self::get_blog_ids() : array( get_current_blog_id() );
583
+ $last_blog_id = end( $blog_ids );
584
+ $skip_active_check = ! self::is_cache_enabler_active();
585
+ $callback_return = array();
586
 
587
+ foreach ( $blog_ids as $blog_id ) {
588
+ self::switch_to_blog( $blog_id, $restart_engine, $skip_active_check );
589
 
590
+ if ( $skip_active_check || self::is_cache_enabler_active() ) {
 
 
591
  $callback_return[ $blog_id ] = call_user_func_array( $callback, $callback_params );
 
592
  }
593
+
594
+ $_restart_engine = ( $restart_engine && $blog_id === $last_blog_id ) ? true : false;
595
+
596
+ self::restore_current_blog( $_restart_engine, $skip_active_check );
597
  }
598
 
599
  return $callback_return;
600
  }
601
 
602
+ /**
603
+ * Switch the current blog.
604
+ *
605
+ * This is a wrapper for switch_to_blog() that can restart the cache engine,
606
+ * allowing the correct site data to be picked up after the switch.
607
+ *
608
+ * @since 1.8.0
609
+ *
610
+ * @param int $blog_id The ID of the blog to switch to.
611
+ * @param bool $restart_engine (Optional) Whether to restart the cache engine after the switch. Default false.
612
+ * @param bool $force_restart (Optional) Whether to force restart the cache engine. Default false.
613
+ * @return bool True if the current blog was switched, false otherwise.
614
+ */
615
+ public static function switch_to_blog( $blog_id, $restart_engine = false, $force_restart = false ) {
616
+
617
+ if ( ! is_multisite() || $blog_id === get_current_blog_id() ) {
618
+ return false;
619
+ }
620
+
621
+ switch_to_blog( $blog_id );
622
+
623
+ if ( ( $force_restart || self::is_cache_enabler_active() ) && $restart_engine ) {
624
+ Cache_Enabler_Engine::start();
625
+ }
626
+
627
+ return true;
628
+ }
629
+
630
+ /**
631
+ * Restore the current blog after switching.
632
+ *
633
+ * This is a wrapper for restore_current_blog() that can restart the cache engine,
634
+ * allowing the correct site data to be picked up after the switch.
635
+ *
636
+ * @since 1.8.0
637
+ *
638
+ * @param bool $restart_engine (Optional) Whether to restart the cache engine after the switch. Default false.
639
+ * @param bool $force_restart (Optional) Whether to force restart the cache engine. Default false.
640
+ * @return bool True if the current blog was restored, false otherwise.
641
+ */
642
+ public static function restore_current_blog( $restart_engine = false, $force_restart = false ) {
643
+
644
+ if ( ! is_multisite() || ! ms_is_switched() ) {
645
+ return false;
646
+ }
647
+
648
+ restore_current_blog();
649
+
650
+ if ( ( $force_restart || self::is_cache_enabler_active() ) && $restart_engine ) {
651
+ Cache_Enabler_Engine::start( true );
652
+ }
653
+
654
+ return true;
655
+ }
656
+
657
+ /**
658
+ * Whether Cache Enabler is active.
659
+ *
660
+ * This checks if Cache Enabler is in the active plugins list. It will not be in
661
+ * that list when installed as a must-use plugin. This copies is_plugin_active().
662
+ * That function is not being used directly because of its availability.
663
+ *
664
+ * @since 1.8.0
665
+ *
666
+ * @return bool True if Cache Enabler is in the active plugins list, false if not.
667
+ */
668
+ private static function is_cache_enabler_active() {
669
+
670
+ if ( in_array( CACHE_ENABLER_BASE, (array) get_option( 'active_plugins', array() ), true ) ) {
671
+ return true;
672
+ }
673
+
674
+ if ( ! is_multisite() ) {
675
+ return false;
676
+ }
677
+
678
+ $plugins = get_site_option( 'active_sitewide_plugins' );
679
+ if ( isset( $plugins[ CACHE_ENABLER_BASE ] ) ) {
680
+ return true;
681
+ }
682
+
683
+ return false;
684
+ }
685
 
686
  /**
687
+ * After a plugin has been activated or deactivated.
688
+ *
689
+ * This runs on the 'activated_plugin' and 'deactivated_plugin' actions.
690
  *
691
  * @since 1.4.0
692
  * @change 1.6.0
693
  */
 
694
  public static function on_plugin_activation_deactivation() {
695
 
 
696
  if ( Cache_Enabler_Engine::$settings['clear_site_cache_on_changed_plugin'] ) {
697
  self::clear_site_cache();
698
  }
699
  }
700
 
 
701
  /**
702
+ * Get the plugin settings from the database for the current site.
703
  *
704
+ * This can update the disk and backend requirements and then clear the complete
705
+ * cache if the settings do not exist or are outdated. If that occurs, the
706
+ * settings after the update will be returned.
707
+ *
708
+ * @since 1.5.0
709
+ * @since 1.8.0 The `$update` parameter was added.
710
+ * @change 1.8.0
711
  *
712
+ * @param bool $update Whether to update the disk and backend requirements if the settings are
713
+ * outdated. Default true.
714
+ * @return array|bool Plugin settings from the database, false if settings do not exist and update
715
+ * was skipped or failed.
716
  */
717
+ public static function get_settings( $update = true ) {
718
 
 
 
 
719
  $settings = get_option( 'cache_enabler' );
720
 
 
721
  if ( $settings === false || ! isset( $settings['version'] ) || $settings['version'] !== CACHE_ENABLER_VERSION ) {
722
+ if ( $update ) {
723
+ self::update();
724
+ $settings = self::get_settings( false );
725
+ }
726
  }
727
 
728
  return $settings;
729
  }
730
 
 
731
  /**
732
+ * Get the blog ID for the current site or of a given site.
733
  *
734
+ * @since 1.8.0
 
735
  *
736
+ * @param WP_Site|int|string $site (Optional) Site instance or site blog ID. Default is the current site.
737
+ * @return int The blog ID or 0 if not found.
738
  */
739
+ private static function get_blog_id( $site = null ) {
740
 
741
+ if ( empty( $site ) ) {
742
+ return get_current_blog_id();
743
+ }
744
+
745
+ if ( $site instanceof WP_Site ) {
746
+ return (int) $site->blog_id;
747
+ }
748
 
749
+ if ( is_numeric( $site ) ) {
750
+ $blog_id = (int) $site;
751
+
752
+ if ( in_array( $blog_id, self::get_blog_ids(), true ) ) {
753
+ return $blog_id;
754
+ }
755
+ }
756
+
757
+ return 0;
758
+ }
759
+
760
+ /**
761
+ * Get the blog IDs.
762
+ *
763
+ * @since 1.5.0
764
+ * @change 1.8.0
765
+ *
766
+ * @global wpdb $wpdb WordPress database abstraction object.
767
+ *
768
+ * @return int[] Blog IDs.
769
+ */
770
+ private static function get_blog_ids() {
771
 
772
  if ( is_multisite() ) {
773
  global $wpdb;
 
774
  $blog_ids = array_map( 'absint', $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs" ) );
775
+ } else {
776
+ $blog_ids = array( 1 );
777
  }
778
 
779
  return $blog_ids;
780
  }
781
 
 
782
  /**
783
+ * Get the blog path for the current site.
784
+ *
785
+ * This gets the end part of the URL in case the installation is in a nested
786
+ * subdirectory. An empty string is being returned instead of '/' as WordPress
787
+ * does because it simplifies checking the blog path in
788
+ * self::get_root_blog_exclusions().
789
  *
790
  * @since 1.6.0
791
+ * @change 1.8.0
792
  *
793
+ * @return string Blog path from site address URL (with leading and trailing slashes), empty
794
+ * string if not found.
795
  */
 
796
  public static function get_blog_path() {
797
 
798
+ $site_url_path = (string) parse_url( home_url(), PHP_URL_PATH );
799
+ $site_url_path_pieces = explode( '/', trim( $site_url_path, '/' ) );
 
800
 
801
+ $blog_path = end( $site_url_path_pieces );
802
+ $blog_path = ( ! empty( $blog_path ) ) ? '/' . $blog_path . '/' : '';
803
 
804
  return $blog_path;
805
  }
806
 
 
807
  /**
808
+ * Get the blog path from a given URL.
809
  *
810
+ * @since 1.8.0
 
811
  *
812
+ * @return string Blog path from URL (with leading and trailing slashes), '/' if not found.
813
  */
814
+ public static function get_blog_path_from_url( $url ) {
815
 
816
+ $url_path = (string) parse_url( $url, PHP_URL_PATH );
817
+ $url_path_pieces = explode( '/', trim( $url_path, '/' ) );
818
+ $blog_path = '/';
819
+ $blog_paths = self::get_blog_paths();
820
+
821
+ foreach ( $url_path_pieces as $url_path_piece ) {
822
+ $url_path_piece = '/' . $url_path_piece . '/';
823
 
824
+ if ( in_array( $url_path_piece, $blog_paths, true ) ) {
825
+ $blog_path = $url_path_piece;
826
+ break;
827
+ }
828
+ }
829
+
830
+ return $blog_path;
831
+ }
832
+
833
+ /**
834
+ * Get the blog paths.
835
+ *
836
+ * @since 1.5.0
837
+ * @change 1.8.0
838
+ *
839
+ * @global wpdb $wpdb WordPress database abstraction object.
840
+ *
841
+ * @return string[] Blog paths.
842
+ */
843
+ public static function get_blog_paths() {
844
 
845
  if ( is_multisite() ) {
846
  global $wpdb;
847
  $blog_paths = $wpdb->get_col( "SELECT path FROM $wpdb->blogs" );
848
+ } else {
849
+ $blog_paths = array( '/' );
850
  }
851
 
852
  return $blog_paths;
853
  }
854
 
 
855
  /**
856
+ * Get the WP-Cron events.
857
  *
858
+ * @since 1.8.0
 
859
  *
860
+ * @return string[] An array of events with action hooks as the keys and recurrences as the values.
861
  */
862
+ private static function get_events() {
863
+
864
+ $events = array( 'cache_enabler_clear_expired_cache' => 'hourly' );
865
+
866
+ return $events;
867
+ }
868
 
869
+ /**
870
+ * Get the permalink structure (deprecated).
871
+ *
872
+ * @since 1.5.0
873
+ * @deprecated 1.8.0
874
+ */
875
  private static function get_permalink_structure() {
876
 
 
877
  $permalink_structure = get_option( 'permalink_structure' );
878
 
 
879
  if ( $permalink_structure && preg_match( '/\/$/', $permalink_structure ) ) {
880
  return 'has_trailing_slash';
881
  }
882
 
 
883
  if ( $permalink_structure && ! preg_match( '/\/$/', $permalink_structure ) ) {
884
  return 'no_trailing_slash';
885
  }
886
 
 
887
  if ( empty( $permalink_structure ) ) {
888
  return 'plain';
889
  }
890
  }
891
 
 
892
  /**
893
+ * Get the cache index for the current site.
894
  *
895
+ * @since 1.8.0
 
896
  *
897
+ * @return array[] Cache index from the disk.
898
  */
899
+ public static function get_cache_index() {
900
 
901
+ $args['subpages']['exclude'] = self::get_root_blog_exclusions();
902
+ $cache = Cache_Enabler_Disk::cache_iterator( home_url(), $args );
903
+ $cache_index = $cache['index'];
904
 
905
+ return $cache_index;
 
 
 
 
 
906
  }
907
 
 
908
  /**
909
+ * Get the cache size for the current site.
910
  *
911
+ * This sets the 'cache_enabler_cache_size' transient in the database when the
912
+ * cache size is retrieved from the disk.
913
  *
914
+ * @since 1.0.0
915
+ * @change 1.8.0
916
+ *
917
+ * @return int Cache size in bytes, either from the database or disk.
918
  */
919
+ public static function get_cache_size() {
920
 
921
+ $cache_size = get_transient( 'cache_enabler_cache_size' );
922
 
923
+ if ( $cache_size === false ) {
924
+ $args['subpages']['exclude'] = self::get_root_blog_exclusions();
925
+ $cache = Cache_Enabler_Disk::cache_iterator( home_url(), $args );
926
+ $cache_size = $cache['size'];
927
 
928
+ set_transient( 'cache_enabler_cache_size', $cache_size, DAY_IN_SECONDS );
929
+ }
930
 
931
+ return $cache_size;
932
+ }
933
 
934
  /**
935
+ * Get the name of the transient that is used in the cache clear notice.
936
  *
937
+ * @since 1.5.0
 
938
  *
939
+ * @return string Name of the transient.
940
  */
 
941
  private static function get_cache_cleared_transient_name() {
942
 
943
  $transient_name = 'cache_enabler_cache_cleared_' . get_current_user_id();
945
  return $transient_name;
946
  }
947
 
 
948
  /**
949
+ * Get the default plugin settings.
950
  *
951
+ * @since 1.5.0
952
+ * @change 1.8.0
953
  *
954
+ * @param string $settings_type (Optional) The default 'system' settings or all default settings if empty.
955
+ * @return array Default plugin settings.
956
  */
 
957
  private static function get_default_settings( $settings_type = null ) {
958
 
959
  $system_default_settings = array(
960
+ 'version' => (string) CACHE_ENABLER_VERSION,
961
+ 'use_trailing_slashes' => (int) ( substr( get_option( 'permalink_structure' ), -1, 1 ) === '/' ),
962
+ 'permalink_structure' => (string) self::get_permalink_structure(), // Deprecated in 1.8.0.
963
  );
964
 
965
  if ( $settings_type === 'system' ) {
971
  'cache_expiry_time' => 0,
972
  'clear_site_cache_on_saved_post' => 0,
973
  'clear_site_cache_on_saved_comment' => 0,
974
+ 'clear_site_cache_on_saved_term' => 0,
975
+ 'clear_site_cache_on_saved_user' => 0,
976
  'clear_site_cache_on_changed_plugin' => 0,
977
  'convert_image_urls_to_webp' => 0,
978
  'mobile_cache' => 0,
985
  'excluded_cookies' => '',
986
  );
987
 
 
988
  $default_settings = wp_parse_args( $user_default_settings, $system_default_settings );
989
 
990
  return $default_settings;
991
  }
992
 
 
993
  /**
994
+ * Get the subpages that do not belong to the root blog in a subdirectory network.
995
  *
996
+ * @since 1.8.0
 
997
  *
998
+ * @return string[] Blog paths to the other sites in a network if the current site is the root blog
999
+ * in a subdirectory network, empty otherwise.
1000
  */
1001
+ private static function get_root_blog_exclusions() {
1002
+
1003
+ if ( ! is_multisite() || is_subdomain_install() ) {
1004
+ return array();
1005
+ }
1006
+
1007
+ $current_blog_path = self::get_blog_path();
1008
+ $network_blog_paths = self::get_blog_paths();
1009
+
1010
+ if ( ! in_array( $current_blog_path, $network_blog_paths, true ) ) {
1011
+ return $network_blog_paths;
1012
+ }
1013
+
1014
+ return array();
1015
+ }
1016
 
1017
+ /**
1018
+ * Upgrade the plugin settings.
1019
+ *
1020
+ * This runs when self::update_backend() is called. An empty replacement value
1021
+ * means the setting will be removed.
1022
+ *
1023
+ * @since 1.8.0
1024
+ *
1025
+ * @param array $settings Plugin settings.
1026
+ * @return array The plugin settings after maybe being upgraded.
1027
+ */
1028
+ private static function upgrade_settings( $settings ) {
1029
 
 
1030
  if ( empty( $settings ) ) {
1031
  return $settings;
1032
  }
1033
 
1034
+ // < 1.5.0
1035
  if ( isset( $settings['expires'] ) && $settings['expires'] > 0 ) {
1036
  $settings['cache_expires'] = 1;
1037
  }
1038
 
1039
+ // < 1.5.0
1040
  if ( isset( $settings['minify_html'] ) && $settings['minify_html'] === 2 ) {
1041
  $settings['minify_html'] = 1;
1042
  $settings['minify_inline_css_js'] = 1;
1043
  }
1044
 
 
1045
  $settings_names = array(
1046
  // 1.4.0
1047
  'excl_regexp' => 'excluded_page_paths',
1049
  // 1.5.0
1050
  'expires' => 'cache_expiry_time',
1051
  'new_post' => 'clear_site_cache_on_saved_post',
1052
+ 'update_product_stock' => '',
1053
  'new_comment' => 'clear_site_cache_on_saved_comment',
1054
  'clear_on_upgrade' => 'clear_site_cache_on_changed_plugin',
1055
  'webp' => 'convert_image_urls_to_webp',
1057
  'excl_ids' => 'excluded_post_ids',
1058
  'excl_paths' => 'excluded_page_paths',
1059
  'excl_cookies' => 'excluded_cookies',
1060
+ 'incl_parameters' => '',
1061
 
1062
  // 1.6.0
1063
  'clear_complete_cache_on_saved_post' => 'clear_site_cache_on_saved_post',
1081
  return $settings;
1082
  }
1083
 
 
1084
  /**
1085
+ * Add the plugin action links in the plugins list table.
1086
  *
1087
+ * This runs on the 'plugin_action_links_cache-enabler/cache-enabler.php' action.
1088
+ *
1089
+ * @since 1.5.0
1090
  * @change 1.7.0
1091
  *
1092
+ * @param string[] $action_links Action links.
1093
+ * @return string[] The action links after maybe being updated.
1094
  */
 
1095
  public static function add_plugin_action_links( $action_links ) {
1096
 
 
1097
  if ( ! current_user_can( 'manage_options' ) ) {
1098
  return $action_links;
1099
  }
1100
 
 
1101
  array_unshift( $action_links, sprintf(
1102
  '<a href="%s">%s</a>',
1103
  admin_url( 'options-general.php?page=cache-enabler' ),
1107
  return $action_links;
1108
  }
1109
 
 
1110
  /**
1111
+ * Add the plugin metadata in the plugins list table.
1112
  *
1113
+ * This runs on the 'plugin_row_meta' action.
1114
+ *
1115
+ * @since 1.5.0
1116
  * @change 1.7.2
1117
  *
1118
+ * @param string[] $plugin_meta An array of the plugin's metadata, including the version, author, author URI,
1119
+ * and plugin URI.
1120
+ * @param string $plugin_file Path to the plugin file relative to the plugins directory.
1121
+ * @return string[] An array of the plugin's metadata after maybe being updated.
1122
  */
 
1123
  public static function add_plugin_row_meta( $plugin_meta, $plugin_file ) {
1124
 
 
1125
  if ( $plugin_file !== CACHE_ENABLER_BASE ) {
1126
  return $plugin_meta;
1127
  }
1128
 
 
1129
  $plugin_meta = wp_parse_args(
1130
  array(
1131
  '<a href="https://www.keycdn.com/support/wordpress-cache-enabler-plugin" target="_blank" rel="nofollow noopener">' . esc_html__( 'Documentation', 'cache-enabler' ) . '</a>',
1136
  return $plugin_meta;
1137
  }
1138
 
 
1139
  /**
1140
+ * Add the cache size to the 'At a Glance' dashboard widget.
1141
  *
1142
+ * This runs on the 'dashboard_glance_items' action.
1143
+ *
1144
+ * @since 1.5.0
1145
+ * @change 1.8.0
1146
  *
1147
+ * @param string[] $items Extra 'At a Glance' widget items.
1148
+ * @return string[] Extra 'At a Glance' widget items after maybe being updated.
1149
  */
1150
+ public static function add_dashboard_cache_size( $items ) {
1151
 
 
 
 
1152
  if ( ! current_user_can( 'manage_options' ) ) {
1153
  return $items;
1154
  }
1155
 
 
1156
  $cache_size = self::get_cache_size();
1157
 
1158
+ $items[] = sprintf(
1159
+ '<a href="%s">%s %s</a>',
1160
+ admin_url( 'options-general.php?page=cache-enabler' ),
1161
+ ( empty( $cache_size ) ) ? esc_html__( 'Empty', 'cache-enabler' ) : size_format( $cache_size ),
1162
+ esc_html__( 'Cache Size', 'cache-enabler' )
 
 
 
 
1163
  );
1164
 
1165
  return $items;
1166
  }
1167
 
 
1168
  /**
1169
+ * Add the admin bar items.
1170
  *
1171
+ * This runs on the 'admin_bar_menu' action. It adds the clear cache buttons to
1172
+ * the admin bar.
1173
  *
1174
+ * @since 1.6.0
1175
+ *
1176
+ * @param WP_Admin_Bar $wp_admin_bar Admin bar instance, passed by reference.
1177
  */
 
1178
  public static function add_admin_bar_items( $wp_admin_bar ) {
1179
 
 
1180
  if ( ! self::user_can_clear_cache() ) {
1181
  return;
1182
  }
1183
 
 
1184
  $title = ( is_multisite() && is_network_admin() ) ? esc_html__( 'Clear Network Cache', 'cache-enabler' ) : esc_html__( 'Clear Site Cache', 'cache-enabler' );
1185
 
 
1186
  $wp_admin_bar->add_menu(
1187
  array(
1188
  'id' => 'cache_enabler_clear_cache',
1196
  )
1197
  );
1198
 
 
1199
  if ( ! is_admin() ) {
1200
  $wp_admin_bar->add_menu(
1201
  array(
1212
  }
1213
  }
1214
 
 
1215
  /**
1216
+ * Add the admin resources.
1217
+ *
1218
+ * This runs on the 'admin_enqueue_scripts' action.
1219
  *
1220
  * @since 1.0.0
1221
  * @change 1.7.0
1222
  */
 
1223
  public static function add_admin_resources( $hook ) {
1224
 
 
1225
  if ( $hook === 'settings_page_cache-enabler' ) {
1226
  wp_enqueue_style( 'cache-enabler-settings', plugins_url( 'css/settings.min.css', CACHE_ENABLER_FILE ), array(), CACHE_ENABLER_VERSION );
1227
  }
1228
  }
1229
 
 
1230
  /**
1231
+ * Add the settings page.
1232
  *
1233
+ * This runs on the 'admin_menu' action. It updates the admin panel's menu
1234
+ * structure by adding the plugin settings page as a submenu page in the Settings
1235
+ * main menu.
1236
+ *
1237
+ * @since 1.0.0
1238
  */
 
1239
  public static function add_settings_page() {
1240
 
1241
  add_options_page(
1247
  );
1248
  }
1249
 
 
1250
  /**
1251
+ * Whether the current user can clear the cache.
1252
  *
1253
  * @since 1.6.0
1254
+ * @change 1.8.0
1255
  *
1256
+ * @return bool True if the current user can clear the cache, false otherwise.
1257
  */
 
1258
  private static function user_can_clear_cache() {
1259
 
1260
+ /**
1261
+ * Filters whether the current user can clear the cache.
1262
+ *
1263
+ * @since 1.6.0
1264
+ *
1265
+ * @param bool $can_clear_cache Whether the current user can clear the cache. Default is whether the current
1266
+ * user has the 'manage_options' capability.
1267
+ */
1268
+ $can_clear_cache = apply_filters( 'cache_enabler_user_can_clear_cache', current_user_can( 'manage_options' ) );
1269
+ $can_clear_cache = apply_filters_deprecated( 'user_can_clear_cache', array( $can_clear_cache ), '1.6.0', 'cache_enabler_user_can_clear_cache' );
1270
+
1271
+ return $can_clear_cache;
1272
  }
1273
 
 
1274
  /**
1275
+ * Process a clear cache request.
1276
  *
1277
+ * This runs on the 'init' action. It clears the cache when a clear cache button
1278
+ * is clicked in the admin bar.
1279
+ *
1280
+ * @since 1.5.0
1281
+ * @change 1.8.0
1282
  */
 
1283
  public static function process_clear_cache_request() {
1284
 
 
1285
  if ( empty( $_GET['_cache'] ) || empty( $_GET['_action'] ) || $_GET['_cache'] !== 'cache-enabler' || ( $_GET['_action'] !== 'clear' && $_GET['_action'] !== 'clearurl' ) ) {
1286
  return;
1287
  }
1288
 
 
1289
  if ( empty( $_GET['_wpnonce'] ) || ! wp_verify_nonce( $_GET['_wpnonce'], 'cache_enabler_clear_cache_nonce' ) ) {
1290
  return;
1291
  }
1292
 
 
1293
  if ( ! self::user_can_clear_cache() ) {
1294
  return;
1295
  }
1296
 
 
1297
  if ( $_GET['_action'] === 'clearurl' ) {
1298
+ self::clear_page_cache_by_url( Cache_Enabler_Engine::$request_headers['Host'] . $_SERVER['REQUEST_URI'] );
 
 
1299
  } elseif ( $_GET['_action'] === 'clear' ) {
1300
+ self::each_site( ( is_multisite() && is_network_admin() ), 'self::clear_site_cache', array(), true );
1301
  }
1302
 
1303
+ // Redirect to the same page.
1304
  wp_safe_redirect( remove_query_arg( array( '_cache', '_action', '_wpnonce' ) ) );
1305
 
 
1306
  if ( is_admin() ) {
1307
  set_transient( self::get_cache_cleared_transient_name(), 1 );
1308
  }
1309
 
 
1310
  exit;
1311
  }
1312
 
 
1313
  /**
1314
+ * Display an admin notice after the cache has been cleared.
1315
  *
1316
+ * This runs on the 'admin_notices' action.
1317
+ *
1318
+ * @since 1.5.0
1319
  * @change 1.7.0
1320
  */
 
1321
  public static function cache_cleared_notice() {
1322
 
 
1323
  if ( ! self::user_can_clear_cache() ) {
1324
  return;
1325
  }
1334
  }
1335
  }
1336
 
 
1337
  /**
1338
+ * When a post has been saved or before it is sent to the trash.
1339
+ *
1340
+ * This runs on the 'save_post' and 'wp_trash_post' actions. It will clear the cache
1341
+ * when any published post type has been created, updated, or about to be trashed.
1342
  *
1343
  * @since 1.5.0
1344
  * @change 1.7.0
1345
  *
1346
+ * @param int $post_id Post ID.
1347
  */
 
1348
  public static function on_save_trash_post( $post_id ) {
1349
 
1350
  $post_status = get_post_status( $post_id );
1351
 
 
1352
  if ( $post_status === 'publish' ) {
1353
  self::clear_cache_on_post_save( $post_id );
1354
  }
1355
  }
1356
 
 
1357
  /**
1358
+ * Before an existing post is updated in the database.
1359
  *
1360
+ * This runs on the 'pre_post_update' action. It will clear the cache when any
1361
+ * published post type is about to be updated but not trashed.
1362
  *
1363
+ * @since 1.7.0
1364
+ *
1365
+ * @param int $post_id Post ID.
1366
+ * @param array $post_data Array of unslashed post data.
1367
  */
 
1368
  public static function on_pre_post_update( $post_id, $post_data ) {
1369
 
1370
  $old_post_status = get_post_status( $post_id );
1371
  $new_post_status = $post_data['post_status'];
1372
 
 
1373
  if ( $old_post_status === 'publish' && $new_post_status !== 'trash' ) {
1374
  self::clear_cache_on_post_save( $post_id );
1375
  }
1376
  }
1377
 
1378
+ /**
1379
+ * After a comment is inserted into the database.
1380
+ *
1381
+ * This runs on the 'comment_post' action. It will clear the cache when a new
1382
+ * approved comment is posted.
1383
+ *
1384
+ * @since 1.6.0
1385
+ * @change 1.8.0
1386
+ *
1387
+ * @param int $comment_id Comment ID.
1388
+ * @param int|string $comment_approved 1 if the comment is approved, 0 if not, 'spam' if spam.
1389
+ */
1390
+ public static function on_comment_post( $comment_id, $comment_approved ) {
1391
+
1392
+ if ( $comment_approved === 1 ) {
1393
+ self::clear_cache_on_comment_save( $comment_id );
1394
+ }
1395
+ }
1396
 
1397
  /**
1398
+ * After a comment is updated in the database.
1399
  *
1400
+ * This runs on the 'edit_comment' action. It will clear the cache when an
1401
+ * approved comment is edited.
1402
+ *
1403
+ * @since 1.6.0
1404
+ * @change 1.8.0
1405
+ *
1406
+ * @param int $comment_id Comment ID.
1407
+ * @param array $comment_data Comment data.
1408
+ */
1409
+ public static function on_edit_comment( $comment_id, $comment_data ) {
1410
+
1411
+ $comment_approved = (int) $comment_data['comment_approved'];
1412
+
1413
+ if ( $comment_approved === 1 ) {
1414
+ self::clear_cache_on_comment_save( $comment_id );
1415
+ }
1416
+ }
1417
+
1418
+ /**
1419
+ * When the comment status is in transition.
1420
+ *
1421
+ * This runs on the 'transition_comment_status' action. It will clear the cache
1422
+ * when a comment's status has changed from or to 'approved'.
1423
  *
1424
+ * @since 1.6.0
1425
+ * @change 1.8.0
1426
+ *
1427
+ * @param int|string $new_status The new comment status.
1428
+ * @param int|string $old_status The old comment status.
1429
+ * @param WP_Comment $comment Comment instance.
1430
  */
1431
+ public static function on_transition_comment_status( $new_status, $old_status, $comment ) {
1432
 
1433
+ if ( $old_status === 'approved' || $new_status === 'approved' ) {
1434
+ self::clear_cache_on_comment_save( $comment );
1435
+ }
1436
+ }
1437
+
1438
+ /**
1439
+ * Before the given terms are edited.
1440
+ *
1441
+ * This runs on the 'edit_terms' action. It will clear the cache before a term is
1442
+ * updated in the database and its taxonomy is viewable.
1443
+ *
1444
+ * @since 1.8.0
1445
+ *
1446
+ * @param int $term_id Term ID
1447
+ * @param string $taxonomy Taxonomy name that `$term_id` is part of.
1448
+ */
1449
+ public static function on_edit_terms( $term_id, $taxonomy ) {
1450
+
1451
+ if ( is_taxonomy_viewable( $taxonomy ) ) {
1452
+ self::clear_cache_on_term_save( $term_id, $taxonomy );
1453
+ }
1454
+ }
1455
+
1456
+ /**
1457
+ * After a term has been saved or deleted and the term cache has been cleaned.
1458
+ *
1459
+ * This runs on the 'saved_term' and 'delete_term' actions. It will clear the
1460
+ * cache after a term has been updated or deleted from the database and its
1461
+ * taxonomy is viewable.
1462
+ *
1463
+ * @since 1.8.0
1464
+ *
1465
+ * @param int $term_id Term ID.
1466
+ * @param int $tt_id Term taxonomy ID.
1467
+ * @param string $taxonomy Taxonomy name that `$term_id` is part of.
1468
+ */
1469
+ public static function on_saved_delete_term( $term_id, $tt_id, $taxonomy ) {
1470
+
1471
+ if ( is_taxonomy_viewable( $taxonomy ) ) {
1472
+ self::clear_cache_on_term_save( $term_id, $taxonomy );
1473
+ }
1474
+ }
1475
+
1476
+ /**
1477
+ * After a user is registered or updated and before a user is deleted.
1478
+ *
1479
+ * This runs on the 'user_register', 'profile_update', and 'delete_user' actions.
1480
+ * It will clear the cache after a new user is registered or an existing user is
1481
+ * updated, and before a user is deleted from the database.
1482
+ *
1483
+ * @since 1.8.0
1484
+ *
1485
+ * @param int $user_id ID of the newly registered, updated, or about to be deleted user.
1486
+ */
1487
+ public static function on_register_update_delete_user( $user_id ) {
1488
+
1489
+ self::clear_cache_on_user_save( $user_id );
1490
+ }
1491
+
1492
+ /**
1493
+ * After a user is deleted from the database.
1494
+ *
1495
+ * This runs on the 'deleted_user' action. It will clear the cache after a user is
1496
+ * deleted from the database and the old posts of that user were reassigned.
1497
+ *
1498
+ * @since 1.8.0
1499
+ *
1500
+ * @param int $user_id ID of the deleted user.
1501
+ * @param int|null $reassign ID of the user reassigned to the old posts of `$user_id`.
1502
+ */
1503
+ public static function on_deleted_user( $user_id, $reassign ) {
1504
+
1505
+ if ( $reassign ) {
1506
+ self::clear_cache_on_user_save( $reassign );
1507
+ }
1508
+ }
1509
+
1510
+ /**
1511
+ * When the WooCommerce stock is updated.
1512
+ *
1513
+ * This runs on the 'woocommerce_product_set_stock',
1514
+ * 'woocommerce_variation_set_stock', 'woocommerce_product_set_stock_status', and
1515
+ * 'woocommerce_variation_set_stock_status' actions. It will clear the cache after
1516
+ * a product's stock is updated.
1517
+ *
1518
+ * @since 1.4.0
1519
+ * @change 1.6.1
1520
+ *
1521
+ * @param WC_Product|int $product Product instance or product ID.
1522
+ */
1523
+ public static function on_woocommerce_stock_update( $product ) {
1524
+
1525
+ if ( is_int( $product ) ) {
1526
+ $product_id = $product;
1527
+ } else {
1528
+ $product_id = $product->get_id();
1529
+ }
1530
+
1531
+ self::clear_cache_on_post_save( $product_id );
1532
+ }
1533
+
1534
+ /**
1535
+ * Clear the site cache of a single site or all sites in a multisite network.
1536
+ *
1537
+ * @since 1.5.0
1538
+ * @change 1.8.0
1539
+ */
1540
+ public static function clear_complete_cache() {
1541
+
1542
+ self::each_site( is_multisite(), 'self::clear_site_cache', array(), true );
1543
+ }
1544
+
1545
+ /**
1546
+ * Clear the complete cache (deprecated).
1547
+ *
1548
+ * @since 1.0.0
1549
+ * @deprecated 1.5.0
1550
+ */
1551
+ public static function clear_total_cache() {
1552
+
1553
+ self::clear_complete_cache();
1554
+ }
1555
+
1556
+ /**
1557
+ * Clear the site cache for the current site or of a given site.
1558
+ *
1559
+ * @since 1.6.0
1560
+ * @since 1.8.0 The `$site` parameter was added.
1561
+ * @change 1.8.0
1562
+ *
1563
+ * @param WP_Site|int|string $site (Optional) Site instance or site blog ID. Default is the current site.
1564
+ */
1565
+ public static function clear_site_cache( $site = null ) {
1566
+
1567
+ self::clear_page_cache_by_site( $site );
1568
+ }
1569
+
1570
+ /**
1571
+ * Clear the expired cache for the current site or of a given site.
1572
+ *
1573
+ * @since 1.8.0
1574
+ *
1575
+ * @param WP_Site|int|string $site (Optional) Site instance or site blog ID. Default is the current site.
1576
+ */
1577
+ public static function clear_expired_cache( $site = null ) {
1578
+
1579
+ $args['expired'] = 1;
1580
+ $args['hooks']['include'] = 'cache_enabler_page_cache_cleared';
1581
+
1582
+ self::clear_page_cache_by_site( $site, $args );
1583
+ }
1584
+
1585
+ /**
1586
+ * Clear the post cache for the current post or of a given post.
1587
+ *
1588
+ * @since 1.8.0
1589
+ *
1590
+ * @param WP_Post|int|string $post (Optional) Post instance or post ID. Default is the current post if set.
1591
+ */
1592
+ public static function clear_post_cache( $post = null ) {
1593
+
1594
+ $post = get_post( $post );
1595
+
1596
+ if ( $post instanceof WP_Post ) {
1597
+ self::clear_page_cache_by_post( $post, 'pagination' );
1598
+ self::clear_post_type_archive_cache( $post );
1599
+ self::clear_post_terms_archives_cache( $post );
1600
+
1601
+ if ( $post->post_type === 'post' ) {
1602
+ self::clear_post_author_archive_cache( $post );
1603
+ self::clear_post_date_archives_cache( $post );
1604
+ }
1605
+ }
1606
+ }
1607
+
1608
+ /**
1609
+ * Clear the comment cache for the current comment or of a given comment.
1610
+ *
1611
+ * @since 1.8.0
1612
+ *
1613
+ * @param WP_Comment|int|string $comment (Optional) Comment instance or comment ID. Default is the current comment if set.
1614
+ */
1615
+ public static function clear_comment_cache( $comment = null ) {
1616
+
1617
+ $comment = get_comment( $comment );
1618
+
1619
+ if ( $comment instanceof WP_Comment ) {
1620
+ self::clear_page_cache_by_comment( $comment, 'pagination' );
1621
+ }
1622
+ }
1623
+
1624
+ /**
1625
+ * Clear the term cache of a given term.
1626
+ *
1627
+ * @since 1.8.0
1628
+ *
1629
+ * @param WP_Term|int $term Term instance or term ID.
1630
+ * @param string $taxonomy (Optional) Taxonomy name that `$term` is part of. Default empty string.
1631
+ */
1632
+ public static function clear_term_cache( $term, $taxonomy = '' ) {
1633
+
1634
+ $term = get_term( $term, $taxonomy );
1635
+
1636
+ if ( $term instanceof WP_Term ) {
1637
+ self::clear_page_cache_by_term( $term, '', 'pagination' );
1638
+ self::clear_term_archive_cache( $term );
1639
+
1640
+ if ( is_taxonomy_hierarchical( $term->taxonomy ) ) {
1641
+ self::clear_term_children_archives_cache( $term );
1642
+ self::clear_term_parents_archives_cache( $term );
1643
+ }
1644
+ }
1645
+ }
1646
+
1647
+ /**
1648
+ * Clear the user cache for the current user or of a given user.
1649
+ *
1650
+ * @since 1.8.0
1651
+ *
1652
+ * @param WP_User|int|string $user (Optional) User instance or user ID. Default is the current user if logged in.
1653
+ */
1654
+ public static function clear_user_cache( $user = null ) {
1655
+
1656
+ if ( empty( $user ) ) {
1657
+ $user = wp_get_current_user();
1658
+ } elseif ( is_numeric( $user ) ) {
1659
+ $user = get_userdata( $user );
1660
+ }
1661
+
1662
+ if ( $user instanceof WP_User ) {
1663
+ self::clear_page_cache_by_user( $user, 'pagination' );
1664
+ self::clear_author_archive_cache( $user );
1665
+ }
1666
+ }
1667
+
1668
+ /**
1669
+ * Clear the cache for pages associated with a new or updated post (deprecated).
1670
+ *
1671
+ * @since 1.5.0
1672
+ * @deprecated 1.8.0
1673
+ */
1674
+ public static function clear_associated_cache( $post ) {
1675
+
1676
+ self::clear_post_type_archive_cache( $post );
1677
+ self::clear_post_terms_archives_cache( $post );
1678
+
1679
+ if ( $post->post_type === 'post' ) {
1680
+ self::clear_post_author_archive_cache( $post );
1681
+ self::clear_post_date_archives_cache( $post );
1682
+ }
1683
+ }
1684
+
1685
+ /**
1686
+ * Clear the post type archives page cache (deprecated).
1687
+ *
1688
+ * @since 1.5.0
1689
+ * @deprecated 1.8.0
1690
+ */
1691
+ public static function clear_post_type_archives_cache( $post_type ) {
1692
+
1693
+ $post_type_archives_url = get_post_type_archive_link( $post_type );
1694
+
1695
+ if ( ! empty( $post_type_archives_url ) ) {
1696
+ self::clear_page_cache_by_url( $post_type_archives_url, 'pagination' );
1697
+ }
1698
+ }
1699
+
1700
+ /**
1701
+ * Clear the post type archive cache for the current post or of a given post.
1702
+ *
1703
+ * @since 1.8.0
1704
+ *
1705
+ * @param WP_Post|int|string $post (Optional) Post instance or post ID. Default is the current post if set.
1706
+ */
1707
+ public static function clear_post_type_archive_cache( $post = null ) {
1708
+
1709
+ $post = get_post( $post );
1710
+
1711
+ if ( $post instanceof WP_Post ) {
1712
+ $post_type_archive_url = get_post_type_archive_link( $post->post_type );
1713
 
1714
+ if ( $post_type_archive_url !== false && strpos( $post_type_archive_url, '?' ) === false ) {
1715
+ self::clear_page_cache_by_url( $post_type_archive_url, 'pagination' );
 
 
 
 
 
 
1716
  }
1717
  }
1718
  }
1719
 
 
1720
  /**
1721
+ * Clear the post terms archives cache for the current post or of a given post.
1722
  *
1723
+ * @since 1.8.0
 
1724
  *
1725
+ * @param WP_Post|int|string $post (Optional) Post instance or post ID. Default is the current post if set.
 
1726
  */
1727
+ public static function clear_post_terms_archives_cache( $post = null ) {
1728
 
1729
+ $post = get_post( $post );
1730
 
1731
+ if ( $post instanceof WP_Post ) {
1732
+ $terms = wp_get_post_terms( $post->ID, get_taxonomies() );
1733
 
1734
+ if ( is_array( $terms ) ) {
1735
+ foreach ( $terms as $term ) {
1736
+ self::clear_term_archive_cache( $term );
1737
+
1738
+ if ( is_taxonomy_hierarchical( $term->taxonomy ) ) {
1739
+ self::clear_term_parents_archives_cache( $term ); // Post can be in the term's parents' archives.
1740
+ }
1741
+ }
1742
  }
1743
  }
1744
  }
1745
 
 
1746
  /**
1747
+ * Clear the post author archive cache for the current post or of a given post.
1748
  *
1749
+ * @since 1.8.0
 
1750
  *
1751
+ * @param WP_Post|int|string $post (Optional) Post instance or post ID. Default is the current post if set.
 
 
1752
  */
1753
+ public static function clear_post_author_archive_cache( $post = null ) {
1754
 
1755
+ $post = get_post( $post );
1756
 
1757
+ if ( $post instanceof WP_Post ) {
1758
+ self::clear_author_archive_cache( (int) $post->post_author );
 
 
 
 
 
 
 
1759
  }
1760
  }
1761
 
 
1762
  /**
1763
+ * Clear the post date archives cache for the current post or of a given post.
1764
  *
1765
+ * @since 1.8.0
 
1766
  *
1767
+ * @param WP_Post|int|string $post (Optional) Post instance or post ID. Default is the current post if set.
1768
  */
1769
+ public static function clear_post_date_archives_cache( $post = null ) {
1770
 
1771
+ $post = get_post( $post );
1772
 
1773
+ if ( $post instanceof WP_Post ) {
1774
+ $date_archive_day = get_the_date( 'd', $post );
1775
+ $date_archive_month = get_the_date( 'm', $post );
1776
+ $date_archive_year = get_the_date( 'Y', $post );
1777
+ $date_archive_urls[] = get_day_link( $date_archive_year, $date_archive_month, $date_archive_day );
1778
+ $date_archive_urls[] = get_month_link( $date_archive_year, $date_archive_month );
1779
+ $date_archive_urls[] = get_year_link( $date_archive_year );
1780
 
1781
+ foreach ( $date_archive_urls as $date_archive_url ) {
1782
+ if ( strpos( $date_archive_url, '?' ) === false ) {
1783
+ self::clear_page_cache_by_url( $date_archive_url, 'pagination' );
1784
+ }
1785
+ }
1786
+ }
1787
  }
1788
 
 
1789
  /**
1790
+ * Clear the taxonomies archives cache by post ID (deprecated).
1791
  *
1792
+ * @since 1.5.0
1793
+ * @deprecated 1.8.0
1794
  */
1795
+ public static function clear_taxonomies_archives_cache_by_post_id( $post_id ) {
1796
 
1797
+ self::clear_post_terms_archives_cache( $post_id );
1798
+ }
1799
 
1800
+ /**
1801
+ * Clear the author archives page cache by user ID (deprecated).
1802
+ *
1803
+ * @since 1.5.0
1804
+ * @deprecated 1.8.0
1805
+ */
1806
+ public static function clear_author_archives_cache_by_user_id( $user_id ) {
1807
 
1808
+ self::clear_author_archive_cache( $user_id );
 
1809
  }
1810
 
 
1811
  /**
1812
+ * Clear the date archives cache by post ID (deprecated).
1813
  *
1814
+ * @since 1.5.0
1815
+ * @deprecated 1.8.0
1816
  */
1817
+ public static function clear_date_archives_cache_by_post_id( $post_id ) {
1818
 
1819
+ self::clear_post_date_archives_cache( $post_id );
 
 
1820
  }
1821
 
 
1822
  /**
1823
+ * Clear the term archive cache of a given term.
1824
  *
1825
+ * @since 1.8.0
1826
+ *
1827
+ * @param WP_Term|int $term Term instance or term ID.
1828
+ * @param string $taxonomy (Optional) Taxonomy name that `$term` is part of. Default empty string.
1829
  */
1830
+ public static function clear_term_archive_cache( $term, $taxonomy = '' ) {
1831
 
1832
+ $term = get_term( $term, $taxonomy );
1833
 
1834
+ if ( $term instanceof WP_Term ) {
1835
+ if ( ! is_taxonomy_viewable( $term->taxonomy ) ) {
1836
+ return; // Term archive cache does not exist.
1837
+ }
1838
+
1839
+ $term_archive_url = get_term_link( $term );
1840
 
1841
+ if ( ! is_wp_error( $term_archive_url ) && strpos( $term_archive_url, '?' ) === false ) {
1842
+ self::clear_page_cache_by_url( $term_archive_url, 'pagination' );
1843
+ }
1844
+ }
1845
+ }
1846
 
1847
  /**
1848
+ * Clear the term children archives cache of a given term.
1849
  *
1850
+ * @since 1.8.0
 
1851
  *
1852
+ * @param WP_Term|int $term Term instance or term ID.
1853
+ * @param string $taxonomy (Optional) Taxonomy name that `$term` is part of. Default empty string.
1854
  */
1855
+ public static function clear_term_children_archives_cache( $term, $taxonomy = '' ) {
1856
 
1857
+ $term = get_term( $term, $taxonomy );
 
 
 
1858
 
1859
+ if ( $term instanceof WP_Term ) {
1860
+ $child_ids = get_term_children( $term->term_id, $term->taxonomy );
1861
 
1862
+ if ( is_array( $child_ids ) ) {
1863
+ foreach ( $child_ids as $child_id ) {
1864
+ self::clear_term_archive_cache( $child_id, $term->taxonomy );
1865
+ }
1866
+ }
1867
  }
1868
  }
1869
 
 
1870
  /**
1871
+ * Clear the term parents archives cache of a given term.
1872
  *
1873
+ * @since 1.8.0
 
1874
  *
1875
+ * @param WP_Term|int $term Term instance or term ID.
1876
+ * @param string $taxonomy (Optional) Taxonomy name that `$term` is part of. Default empty string.
1877
  */
1878
+ public static function clear_term_parents_archives_cache( $term, $taxonomy = '' ) {
1879
 
1880
+ $term = get_term( $term, $taxonomy );
1881
 
1882
+ if ( $term instanceof WP_Term ) {
1883
+ $parent_ids = get_ancestors( $term->term_id, $term->taxonomy, 'taxonomy' );
1884
 
1885
+ foreach ( $parent_ids as $parent_id ) {
1886
+ self::clear_term_archive_cache( $parent_id, $term->taxonomy );
1887
+ }
1888
  }
1889
  }
1890
 
 
1891
  /**
1892
+ * Clear the author archive cache for the current user or of a given user.
1893
  *
1894
+ * @since 1.8.0
 
1895
  *
1896
+ * @param WP_User|int|string $author (Optional) User instance or user ID of the author. Default is the current user
1897
+ * if logged in.
1898
  */
1899
+ public static function clear_author_archive_cache( $author = null ) {
1900
 
1901
+ if ( empty( $author ) ) {
1902
+ $author = wp_get_current_user();
1903
+ } elseif ( is_numeric( $author ) ) {
1904
+ $author = get_userdata( $author );
1905
+ }
1906
 
1907
+ if ( $author instanceof WP_User ) {
1908
+ if ( empty( $author->user_nicename ) ) {
1909
+ return; // Author archive cache does not exist.
1910
+ }
1911
 
1912
+ $author_archive_url = get_author_posts_url( $author->ID, $author->user_nicename );
 
 
1913
 
1914
+ if ( strpos( $author_archive_url, '?' ) === false ) {
1915
+ self::clear_page_cache_by_url( $author_archive_url, 'pagination' );
 
 
 
1916
  }
1917
  }
1918
  }
1919
 
 
1920
  /**
1921
+ * Clear the page cache associated with a given site.
1922
  *
1923
+ * @since 1.8.0
 
1924
  *
1925
+ * @param WP_Site|int|string $site Site instance or site blog ID.
1926
+ * @param array|string $args (Optional) See Cache_Enabler_Disk::cache_iterator() for the available
1927
+ * arguments. Default empty array.
1928
  */
1929
+ public static function clear_page_cache_by_site( $site, $args = array() ) {
1930
 
1931
+ $blog_id = self::get_blog_id( $site );
1932
+
1933
+ if ( $blog_id === 0 ) {
1934
+ return; // Page cache does not exist.
1935
+ }
1936
+
1937
+ if ( is_array( $args ) ) {
1938
+ $args['subpages']['exclude'] = self::get_root_blog_exclusions();
1939
 
1940
+ if ( ! isset( $args['hooks']['include'] ) ) {
1941
+ $args['hooks']['include'] = 'cache_enabler_complete_cache_cleared,cache_enabler_site_cache_cleared';
1942
+ }
1943
+ }
1944
 
1945
+ self::clear_page_cache_by_url( get_home_url( $blog_id ), $args );
 
1946
  }
1947
 
1948
+ /**
1949
+ * Clear the page cache by post ID (deprecated).
1950
+ *
1951
+ * @since 1.0.0
1952
+ * @deprecated 1.8.0
1953
+ */
1954
+ public static function clear_page_cache_by_post_id( $post_id, $args = array() ) {
1955
+
1956
+ self::clear_page_cache_by_post( $post_id, $args );
1957
+ }
1958
 
1959
  /**
1960
+ * Clear the page cache of a given post.
1961
  *
1962
+ * @since 1.8.0
 
1963
  *
1964
+ * @param WP_Post|int|string $post Post instance or post ID.
1965
+ * @param array|string $args (Optional) See Cache_Enabler_Disk::cache_iterator() for the available
1966
+ * arguments. Default empty array.
1967
  */
1968
+ public static function clear_page_cache_by_post( $post, $args = array() ) {
1969
 
1970
+ $post = get_post( $post );
1971
 
1972
+ if ( $post instanceof WP_Post ) {
1973
+ if ( $post->post_status !== 'publish' ) {
1974
+ return; // Page cache does not exist.
1975
+ }
1976
 
1977
+ $post_url = get_permalink( $post );
 
 
 
1978
 
1979
+ if ( $post_url !== false && strpos( $post_url, '?' ) === false ) {
1980
+ self::clear_page_cache_by_url( $post_url, $args );
1981
+ }
1982
+ }
1983
  }
1984
 
 
1985
  /**
1986
+ * Clear the page cache of the post associated with a given comment.
1987
  *
1988
+ * @since 1.8.0
 
1989
  *
1990
+ * @param WP_Comment|int|string $comment Comment instance or comment ID.
1991
+ * @param array|string $args (Optional) See Cache_Enabler_Disk::cache_iterator() for the available
1992
+ * arguments. Default empty array.
1993
  */
1994
+ public static function clear_page_cache_by_comment( $comment, $args = array() ) {
1995
 
1996
+ $comment = get_comment( $comment );
 
 
 
1997
 
1998
+ if ( $comment instanceof WP_Comment ) {
1999
+ if ( $comment->comment_approved !== '1' ) {
2000
+ return; // Page cache does not exist.
2001
+ }
2002
 
2003
+ self::clear_page_cache_by_post( (int) $comment->comment_post_ID, $args );
 
 
2004
  }
2005
  }
2006
 
 
2007
  /**
2008
+ * Clear the page cache of the posts associated with a given term.
2009
  *
2010
+ * This clears the page cache of the posts that have the term set.
 
2011
  *
2012
+ * @since 1.8.0
2013
+ *
2014
+ * @param WP_Term|int $term Term instance or term ID.
2015
+ * @param string $taxonomy (Optional) Taxonomy name that `$term` is part of. Default empty string.
2016
+ * @param array|string $args (Optional) See Cache_Enabler_Disk::cache_iterator() for the available
2017
+ * arguments. Default empty array.
2018
  */
2019
+ public static function clear_page_cache_by_term( $term, $taxonomy = '', $args = array() ) {
2020
 
2021
+ $term = get_term( $term, $taxonomy );
2022
 
2023
+ if ( ! $term instanceof WP_Term ) {
2024
+ return;
2025
+ }
2026
+
2027
+ $post_query_args = array(
2028
+ 'post_type' => 'any',
2029
+ 'post_status' => 'publish',
2030
+ 'numberposts' => -1,
2031
+ 'order' => 'none',
2032
+ 'cache_results' => false,
2033
+ 'no_found_rows' => true,
2034
+ 'tax_query' => array(
2035
+ array(
2036
+ 'taxonomy' => $term->taxonomy,
2037
+ 'terms' => $term->term_id,
2038
+ ),
2039
+ ),
2040
+ );
2041
 
2042
+ $posts = get_posts( $post_query_args );
2043
+
2044
+ foreach ( $posts as $post ) {
2045
+ self::clear_page_cache_by_post( $post, $args );
2046
+ }
2047
+ }
2048
 
2049
  /**
2050
+ * Clear the page cache of the posts associated with a given user.
2051
  *
2052
+ * This clears the page cache of the posts that the user is the author of or has
2053
+ * commented on.
2054
+ *
2055
+ * @since 1.8.0
2056
  *
2057
+ * @param WP_User|int|string $user User instance or user ID.
2058
+ * @param array|string $args (Optional) See Cache_Enabler_Disk::cache_iterator() for the available
2059
+ * arguments. Default empty array.
2060
  */
2061
+ public static function clear_page_cache_by_user( $user, $args = array() ) {
2062
 
2063
+ if ( is_numeric( $user ) ) {
2064
+ $user = get_userdata( $user );
2065
+ }
 
2066
 
2067
+ if ( ! $user instanceof WP_User ) {
 
2068
  return;
2069
  }
2070
 
2071
+ $post_query_args = array(
2072
+ 'author' => $user->ID,
2073
+ 'post_type' => 'any',
2074
+ 'post_status' => 'publish',
2075
+ 'numberposts' => -1,
2076
+ 'fields' => 'ids',
2077
+ 'order' => 'none',
2078
+ 'cache_results' => false,
2079
+ 'no_found_rows' => true,
2080
+ );
2081
 
2082
+ $post_ids = get_posts( $post_query_args );
 
2083
 
2084
+ $comment_query_args = array(
2085
+ 'status' => 'approve',
2086
+ 'user_id' => $user->ID,
2087
+ );
2088
 
2089
+ $comments = get_comments( $comment_query_args );
 
2090
 
2091
+ foreach ( $comments as $comment ) {
2092
+ $comment_post_id = (int) $comment->comment_post_ID;
 
 
2093
 
2094
+ if ( ! in_array( $comment_post_id, $post_ids, true ) ) {
2095
+ $post_ids[] = $comment_post_id;
2096
+ }
2097
+ }
2098
 
2099
+ foreach ( $post_ids as $post_id ) {
2100
+ self::clear_page_cache_by_post( $post_id, $args );
 
2101
  }
2102
+ }
2103
 
2104
+ /**
2105
+ * Clear the page cache of a given URL.
2106
+ *
2107
+ * @since 1.0.0
2108
+ * @since 1.8.0 The `$args` parameter was added.
2109
+ * @change 1.8.0
2110
+ *
2111
+ * @param string $url URL to a cached page (with or without scheme, wildcard path, and query string).
2112
+ * @param array|string $args (Optional) See Cache_Enabler_Disk::cache_iterator() for the available
2113
+ * arguments. Default empty array.
2114
+ */
2115
+ public static function clear_page_cache_by_url( $url, $args = array() ) {
2116
+
2117
+ if ( is_array( $args ) ) {
2118
+ $args['clear'] = 1;
2119
+
2120
+ if ( ! isset( $args['hooks']['include'] ) ) {
2121
+ $args['hooks']['include'] = 'cache_enabler_page_cache_cleared';
2122
+ }
2123
  }
2124
+
2125
+ Cache_Enabler_Disk::cache_iterator( $url, $args );
2126
  }
2127
 
2128
+ /**
2129
+ * Clear the site cache by blog ID (deprecated).
2130
+ *
2131
+ * @since 1.4.0
2132
+ * @deprecated 1.8.0
2133
+ */
2134
+ public static function clear_site_cache_by_blog_id( $blog_id, $deprecated = null ) {
2135
+
2136
+ self::clear_page_cache_by_site( $blog_id );
2137
+ }
2138
 
2139
  /**
2140
+ * Clear the cache when any post type has been published, updated, or trashed.
2141
  *
2142
  * @since 1.5.0
2143
+ * @change 1.8.0
2144
  *
2145
+ * @param WP_Post|int|string $post Post instance or post ID.
2146
  */
 
2147
  public static function clear_cache_on_post_save( $post ) {
2148
 
2149
+ if ( Cache_Enabler_Engine::$settings['clear_site_cache_on_saved_post'] ) {
2150
+ self::clear_site_cache();
2151
+ } else {
2152
+ self::clear_post_cache( $post );
2153
+ }
2154
+ }
2155
+
2156
+ /**
2157
+ * Clear the cache when a comment been posted, updated, spammed, or trashed.
2158
+ *
2159
+ * @since 1.8.0
2160
+ *
2161
+ * @param WP_Comment|int|string $comment Comment instance or comment ID.
2162
+ */
2163
+ public static function clear_cache_on_comment_save( $comment ) {
2164
 
2165
+ if ( Cache_Enabler_Engine::$settings['clear_site_cache_on_saved_comment'] ) {
2166
+ self::clear_site_cache();
2167
+ } else {
2168
+ self::clear_comment_cache( $comment );
 
2169
  }
2170
+ }
2171
 
2172
+ /**
2173
+ * Clear the cache when any term has been added, updated, or deleted.
2174
+ *
2175
+ * @since 1.8.0
2176
+ *
2177
+ * @param WP_Term|int $term Term instance or term ID.
2178
+ * @param string $taxonomy (Optional) Taxonomy name that `$term` is part of. Default empty string.
2179
+ */
2180
+ public static function clear_cache_on_term_save( $term, $taxonomy = '' ) {
2181
+
2182
+ if ( Cache_Enabler_Engine::$settings['clear_site_cache_on_saved_term'] ) {
2183
  self::clear_site_cache();
 
2184
  } else {
2185
+ self::clear_term_cache( $term, $taxonomy );
 
2186
  }
2187
  }
2188
 
2189
+ /**
2190
+ * Clear the cache when any user has been added, updated, or deleted.
2191
+ *
2192
+ * @since 1.8.0
2193
+ *
2194
+ * @param WP_User|int|string $user User instance or user ID.
2195
+ */
2196
+ public static function clear_cache_on_user_save( $user ) {
2197
+
2198
+ if ( Cache_Enabler_Engine::$settings['clear_site_cache_on_saved_user'] ) {
2199
+ self::clear_site_cache();
2200
+ } else {
2201
+ self::clear_user_cache( $user );
2202
+ }
2203
+ }
2204
 
2205
  /**
2206
+ * Clear the cache when an option is about to be updated or already has been.
2207
  *
2208
+ * @since 1.8.0
2209
+ *
2210
+ * @param string $option Name of the option.
2211
+ * @param mixed $old_value The old option value.
2212
+ * @param mixed $value The new option value.
2213
  */
2214
+ public static function clear_cache_on_option_save( $option, $old_value, $value ) {
2215
+
2216
+ switch ( $option ) {
2217
+ case 'page_for_posts':
2218
+ case 'page_on_front':
2219
+ array_map( 'self::clear_page_cache_by_post', array( $old_value, $value ) );
2220
+ break;
2221
+ default:
2222
+ self::clear_site_cache();
2223
+ }
2224
+ }
2225
 
2226
+ /**
2227
+ * Check plugin's requirements.
2228
+ *
2229
+ * @since 1.1.0
2230
+ * @change 1.8.0
2231
+ *
2232
+ * @global string $wp_version WordPress version.
2233
+ */
2234
  public static function requirements_check() {
2235
 
 
2236
  if ( ! current_user_can( 'manage_options' ) ) {
2237
  return;
2238
  }
2239
 
2240
+ // Check the PHP version.
2241
  if ( version_compare( PHP_VERSION, CACHE_ENABLER_MIN_PHP, '<' ) ) {
2242
  printf(
2243
  '<div class="notice notice-error"><p>%s</p></div>',
2250
  );
2251
  }
2252
 
2253
+ // Check the WordPress version.
2254
  if ( version_compare( $GLOBALS['wp_version'], CACHE_ENABLER_MIN_WP . 'alpha', '<' ) ) {
2255
  printf(
2256
  '<div class="notice notice-error"><p>%s</p></div>',
2263
  );
2264
  }
2265
 
2266
+ // Check the advanced-cache.php drop-in file.
2267
+ if ( ! file_exists( WP_CONTENT_DIR . '/advanced-cache.php' ) && Cache_Enabler_Disk::create_advanced_cache_file() === false ) {
2268
  printf(
2269
  '<div class="notice notice-warning"><p>%s</p></div>',
2270
  sprintf(
2271
  // translators: 1. Cache Enabler 2. advanced-cache.php 3. wp-content/plugins/cache-enabler 4. wp-content
2272
+ esc_html__( '%1$s was unable to create the required %2$s drop-in file. You can manually create it by locating the sample file in the %3$s directory, editing it as needed, and then saving it in the %4$s directory.', 'cache-enabler' ),
2273
  '<strong>Cache Enabler</strong>',
2274
  '<code>advanced-cache.php</code>',
2275
+ '<code>' . str_replace( ABSPATH, '', CACHE_ENABLER_DIR ) . '</code>',
2276
+ '<code>' . basename( WP_CONTENT_DIR ) . '</code>'
2277
+ )
2278
+ );
2279
+ }
2280
+
2281
+ // Check the WordPress installation directory index file.
2282
+ if ( ! file_exists( CACHE_ENABLER_INDEX_FILE ) ) {
2283
+ printf(
2284
+ '<div class="notice notice-warning"><p>%s</p></div>',
2285
+ sprintf(
2286
+ // translators: 1. Cache Enabler 2. /path/to/index.php 3. CACHE_ENABLER_INDEX_FILE 4. wp-config.php
2287
+ esc_html__( '%1$s was unable to find the WordPress installation directory index file at %2$s. Please define the %3$s constant in your %4$s file as the full path to the location of this file.', 'cache-enabler' ),
2288
+ '<strong>Cache Enabler</strong>',
2289
+ '<code>' . CACHE_ENABLER_INDEX_FILE . '</code>',
2290
+ '<code>CACHE_ENABLER_INDEX_FILE</code>',
2291
+ '<code>wp-config.php</code>',
2292
  )
2293
  );
2294
  }
2295
 
2296
+ // Check the permalink structure.
2297
+ if ( empty( get_option( 'permalink_structure' ) ) ) {
2298
  printf(
2299
  '<div class="notice notice-warning"><p>%s</p></div>',
2300
  sprintf(
2310
  );
2311
  }
2312
 
2313
+ // Check the file permissions.
2314
+ $dirs = array( CACHE_ENABLER_CACHE_DIR, CACHE_ENABLER_SETTINGS_DIR );
2315
+ foreach ( $dirs as $dir ) {
2316
+ $parent_dir = dirname( $dir );
2317
+ if ( file_exists( $parent_dir ) && ! is_writable( $parent_dir ) ) {
2318
+ printf(
2319
+ '<div class="notice notice-warning"><p>%s</p></div>',
 
 
 
2320
  sprintf(
2321
+ // translators: 1. Cache Enabler 2. 755 3. wp-content/cache 4. file permissions
2322
+ esc_html__( '%1$s requires write permissions %2$s in the %3$s directory. Please change the %4$s.', 'cache-enabler' ),
2323
+ '<strong>Cache Enabler</strong>',
2324
+ '<code>755</code>',
2325
+ '<code>' . str_replace( ABSPATH, '', $parent_dir ) . '</code>',
2326
+ sprintf(
2327
+ '<a href="%s" target="_blank" rel="nofollow noopener">%s</a>',
2328
+ 'https://wordpress.org/support/article/changing-file-permissions/',
2329
+ esc_html__( 'file permissions', 'cache-enabler' )
2330
+ )
2331
  )
2332
+ );
2333
+ }
2334
  }
2335
 
2336
+ // Check the Autoptimize HTML optimization.
2337
  if ( defined( 'AUTOPTIMIZE_PLUGIN_DIR' ) && Cache_Enabler_Engine::$settings['minify_html'] && get_option( 'autoptimize_html', '' ) !== '' ) {
2338
  printf(
2339
  '<div class="notice notice-warning"><p>%s</p></div>',
2351
  }
2352
  }
2353
 
 
2354
  /**
2355
+ * Load plugin's translated strings.
2356
  *
2357
+ * @since 1.0.0
 
2358
  */
 
2359
  public static function register_textdomain() {
2360
 
 
2361
  load_plugin_textdomain( 'cache-enabler', false, 'cache-enabler/lang' );
2362
  }
2363
 
 
2364
  /**
2365
+ * Register plugin's settings.
2366
  *
2367
  * @since 1.0.0
2368
  * @change 1.5.0
2369
  */
 
2370
  public static function register_settings() {
2371
 
2372
  register_setting( 'cache_enabler', 'cache_enabler', array( __CLASS__, 'validate_settings' ) );
2373
  }
2374
 
2375
+ /**
2376
+ * Schedule WP-Cron events.
2377
+ *
2378
+ * @since 1.8.0
2379
+ */
2380
+ public static function schedule_events() {
2381
+
2382
+ if ( ! Cache_Enabler_Engine::$started ) {
2383
+ return;
2384
+ }
2385
+
2386
+ $events = self::get_events();
2387
+
2388
+ foreach ( $events as $hook => $recurrence ) {
2389
+ if ( $hook === 'cache_enabler_clear_expired_cache' ) {
2390
+ if ( ! Cache_Enabler_Engine::$settings['cache_expires'] || Cache_Enabler_Engine::$settings['cache_expiry_time'] === 0 ) {
2391
+ continue;
2392
+ }
2393
+ }
2394
+
2395
+ if ( ! wp_next_scheduled( $hook ) ) {
2396
+ wp_schedule_event( time(), $recurrence, $hook );
2397
+ }
2398
+ }
2399
+ }
2400
+
2401
+ /**
2402
+ * Unschedule WP-Cron events.
2403
+ *
2404
+ * @since 1.8.0
2405
+ */
2406
+ public static function unschedule_events() {
2407
+
2408
+ $events = self::get_events();
2409
+
2410
+ foreach ( $events as $hook => $recurrence ) {
2411
+ wp_unschedule_event( wp_next_scheduled( $hook ), $hook );
2412
+ }
2413
+ }
2414
 
2415
  /**
2416
+ * Validate regex.
2417
  *
2418
  * @since 1.2.3
2419
  * @change 1.5.0
2420
  *
2421
+ * @param string $regex Regex.
2422
+ * @return string Validated regex.
2423
  */
 
2424
  public static function validate_regex( $regex ) {
2425
 
2426
  if ( ! empty( $regex ) ) {
2440
  return '';
2441
  }
2442
 
 
2443
  /**
2444
+ * Validate plugin settings.
2445
  *
2446
  * @since 1.0.0
2447
+ * @change 1.8.0
2448
  *
2449
+ * @param array $settings Plugin settings.
2450
+ * @return array Validated plugin settings.
2451
  */
 
2452
  public static function validate_settings( $settings ) {
2453
 
2454
  $validated_settings = array(
2455
  'cache_expires' => (int) ( ! empty( $settings['cache_expires'] ) ),
2456
+ 'cache_expiry_time' => absint( $settings['cache_expiry_time'] ),
2457
  'clear_site_cache_on_saved_post' => (int) ( ! empty( $settings['clear_site_cache_on_saved_post'] ) ),
2458
  'clear_site_cache_on_saved_comment' => (int) ( ! empty( $settings['clear_site_cache_on_saved_comment'] ) ),
2459
+ 'clear_site_cache_on_saved_term' => (int) ( ! empty( $settings['clear_site_cache_on_saved_term'] ) ),
2460
+ 'clear_site_cache_on_saved_user' => (int) ( ! empty( $settings['clear_site_cache_on_saved_user'] ) ),
2461
  'clear_site_cache_on_changed_plugin' => (int) ( ! empty( $settings['clear_site_cache_on_changed_plugin'] ) ),
2462
  'convert_image_urls_to_webp' => (int) ( ! empty( $settings['convert_image_urls_to_webp'] ) ),
2463
  'mobile_cache' => (int) ( ! empty( $settings['mobile_cache'] ) ),
2470
  'excluded_cookies' => (string) self::validate_regex( $settings['excluded_cookies'] ),
2471
  );
2472
 
 
2473
  $validated_settings = wp_parse_args( $validated_settings, self::get_default_settings( 'system' ) );
2474
 
 
2475
  if ( ! empty( $settings['clear_site_cache_on_saved_settings'] ) ) {
2476
  self::clear_site_cache();
2477
  set_transient( self::get_cache_cleared_transient_name(), 1 );
2480
  return $validated_settings;
2481
  }
2482
 
 
2483
  /**
2484
+ * Plugin settings page.
2485
  *
2486
  * @since 1.0.0
2487
+ * @change 1.8.0
2488
  */
 
2489
  public static function settings_page() {
2490
 
2491
  ?>
2549
  <p class="subheading"><?php esc_html_e( 'Clearing', 'cache-enabler' ); ?></p>
2550
  <label for="cache_enabler_clear_site_cache_on_saved_post">
2551
  <input name="cache_enabler[clear_site_cache_on_saved_post]" type="checkbox" id="cache_enabler_clear_site_cache_on_saved_post" value="1" <?php checked( '1', Cache_Enabler_Engine::$settings['clear_site_cache_on_saved_post'] ); ?> />
2552
+ <?php esc_html_e( 'Clear the site cache if any post type has been published, updated, or trashed (instead of the post cache).', 'cache-enabler' ); ?>
2553
  </label>
2554
 
2555
  <br />
2556
 
2557
  <label for="cache_enabler_clear_site_cache_on_saved_comment">
2558
  <input name="cache_enabler[clear_site_cache_on_saved_comment]" type="checkbox" id="cache_enabler_clear_site_cache_on_saved_comment" value="1" <?php checked( '1', Cache_Enabler_Engine::$settings['clear_site_cache_on_saved_comment'] ); ?> />
2559
+ <?php esc_html_e( 'Clear the site cache if a comment has been posted, updated, spammed, or trashed (instead of the comment cache).', 'cache-enabler' ); ?>
2560
+ </label>
2561
+
2562
+ <br />
2563
+
2564
+ <label for="cache_enabler_clear_site_cache_on_saved_term">
2565
+ <input name="cache_enabler[clear_site_cache_on_saved_term]" type="checkbox" id="cache_enabler_clear_site_cache_on_saved_term" value="1" <?php checked( '1', Cache_Enabler_Engine::$settings['clear_site_cache_on_saved_term'] ); ?> />
2566
+ <?php esc_html_e( 'Clear the site cache if a term has been added, updated, or deleted (instead of the term cache).', 'cache-enabler' ); ?>
2567
+ </label>
2568
+
2569
+ <br />
2570
+
2571
+ <label for="cache_enabler_clear_site_cache_on_saved_user">
2572
+ <input name="cache_enabler[clear_site_cache_on_saved_user]" type="checkbox" id="cache_enabler_clear_site_cache_on_saved_user" value="1" <?php checked( '1', Cache_Enabler_Engine::$settings['clear_site_cache_on_saved_user'] ); ?> />
2573
+ <?php esc_html_e( 'Clear the site cache if a user has been added, updated, or deleted (instead of the user cache).', 'cache-enabler' ); ?>
2574
  </label>
2575
 
2576
  <br />
2582
 
2583
  <br />
2584
 
2585
+ <p class="subheading"><?php esc_html_e( 'Versions', 'cache-enabler' ); ?></p>
2586
  <label for="cache_enabler_convert_image_urls_to_webp">
2587
  <input name="cache_enabler[convert_image_urls_to_webp]" type="checkbox" id="cache_enabler_convert_image_urls_to_webp" value="1" <?php checked( '1', Cache_Enabler_Engine::$settings['convert_image_urls_to_webp'] ); ?> />
2588
  <?php
2589
  printf(
2590
  // translators: %s: Optimus
2591
+ esc_html__( 'Create a cached version for WebP support. Convert your images to WebP with %s.', 'cache-enabler' ),
2592
  '<a href="https://optimus.io" target="_blank" rel="nofollow noopener">Optimus</a>'
2593
  );
2594
  ?>
2598
 
2599
  <label for="cache_enabler_mobile_cache">
2600
  <input name="cache_enabler[mobile_cache]" type="checkbox" id="cache_enabler_mobile_cache" value="1" <?php checked( '1', Cache_Enabler_Engine::$settings['mobile_cache'] ); ?> />
2601
+ <?php esc_html_e( 'Create a cached version for mobile devices.', 'cache-enabler' ); ?>
2602
  </label>
2603
 
2604
  <br />
2605
 
2606
  <label for="cache_enabler_compress_cache">
2607
  <input name="cache_enabler[compress_cache]" type="checkbox" id="cache_enabler_compress_cache" value="1" <?php checked( '1', Cache_Enabler_Engine::$settings['compress_cache'] ); ?> />
2608
+ <?php ( function_exists( 'brotli_compress' ) && is_ssl() ) ? esc_html_e( 'Create a cached version pre-compressed with Brotli or Gzip.', 'cache-enabler' ) : esc_html_e( 'Create a cached version pre-compressed with Gzip.', 'cache-enabler' ); ?>
2609
  </label>
2610
 
2611
  <br />
inc/cache_enabler_cli.class.php CHANGED
@@ -1,6 +1,6 @@
1
  <?php
2
  /**
3
- * Interact with Cache Enabler.
4
  *
5
  * @since 1.3.5
6
  */
@@ -10,20 +10,20 @@ if ( ! defined( 'ABSPATH' ) ) {
10
  }
11
 
12
  class Cache_Enabler_CLI {
13
-
14
  /**
15
  * Clear the page cache.
16
  *
17
  * ## OPTIONS
18
  *
19
  * [--ids=<id>]
20
- * : Clear the cache for given post ID(s). Separate multiple IDs with commas.
21
  *
22
  * [--urls=<url>]
23
- * : Clear the cache for the given URL(s). Separate multiple URLs with commas.
 
24
  *
25
  * [--sites=<site>]
26
- * : Clear the cache for the given blog ID(s). Separate multiple blog IDs with commas.
27
  *
28
  * ## EXAMPLES
29
  *
@@ -31,21 +31,28 @@ class Cache_Enabler_CLI {
31
  * $ wp cache-enabler clear
32
  * Success: Site cache cleared.
33
  *
34
- * # Clear the page cache for post IDs 1, 2, and 3.
35
  * $ wp cache-enabler clear --ids=1,2,3
36
  * Success: Pages cache cleared.
37
  *
38
- * # Clear the page cache for a particular URL.
39
- * $ wp cache-enabler clear --urls=https://www.example.com/about-us/
 
 
 
 
40
  * Success: Page cache cleared.
41
  *
42
- * # Clear all pages cache for sites with blog IDs 1, 2, and 3.
 
 
 
 
43
  * $ wp cache-enabler clear --sites=1,2,3
44
  * Success: Sites cache cleared.
45
  *
46
  * @alias clear
47
  */
48
-
49
  public function clear( $args, $assoc_args ) {
50
 
51
  $assoc_args = wp_parse_args(
@@ -57,19 +64,16 @@ class Cache_Enabler_CLI {
57
  )
58
  );
59
 
60
- // clear complete cache if no associative arguments are given
61
- if ( empty( $assoc_args['ids'] ) && empty( $assoc_args['urls'] ) && empty( $assoc_args['sites'] ) ) {
62
  Cache_Enabler::clear_complete_cache();
63
 
64
- return WP_CLI::success( ( is_multisite() ) ? esc_html__( 'Network cache cleared.', 'cache-enabler' ) : esc_html__( 'Site cache cleared.', 'cache-enabler' ) );
65
  }
66
 
67
- // clear page(s) cache by post ID(s) and/or URL(s)
68
- if ( ! empty( $assoc_args['ids'] ) || ! empty( $assoc_args['urls'] ) ) {
69
- array_map( 'Cache_Enabler::clear_page_cache_by_post_id', explode( ',', $assoc_args['ids'] ) );
70
  array_map( 'Cache_Enabler::clear_page_cache_by_url', explode( ',', $assoc_args['urls'] ) );
71
 
72
- // check if there is more than one ID and/or URL
73
  $separators = substr_count( $assoc_args['ids'], ',' ) + substr_count( $assoc_args['urls'], ',' );
74
 
75
  if ( $separators > 0 ) {
@@ -79,11 +83,9 @@ class Cache_Enabler_CLI {
79
  }
80
  }
81
 
82
- // clear pages cache by blog ID(s)
83
- if ( ! empty( $assoc_args['sites'] ) ) {
84
- array_map( 'Cache_Enabler::clear_site_cache_by_blog_id', explode( ',', $assoc_args['sites'] ) );
85
 
86
- // check if there is more than one site
87
  $separators = substr_count( $assoc_args['sites'], ',' );
88
 
89
  if ( $separators > 0 ) {
@@ -94,6 +96,3 @@ class Cache_Enabler_CLI {
94
  }
95
  }
96
  }
97
-
98
- // add WP-CLI command
99
- WP_CLI::add_command( 'cache-enabler', 'Cache_Enabler_CLI' );
1
  <?php
2
  /**
3
+ * Interact with Cache Enabler from the command line.
4
  *
5
  * @since 1.3.5
6
  */
10
  }
11
 
12
  class Cache_Enabler_CLI {
 
13
  /**
14
  * Clear the page cache.
15
  *
16
  * ## OPTIONS
17
  *
18
  * [--ids=<id>]
19
+ * : Clear the cache of a given post ID. Separate multiple IDs with commas.
20
  *
21
  * [--urls=<url>]
22
+ * : Clear the cache of a given URL. The URL can be with or without a scheme,
23
+ * wildcard path, and query string. Separate multiple URLs with commas.
24
  *
25
  * [--sites=<site>]
26
+ * : Clear the cache of a given blog ID. Separate multiple blog IDs with commas.
27
  *
28
  * ## EXAMPLES
29
  *
31
  * $ wp cache-enabler clear
32
  * Success: Site cache cleared.
33
  *
34
+ * # Clear the page cache of post IDs 1, 2, and 3.
35
  * $ wp cache-enabler clear --ids=1,2,3
36
  * Success: Pages cache cleared.
37
  *
38
+ * # Clear the page cache of https://www.example.com/about-us/.
39
+ * $ wp cache-enabler clear --urls=www.example.com/about-us/
40
+ * Success: Page cache cleared.
41
+ *
42
+ * # Clear the page cache of any URL that starts with https://www.example.com/blog/how-to-.
43
+ * $ wp cache-enabler clear --urls=www.example.com/blog/how-to-*
44
  * Success: Page cache cleared.
45
  *
46
+ * # Clear the page cache of https://www.example.com/blog/ and all of its subpages.
47
+ * $ wp cache-enabler clear --urls=www.example.com/blog/*
48
+ * Success: Page cache cleared.
49
+ *
50
+ * # Clear the page cache of sites with blog IDs 1, 2, and 3.
51
  * $ wp cache-enabler clear --sites=1,2,3
52
  * Success: Sites cache cleared.
53
  *
54
  * @alias clear
55
  */
 
56
  public function clear( $args, $assoc_args ) {
57
 
58
  $assoc_args = wp_parse_args(
64
  )
65
  );
66
 
67
+ if ( $assoc_args['ids'] === '' && $assoc_args['urls'] === '' && $assoc_args['sites'] === '' ) {
 
68
  Cache_Enabler::clear_complete_cache();
69
 
70
+ return WP_CLI::success( is_multisite() ? esc_html__( 'Network cache cleared.', 'cache-enabler' ) : esc_html__( 'Site cache cleared.', 'cache-enabler' ) );
71
  }
72
 
73
+ if ( $assoc_args['ids'] !== '' || $assoc_args['urls'] !== '' ) {
74
+ array_map( 'Cache_Enabler::clear_page_cache_by_post', explode( ',', $assoc_args['ids'] ) );
 
75
  array_map( 'Cache_Enabler::clear_page_cache_by_url', explode( ',', $assoc_args['urls'] ) );
76
 
 
77
  $separators = substr_count( $assoc_args['ids'], ',' ) + substr_count( $assoc_args['urls'], ',' );
78
 
79
  if ( $separators > 0 ) {
83
  }
84
  }
85
 
86
+ if ( $assoc_args['sites'] !== '' ) {
87
+ array_map( 'Cache_Enabler::clear_page_cache_by_site', explode( ',', $assoc_args['sites'] ) );
 
88
 
 
89
  $separators = substr_count( $assoc_args['sites'], ',' );
90
 
91
  if ( $separators > 0 ) {
96
  }
97
  }
98
  }
 
 
 
inc/cache_enabler_disk.class.php CHANGED
@@ -1,6 +1,6 @@
1
  <?php
2
  /**
3
- * Cache Enabler disk handling
4
  *
5
  * @since 1.0.0
6
  */
@@ -10,471 +10,728 @@ if ( ! defined( 'ABSPATH' ) ) {
10
  }
11
 
12
  final class Cache_Enabler_Disk {
13
-
14
  /**
15
- * cache directory
16
- *
17
- * @since 1.5.0
18
- * @change 1.5.0
19
  *
20
- * @var string
 
21
  */
22
-
23
  public static $cache_dir = WP_CONTENT_DIR . '/cache/cache-enabler';
24
 
25
-
26
  /**
27
- * settings directory
28
  *
29
- * @since 1.5.0
30
- * @change 1.5.0
31
  *
32
- * @var string
33
  */
34
-
35
- private static $settings_dir = WP_CONTENT_DIR . '/settings/cache-enabler';
36
-
37
 
38
  /**
39
- * directories cleared
40
- *
41
- * @since 1.6.0
42
- * @change 1.6.0
43
- *
44
- * @var array
45
- */
46
-
47
- private static $dir_cleared = array();
48
-
49
-
50
- /**
51
- * configure system files
52
  *
53
  * @since 1.5.0
54
- * @change 1.7.0
55
  */
56
-
57
  public static function setup() {
58
 
59
- // add advanced-cache.php drop-in
60
- copy( CACHE_ENABLER_DIR . '/advanced-cache.php', WP_CONTENT_DIR . '/advanced-cache.php' );
61
-
62
- // set WP_CACHE constant in config file if not already set
63
  self::set_wp_cache_constant();
64
  }
65
 
66
-
67
  /**
68
- * clean system files
69
  *
70
  * @since 1.5.0
71
- * @change 1.5.0
72
  */
73
-
74
  public static function clean() {
75
 
76
- // delete settings file
77
  self::delete_settings_file();
78
 
79
- // check if settings directory exists
80
- if ( ! is_dir( self::$settings_dir ) ) {
81
- // delete old advanced cache settings file(s) (1.4.0)
82
- array_map( 'unlink', glob( WP_CONTENT_DIR . '/cache/cache-enabler-advcache-*.json' ) );
83
- // delete incorrect advanced cache settings file(s) that may have been created in 1.4.0 (1.4.5)
84
- array_map( 'unlink', glob( ABSPATH . 'CE_SETTINGS_PATH-*.json' ) );
85
- // delete advanced-cache.php drop-in
86
  @unlink( WP_CONTENT_DIR . '/advanced-cache.php' );
87
- // unset WP_CACHE constant in config file if set by Cache Enabler
88
  self::set_wp_cache_constant( false );
89
  }
90
  }
91
 
92
-
93
  /**
94
- * store cached page
95
  *
96
- * @since 1.0.0
97
  * @change 1.7.0
98
  *
99
- * @param string $page_contents contents of a page from the output buffer
100
  */
101
-
102
  public static function cache_page( $page_contents ) {
103
 
104
- // page contents before store hook
 
 
 
 
 
 
105
  $page_contents = apply_filters( 'cache_enabler_page_contents_before_store', $page_contents );
106
-
107
- // deprecated page contents before store hook
108
  $page_contents = apply_filters_deprecated( 'cache_enabler_before_store', array( $page_contents ), '1.6.0', 'cache_enabler_page_contents_before_store' );
109
 
110
- // create cached page to be stored
111
  self::create_cache_file( $page_contents );
112
  }
113
 
114
-
115
  /**
116
- * check if cached page exists
117
  *
118
- * @since 1.0.0
119
  * @change 1.7.0
120
  *
121
- * @param string $cache_file file path to potentially cached page
122
- * @return boolean true if cached page exists and is readable, false otherwise
123
  */
124
-
125
  public static function cache_exists( $cache_file ) {
126
 
127
  return is_readable( $cache_file );
128
  }
129
 
130
-
131
  /**
132
- * check if cached page expired
133
  *
134
- * @since 1.0.1
135
- * @change 1.7.0
136
  *
137
- * @param string $cache_file file path to existing cached page
138
- * @return boolean true if cached page expired, false otherwise
139
  */
140
-
141
  public static function cache_expired( $cache_file ) {
142
 
143
- // check if cached pages are set to expire
144
  if ( ! Cache_Enabler_Engine::$settings['cache_expires'] || Cache_Enabler_Engine::$settings['cache_expiry_time'] === 0 ) {
145
  return false;
146
  }
147
 
148
- $now = time();
149
- $expires_seconds = 60 * 60 * Cache_Enabler_Engine::$settings['cache_expiry_time'];
150
 
151
- // check if cached page has expired
152
- if ( ( filemtime( $cache_file ) + $expires_seconds ) <= $now ) {
153
  return true;
154
  }
155
 
156
  return false;
157
  }
158
 
159
-
160
  /**
161
- * clear cached page(s)
162
- *
163
- * @since 1.0.0
164
- * @change 1.7.0
165
- *
166
- * @param string $clear_url full URL to potentially cached page
167
- * @param string $clear_type clear the `pagination` cache or all `subpages` cache instead of only the `page` cache
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
168
  */
 
169
 
170
- public static function clear_cache( $clear_url = null, $clear_type = 'page' ) {
 
 
 
171
 
172
- // check if cache should be cleared
173
- if ( empty( $clear_url ) ) {
174
- return;
175
  }
176
 
177
- // get directory
178
- $dir = self::get_cache_file_dir( $clear_url );
179
 
180
- // check if directory exists
181
- if ( ! is_dir( $dir ) ) {
182
- return;
183
  }
184
 
185
- // check if page and subpages cache should be cleared
186
- if ( $clear_type === 'subpages' ) {
187
- self::clear_dir( $dir );
188
- // clear page and/or pagination cache otherwise
189
- } else {
190
- $skip_child_dir = true;
191
- self::clear_dir( $dir, $skip_child_dir );
192
-
193
- if ( $clear_type === 'pagination' ) {
194
- $pagination_base = $GLOBALS['wp_rewrite']->pagination_base;
195
- if ( strlen( $pagination_base ) > 0 ) {
196
- $pagination_dir = $dir . '/' . $pagination_base;
197
- self::clear_dir( $pagination_dir );
198
- }
199
  }
200
  }
201
 
202
- // delete parent directory if empty
203
- self::delete_parent_dir( $dir );
 
 
 
 
 
 
 
 
 
 
 
 
204
 
205
- // cache cleared hooks
206
- foreach ( self::$dir_cleared as $dir => $dir_objects ) {
207
- if ( strpos( $dir, self::$cache_dir ) !== false ) {
208
- if ( Cache_Enabler::$fire_page_cache_cleared_hook ) {
209
- if ( ! empty( preg_grep( '/index/', $dir_objects ) ) ) {
210
- // page cache cleared hook
211
- $page_cleared_url = parse_url( home_url(), PHP_URL_SCHEME ) . '://' . str_replace( self::$cache_dir . '/', '', $dir );
212
- $page_cleared_id = url_to_postid( $page_cleared_url );
213
- do_action( 'cache_enabler_page_cache_cleared', $page_cleared_url, $page_cleared_id );
214
- do_action( 'ce_action_cache_by_url_cleared', $page_cleared_url ); // deprecated in 1.6.0
215
- }
216
- } else {
217
- // complete cache cleared hook
218
- if ( $dir === self::$cache_dir ) {
219
- do_action( 'cache_enabler_complete_cache_cleared' );
220
- do_action( 'ce_action_cache_cleared' ); // deprecated in 1.6.0
221
- }
222
 
223
- // site cache cleared hook
224
- if ( $dir === self::get_cache_file_dir( home_url() ) ) {
225
- $site_cleared_url = home_url();
226
- $site_cleared_id = get_current_blog_id();
227
- do_action( 'cache_enabler_site_cache_cleared', $site_cleared_url, $site_cleared_id );
 
 
228
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
229
  }
230
 
231
- unset( self::$dir_cleared[ $dir ] );
 
232
  }
233
  }
234
- }
235
 
 
 
 
 
 
 
 
 
 
 
 
 
 
236
 
237
  /**
238
- * clear directory
239
- *
240
- * @since 1.0.0
241
- * @change 1.6.0
242
  *
243
- * @param string $dir directory path to clear
244
- * @param boolean $skip_child_dir whether or not child directories should be skipped
245
  */
 
246
 
247
- private static function clear_dir( $dir, $skip_child_dir = false ) {
 
248
 
249
- // remove trailing slash if there happens to be one
250
- $dir = untrailingslashit( $dir );
 
 
 
 
 
251
 
252
- // check if directory exists
253
- if ( ! is_dir( $dir ) ) {
254
- return;
255
- }
256
 
257
- // get directory objects
258
- $dir_objects = self::get_dir_objects( $dir );
 
 
 
 
 
 
259
 
260
- foreach ( $dir_objects as $dir_object ) {
261
- // get full path
262
- $dir_object = $dir . '/' . $dir_object;
263
 
264
- if ( is_dir( $dir_object ) && ! $skip_child_dir ) {
265
- self::clear_dir( $dir_object );
266
- } elseif ( is_file( $dir_object ) ) {
267
- // clear cached page variant
268
- unlink( $dir_object );
269
- }
270
  }
271
 
272
- // delete directory if empty
273
- @rmdir( $dir );
 
274
 
275
- // clear file status cache
276
- clearstatcache();
 
277
 
278
- // add cleared directory to directories cleared list
279
- self::$dir_cleared[ $dir ] = $dir_objects;
280
- }
281
 
 
 
282
 
283
  /**
284
- * create file for cache
285
  *
286
- * @since 1.0.0
287
- * @change 1.7.0
288
  *
289
- * @param string $page_contents contents of a page from the output buffer
290
  */
291
-
292
  private static function create_cache_file( $page_contents ) {
293
 
294
- // check cache file requirements
295
  if ( ! is_string( $page_contents ) || strlen( $page_contents ) === 0 ) {
296
  return;
297
  }
298
 
299
- // get new cache file
300
  $new_cache_file = self::get_cache_file();
301
  $new_cache_file_dir = dirname( $new_cache_file );
302
  $new_cache_file_name = basename( $new_cache_file );
303
 
304
- // if setting enabled minify HTML
305
  if ( Cache_Enabler_Engine::$settings['minify_html'] ) {
306
  $page_contents = self::minify_html( $page_contents );
307
  }
308
 
309
- // append cache signature
310
  $page_contents = $page_contents . self::get_cache_signature( $new_cache_file_name );
311
 
312
- // convert image URLs to WebP if applicable
313
  if ( strpos( $new_cache_file_name, 'webp' ) !== false ) {
314
  $page_contents = self::converter( $page_contents );
315
  }
316
 
317
- // compress page contents with Gzip if applicable
318
- if ( strpos( $new_cache_file_name, 'gz' ) !== false ) {
319
- $page_contents = gzencode( $page_contents, 9 );
 
 
 
 
 
320
 
321
- // check if Gzip compression failed
322
- if ( $page_contents === false ) {
323
- return;
324
- }
325
  }
326
 
327
- // create directory if necessary
328
  if ( ! self::mkdir_p( $new_cache_file_dir ) ) {
329
  return;
330
  }
331
 
332
- // create new cache file
333
- file_put_contents( $new_cache_file, $page_contents, LOCK_EX );
334
-
335
- // clear file status cache
336
- clearstatcache();
337
-
338
- // set file permissions
339
- $new_cache_file_stats = @stat( $new_cache_file_dir );
340
- $new_cache_file_perms = $new_cache_file_stats['mode'] & 0007777;
341
- $new_cache_file_perms = $new_cache_file_perms & 0000666;
342
- @chmod( $new_cache_file, $new_cache_file_perms );
343
-
344
- // clear file status cache
345
- clearstatcache();
 
 
 
 
 
 
 
 
 
 
 
 
 
346
  }
347
 
348
-
349
  /**
350
- * create settings file
351
  *
352
- * @since 1.2.3
353
- * @change 1.7.0
354
  *
355
- * @param array $settings settings from database
356
- * @return string $new_settings_file file path to new settings file
357
  */
358
-
359
  public static function create_settings_file( $settings ) {
360
 
361
- // check settings file requirements
362
  if ( ! is_array( $settings ) || ! function_exists( 'home_url' ) ) {
363
- return;
364
  }
365
 
366
- // get new settings file
367
  $new_settings_file = self::get_settings_file();
368
 
369
- // add new settings file contents
370
  $new_settings_file_contents = '<?php' . PHP_EOL;
371
  $new_settings_file_contents .= '/**' . PHP_EOL;
372
- $new_settings_file_contents .= ' * Cache Enabler settings for ' . home_url() . PHP_EOL;
 
 
 
373
  $new_settings_file_contents .= ' *' . PHP_EOL;
374
- $new_settings_file_contents .= ' * @since 1.5.0' . PHP_EOL;
375
- $new_settings_file_contents .= ' * @change 1.5.0' . PHP_EOL;
376
  $new_settings_file_contents .= ' *' . PHP_EOL;
377
- $new_settings_file_contents .= ' * @generated ' . self::get_current_time() . PHP_EOL;
 
 
 
 
 
 
 
 
 
 
 
378
  $new_settings_file_contents .= ' */' . PHP_EOL;
379
  $new_settings_file_contents .= PHP_EOL;
380
  $new_settings_file_contents .= 'return ' . var_export( $settings, true ) . ';';
381
 
382
- // create directory if necessary
383
  if ( ! self::mkdir_p( dirname( $new_settings_file ) ) ) {
384
- return;
385
  }
386
 
387
- // create new settings file
388
- file_put_contents( $new_settings_file, $new_settings_file_contents, LOCK_EX );
389
 
390
- return $new_settings_file;
391
  }
392
 
393
-
394
  /**
395
- * get cache file
396
  *
397
- * @since 1.7.0
398
- * @change 1.7.0
399
  *
400
- * @return string $cache_file file path to new or potentially cached page
 
401
  */
 
402
 
403
- public static function get_cache_file() {
 
 
404
 
405
- $cache_file = sprintf(
406
- '%s/%s',
407
- self::get_cache_file_dir(),
408
- self::get_cache_file_name()
409
- );
410
 
411
- return $cache_file;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
412
  }
413
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
414
 
415
  /**
416
- * get cache file directory path
417
  *
418
- * @since 1.0.0
419
- * @change 1.7.0
 
420
  *
421
- * @param string $url full URL to potentially cached page
422
- * @return string $cache_file_dir directory path to new or potentially cached page, empty if provided URL is invalid
 
 
 
423
  */
 
424
 
425
- private static function get_cache_file_dir( $url = null ) {
 
 
426
 
427
- $cache_file_dir = '';
 
 
 
428
 
429
- // validate URL
430
- if ( $url && ! filter_var( $url, FILTER_VALIDATE_URL ) ) {
431
- return $cache_file_dir;
 
 
432
  }
433
 
434
- $cache_file_dir = sprintf(
435
  '%s/%s%s',
436
- self::$cache_dir,
437
- ( $url ) ? parse_url( $url, PHP_URL_HOST ) : strtolower( Cache_Enabler_Engine::$request_headers['Host'] ),
438
- parse_url( ( $url ) ? $url : $_SERVER['REQUEST_URI'], PHP_URL_PATH )
439
  );
440
 
441
- // remove trailing slash
442
- $cache_file_dir = rtrim( $cache_file_dir, '/\\' );
443
 
444
- return $cache_file_dir;
445
  }
446
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
447
 
448
  /**
449
- * get cache file name
 
 
 
 
 
 
450
  *
451
  * @since 1.7.0
452
- * @change 1.7.0
453
  *
454
- * @return string $cache_file_name file name for new or potentially cached page
455
  */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
456
 
 
 
 
 
 
 
 
457
  private static function get_cache_file_name() {
458
 
459
- $cache_keys = self::get_cache_keys();
460
  $cache_file_name = $cache_keys['scheme'] . 'index' . $cache_keys['device'] . $cache_keys['webp'] . '.html' . $cache_keys['compression'];
461
 
462
  return $cache_file_name;
463
  }
464
 
465
-
466
  /**
467
- * get cache keys
 
 
468
  *
469
  * @since 1.7.0
470
- * @change 1.7.0
471
  *
472
- * @return array $cache_keys cache keys to new or potentially cached page
473
  */
474
-
475
  private static function get_cache_keys() {
476
 
477
- // set default cache keys
478
  $cache_keys = array(
479
  'scheme' => 'http-',
480
  'device' => '',
@@ -482,16 +739,16 @@ final class Cache_Enabler_Disk {
482
  'compression' => '',
483
  );
484
 
485
- // scheme
486
  if ( isset( $_SERVER['HTTPS'] ) && ( strtolower( $_SERVER['HTTPS'] ) === 'on' || $_SERVER['HTTPS'] == '1' ) ) {
487
  $cache_keys['scheme'] = 'https-';
488
  } elseif ( isset( $_SERVER['SERVER_PORT'] ) && $_SERVER['SERVER_PORT'] == '443' ) {
489
  $cache_keys['scheme'] = 'https-';
490
- } elseif ( Cache_Enabler_Engine::$request_headers['X-Forwarded-Proto'] === 'https' || Cache_Enabler_Engine::$request_headers['X-Forwarded-Scheme'] === 'https' ) {
 
 
491
  $cache_keys['scheme'] = 'https-';
492
  }
493
 
494
- // device
495
  if ( Cache_Enabler_Engine::$settings['mobile_cache'] ) {
496
  if ( strpos( Cache_Enabler_Engine::$request_headers['User-Agent'], 'Mobile' ) !== false
497
  || strpos( Cache_Enabler_Engine::$request_headers['User-Agent'], 'Android' ) !== false
@@ -505,16 +762,19 @@ final class Cache_Enabler_Disk {
505
  }
506
  }
507
 
508
- // WebP
509
  if ( Cache_Enabler_Engine::$settings['convert_image_urls_to_webp'] ) {
510
  if ( strpos( Cache_Enabler_Engine::$request_headers['Accept'], 'image/webp' ) !== false ) {
511
  $cache_keys['webp'] = '-webp';
512
  }
513
  }
514
 
515
- // compression
516
  if ( Cache_Enabler_Engine::$settings['compress_cache'] ) {
517
- if ( strpos( Cache_Enabler_Engine::$request_headers['Accept-Encoding'], 'gzip' ) !== false ) {
 
 
 
 
 
518
  $cache_keys['compression'] = '.gz';
519
  }
520
  }
@@ -522,17 +782,60 @@ final class Cache_Enabler_Disk {
522
  return $cache_keys;
523
  }
524
 
525
-
526
  /**
527
- * get cache signature
528
  *
529
- * @since 1.0.0
530
- * @change 1.7.0
 
 
 
 
 
531
  *
532
- * @param string $cache_file_name file name for new cached page
533
- * @return string $cache_signature cache signature
534
  */
 
535
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
536
  private static function get_cache_signature( $cache_file_name ) {
537
 
538
  $cache_signature = sprintf(
@@ -545,108 +848,104 @@ final class Cache_Enabler_Disk {
545
  return $cache_signature;
546
  }
547
 
548
-
549
  /**
550
- * get cache size from disk
551
  *
552
- * @since 1.0.0
553
- * @change 1.7.0
554
- *
555
- * @param string $dir directory path to scan recursively
556
- * @return integer $cache_size cache size in bytes
557
  */
558
-
559
  public static function get_cache_size( $dir = null ) {
560
 
561
- $cache_size = 0;
562
-
563
- // get directory objects if provided directory exists
564
- if ( is_dir( $dir ) ) {
565
- $dir_objects = self::get_dir_objects( $dir );
566
- // get site objects otherwise
567
  } else {
568
- $dir_objects = self::get_site_objects( home_url() );
 
 
569
  }
570
 
571
- // check if directory is empty
572
- if ( empty( $dir_objects ) ) {
573
- return $cache_size;
574
- }
575
 
576
- foreach ( $dir_objects as $dir_object ) {
577
- // get full path
578
- $dir_object = trailingslashit( ( $dir ) ? $dir : ( self::$cache_dir . '/' . parse_url( home_url(), PHP_URL_HOST ) . parse_url( home_url(), PHP_URL_PATH ) ) ) . $dir_object;
 
 
 
 
 
 
 
 
 
 
579
 
580
- if ( is_dir( $dir_object ) ) {
581
- $cache_size += self::get_cache_size( $dir_object );
582
- } elseif ( is_file( $dir_object ) ) {
583
- $cache_size += filesize( $dir_object );
584
- }
585
  }
586
 
587
- return $cache_size;
588
- }
589
 
 
 
590
 
591
  /**
592
- * get settings file
593
  *
594
  * @since 1.4.0
595
- * @change 1.5.5
596
  *
597
- * @param boolean $fallback whether or not fallback settings file should be returned
598
- * @return string $settings_file file path to settings file
599
  */
600
-
601
  private static function get_settings_file( $fallback = false ) {
602
 
603
  $settings_file = sprintf(
604
  '%s/%s',
605
- self::$settings_dir,
606
  self::get_settings_file_name( $fallback )
607
  );
608
 
609
  return $settings_file;
610
  }
611
 
612
-
613
  /**
614
- * get settings file name
615
  *
616
  * @since 1.5.5
617
- * @change 1.7.0
618
  *
619
- * @param boolean $fallback whether or not fallback settings file name should be returned
620
- * @param boolean $skip_blog_path whether or not blog path should be included in settings file name
621
- * @return string $settings_file_name file name for settings file
 
622
  */
623
-
624
  private static function get_settings_file_name( $fallback = false, $skip_blog_path = false ) {
625
 
626
  $settings_file_name = '';
627
 
628
- // if creating or deleting settings file
629
  if ( function_exists( 'home_url' ) ) {
630
  $settings_file_name = parse_url( home_url(), PHP_URL_HOST );
631
 
632
- // subdirectory network
633
  if ( is_multisite() && defined( 'SUBDOMAIN_INSTALL' ) && ! SUBDOMAIN_INSTALL ) {
634
  $blog_path = Cache_Enabler::get_blog_path();
635
  $settings_file_name .= ( ! empty( $blog_path ) ) ? '.' . trim( $blog_path, '/' ) : '';
636
  }
637
 
638
  $settings_file_name .= '.php';
639
- // if getting settings from settings file
640
- } elseif ( is_dir( self::$settings_dir ) ) {
641
  if ( $fallback ) {
642
- $settings_files = self::get_dir_objects( self::$settings_dir );
643
  $settings_file_regex = '/\.php$/';
644
 
645
  if ( is_multisite() ) {
646
  $settings_file_regex = '/^' . strtolower( Cache_Enabler_Engine::$request_headers['Host'] );
647
  $settings_file_regex = str_replace( '.', '\.', $settings_file_regex );
648
 
649
- // subdirectory network
650
  if ( defined( 'SUBDOMAIN_INSTALL' ) && ! SUBDOMAIN_INSTALL && ! $skip_blog_path ) {
651
  $url_path = trim( parse_url( $_SERVER['REQUEST_URI'], PHP_URL_PATH ), '/' );
652
 
@@ -672,7 +971,6 @@ final class Cache_Enabler_Disk {
672
  } else {
673
  $settings_file_name = strtolower( Cache_Enabler_Engine::$request_headers['Host'] );
674
 
675
- // subdirectory network
676
  if ( is_multisite() && defined( 'SUBDOMAIN_INSTALL' ) && ! SUBDOMAIN_INSTALL && ! $skip_blog_path ) {
677
  $url_path = $_SERVER['REQUEST_URI'];
678
  $url_path_pieces = explode( '/', $url_path, 3 );
@@ -684,8 +982,8 @@ final class Cache_Enabler_Disk {
684
 
685
  $settings_file_name .= '.php';
686
 
687
- // check if main site
688
- if ( ! is_file( self::$settings_dir . '/' . $settings_file_name ) ) {
689
  $fallback = false;
690
  $skip_blog_path = true;
691
  $settings_file_name = self::get_settings_file_name( $fallback, $skip_blog_path );
@@ -699,107 +997,150 @@ final class Cache_Enabler_Disk {
699
  return $settings_file_name;
700
  }
701
 
702
-
703
  /**
704
- * get settings from settings file
 
 
 
 
 
 
705
  *
706
  * @since 1.5.0
707
- * @change 1.6.0
 
708
  *
709
- * @return array $settings current settings from settings file
 
 
710
  */
 
711
 
712
- public static function get_settings() {
713
-
714
- $settings = array();
715
-
716
- // get settings file
717
  $settings_file = self::get_settings_file();
718
 
719
- // include settings file if it exists
720
  if ( is_file( $settings_file ) ) {
721
- $settings = include_once $settings_file;
722
- // try to get fallback settings file otherwise
723
  } else {
724
- $fallback = true;
725
- $fallback_settings_file = self::get_settings_file( $fallback );
726
 
727
- if ( is_file( $fallback_settings_file ) ) {
728
- $settings = include_once $fallback_settings_file;
729
  }
730
  }
731
 
732
- // create settings file if it does not exist and in late engine start
 
 
 
 
 
733
  if ( empty( $settings ) && class_exists( 'Cache_Enabler' ) ) {
734
- $new_settings_file = self::create_settings_file( Cache_Enabler::get_settings() );
 
 
 
 
 
 
 
735
 
736
- if ( is_file( $new_settings_file ) ) {
737
- $settings = include_once $new_settings_file;
 
738
  }
739
  }
740
 
741
  return $settings;
742
  }
743
 
744
-
745
  /**
746
- * get directory file system objects
747
  *
748
  * @since 1.4.7
749
- * @change 1.6.0
750
- *
751
- * @param string $dir directory path to scan
752
- * @return array $dir_objects directory objects
 
 
 
 
 
 
753
  */
 
754
 
755
- private static function get_dir_objects( $dir ) {
756
 
757
- $dir_objects = scandir( $dir );
 
 
758
 
759
- if ( is_array( $dir_objects ) ) {
760
- $dir_objects = array_diff( $dir_objects, array( '..', '.' ) );
761
- } else {
762
- $dir_objects = array();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
763
  }
764
 
765
  return $dir_objects;
766
  }
767
 
768
-
769
  /**
770
- * get site file system objects
771
- *
772
- * @since 1.6.0
773
- * @change 1.7.0
774
  *
775
- * @param string $site_url site URL
776
- * @return array $site_objects site objects
777
  */
778
-
779
  public static function get_site_objects( $site_url ) {
780
 
781
  $site_objects = array();
 
782
 
783
- // get directory
784
- $dir = self::get_cache_file_dir( $site_url );
785
-
786
- // check if directory exists
787
  if ( ! is_dir( $dir ) ) {
788
  return $site_objects;
789
  }
790
 
791
- // get site objects
792
- $site_objects = self::get_dir_objects( $dir );
793
 
794
- // maybe filter subdirectory network site objects
795
  if ( is_multisite() && ! is_subdomain_install() ) {
796
  $blog_path = Cache_Enabler::get_blog_path();
797
  $blog_paths = Cache_Enabler::get_blog_paths();
798
 
799
- // check if main site in subdirectory network
800
  if ( ! in_array( $blog_path, $blog_paths, true ) ) {
801
  foreach ( $site_objects as $key => $site_object ) {
802
- // delete site object if it does not belong to main site
803
  if ( in_array( '/' . $site_object . '/', $blog_paths, true ) ) {
804
  unset( $site_objects[ $key ] );
805
  }
@@ -810,16 +1151,13 @@ final class Cache_Enabler_Disk {
810
  return $site_objects;
811
  }
812
 
813
-
814
  /**
815
- * get current time
816
  *
817
- * @since 1.7.0
818
- * @change 1.7.0
819
  *
820
- * @return string $current_time current time in HTTP-date format
821
  */
822
-
823
  private static function get_current_time() {
824
 
825
  $current_time = current_time( 'D, d M Y H:i:s', true ) . ' GMT';
@@ -827,52 +1165,54 @@ final class Cache_Enabler_Disk {
827
  return $current_time;
828
  }
829
 
830
-
831
  /**
832
- * get image path
 
 
833
  *
834
  * @since 1.4.8
835
- * @change 1.7.0
836
  *
837
- * @param string $image_url full or relative URL with or without intrinsic width or density descriptor
838
- * @return string $image_path file path to image
839
  */
840
-
841
  private static function get_image_path( $image_url ) {
842
 
843
- // in case image has intrinsic width or density descriptor
844
- $image_parts = explode( ' ', $image_url );
845
- $image_url = $image_parts[0];
846
 
847
- // in case installation is in a subdirectory
848
- $image_url_path = ltrim( parse_url( $image_url, PHP_URL_PATH ), '/' );
849
  $installation_dir = ltrim( parse_url( site_url( '/' ), PHP_URL_PATH ), '/' );
850
- $image_path = str_replace( $installation_dir, '', ABSPATH ) . $image_url_path;
851
 
852
  return $image_path;
853
  }
854
 
855
-
856
  /**
857
- * get current WP Filesystem instance
 
 
 
858
  *
859
  * @since 1.7.0
860
- * @change 1.7.0
 
 
 
 
861
  *
862
- * @throws \RuntimeException if filesystem could not be initialized
863
- * @return WP_Filesystem_Base $wp_filesystem filesystem instance
864
  */
865
-
866
  public static function get_filesystem() {
867
 
868
  global $wp_filesystem;
869
 
870
- // check if we already have a filesystem instance
871
  if ( $wp_filesystem instanceof WP_Filesystem_Base ) {
872
  return $wp_filesystem;
873
  }
874
 
875
- // try initializing filesystem instance and cache the result
876
  try {
877
  require_once ABSPATH . 'wp-admin/includes/file.php';
878
 
@@ -886,7 +1226,7 @@ final class Cache_Enabler_Disk {
886
  if ( is_wp_error( $wp_filesystem->errors ) && $wp_filesystem->errors->has_errors() ) {
887
  throw new \RuntimeException(
888
  $wp_filesystem->errors->get_error_message(),
889
- ( is_numeric( $wp_filesystem->errors->get_error_code() ) ) ? (int) $wp_filesystem->errors->get_error_code() : 0
890
  );
891
  }
892
 
@@ -907,40 +1247,46 @@ final class Cache_Enabler_Disk {
907
  return $wp_filesystem;
908
  }
909
 
910
-
911
  /**
912
- * makes directory recursively based on directory path
 
 
 
913
  *
914
  * @since 1.7.0
915
  * @change 1.7.2
916
  *
917
- * @param string $dir directory path to create
918
- * @return boolean true if the directory either already exists or was created and has the correct permissions, false otherwise
 
919
  */
920
-
921
  private static function mkdir_p( $dir ) {
922
 
923
- $fs = self::get_filesystem();
 
 
 
 
 
 
 
924
  $mode_octal = apply_filters( 'cache_enabler_mkdir_mode', 0755 );
925
- $mode_string = decoct( $mode_octal ); // get last three digits (e.g. '755')
926
  $parent_dir = dirname( $dir );
 
927
 
928
- // check if directory and its parent have correct permissions
929
  if ( $fs->is_dir( $dir ) && $fs->getchmod( $dir ) === $mode_string && $fs->getchmod( $parent_dir ) === $mode_string ) {
930
  return true;
931
  }
932
 
933
- // create any directories that do not exist yet
934
  if ( ! wp_mkdir_p( $dir ) ) {
935
  return false;
936
  }
937
 
938
- // check parent directory permissions
939
  if ( $fs->getchmod( $parent_dir ) !== $mode_string ) {
940
  return $fs->chmod( $parent_dir, $mode_octal, true );
941
  }
942
 
943
- // check directory permissions
944
  if ( $fs->getchmod( $dir ) !== $mode_string ) {
945
  return $fs->chmod( $dir, $mode_octal );
946
  }
@@ -948,46 +1294,76 @@ final class Cache_Enabler_Disk {
948
  return true;
949
  }
950
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
951
 
952
  /**
953
- * set or unset WP_CACHE constant in wp-config.php
954
  *
955
- * @since 1.1.1
956
  * @change 1.7.0
957
  *
958
- * @param boolean $set true to set WP_CACHE constant, false to unset
959
  */
960
-
961
  private static function set_wp_cache_constant( $set = true ) {
962
 
963
- // get config file
964
  if ( file_exists( ABSPATH . 'wp-config.php' ) ) {
965
- // config file resides in ABSPATH
966
  $wp_config_file = ABSPATH . 'wp-config.php';
967
  } elseif ( @file_exists( dirname( ABSPATH ) . '/wp-config.php' ) && ! @file_exists( dirname( ABSPATH ) . '/wp-settings.php' ) ) {
968
- // config file resides one level above ABSPATH but is not part of another installation
969
  $wp_config_file = dirname( ABSPATH ) . '/wp-config.php';
970
  } else {
971
  $wp_config_file = false;
972
  }
973
 
974
- // check if config file can be written to
975
  if ( ! $wp_config_file || ! is_writable( $wp_config_file ) ) {
976
  return;
977
  }
978
 
979
- // get config file contents
980
  $wp_config_file_contents = file_get_contents( $wp_config_file );
981
 
982
- // validate config file
983
  if ( ! is_string( $wp_config_file_contents ) ) {
984
  return;
985
  }
986
 
987
- // search for WP_CACHE constant
988
  $found_wp_cache_constant = preg_match( '/define\s*\(\s*[\'\"]WP_CACHE[\'\"]\s*,.+\);/', $wp_config_file_contents );
989
 
990
- // if not found set WP_CACHE constant when config file is default (must be before WordPress sets up)
991
  if ( $set && ! $found_wp_cache_constant ) {
992
  $ce_wp_config_lines = '/** Enables page caching for Cache Enabler. */' . PHP_EOL;
993
  $ce_wp_config_lines .= "if ( ! defined( 'WP_CACHE' ) ) {" . PHP_EOL;
@@ -997,140 +1373,174 @@ final class Cache_Enabler_Disk {
997
  $wp_config_file_contents = preg_replace( '/(\/\*\* Sets up WordPress vars and included files\. \*\/)/', $ce_wp_config_lines . '$1', $wp_config_file_contents );
998
  }
999
 
1000
- // unset WP_CACHE constant if set by Cache Enabler
1001
  if ( ! $set ) {
1002
  $wp_config_file_contents = preg_replace( '/.+Added by Cache Enabler\r\n/', '', $wp_config_file_contents ); // < 1.5.0
1003
  $wp_config_file_contents = preg_replace( '/\/\*\* Enables page caching for Cache Enabler\. \*\/' . PHP_EOL . '.+' . PHP_EOL . '.+' . PHP_EOL . '\}' . PHP_EOL . PHP_EOL . '/', '', $wp_config_file_contents );
1004
  }
1005
 
1006
- // update config file
1007
  file_put_contents( $wp_config_file, $wp_config_file_contents, LOCK_EX );
1008
  }
1009
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1010
 
1011
  /**
1012
- * convert page contents
 
 
1013
  *
1014
  * @since 1.7.0
1015
- * @change 1.7.0
1016
  *
1017
- * @param string $page_contents contents of a page from the output buffer
1018
- * @return string $converted_page_contents converted contents of a page from the output buffer
1019
  */
1020
-
1021
  private static function converter( $page_contents ) {
1022
 
1023
- // attributes to convert during WebP conversion hook
1024
- $attributes = (array) apply_filters( 'cache_enabler_convert_webp_attributes', array( 'src', 'srcset', 'data-[^=]+' ) );
1025
-
1026
- // stringify
 
 
 
 
 
1027
  $attributes_regex = implode( '|', $attributes );
1028
 
1029
- // magic regex rule
1030
- $image_urls_regex = '#(?:(?:(' . $attributes_regex . ')\s*=|(url)\()\s*[\'\"]?\s*)\K(?:[^\?\"\'\s>]+)(?:\.jpe?g|\.png)(?:\s\d+[wx][^\"\'>]*)?(?=\/?[\"\'\s\)>])(?=[^<{]*(?:\)[^<{]*\}|>))#i';
1031
-
1032
- // ignore query strings during WebP conversion hook
1033
- if ( ! apply_filters( 'cache_enabler_convert_webp_ignore_query_strings', true ) ) {
 
 
 
 
 
 
1034
  $image_urls_regex = '#(?:(?:(' . $attributes_regex . ')\s*=|(url)\()\s*[\'\"]?\s*)\K(?:[^\"\'\s>]+)(?:\.jpe?g|\.png)(?:\s\d+[wx][^\"\'>]*)?(?=\/?[\?\"\'\s\)>])(?=[^<{]*(?:\)[^<{]*\}|>))#i';
1035
  }
1036
 
1037
- // page contents after WebP conversion hook
 
 
 
 
 
 
1038
  $converted_page_contents = apply_filters( 'cache_enabler_page_contents_after_webp_conversion', preg_replace_callback( $image_urls_regex, 'self::convert_webp', $page_contents ) );
1039
-
1040
- // deprecated page contents after WebP conversion hook
1041
  $converted_page_contents = apply_filters_deprecated( 'cache_enabler_disk_webp_converted_data', array( $converted_page_contents ), '1.6.0', 'cache_enabler_page_contents_after_webp_conversion' );
1042
 
1043
  return $converted_page_contents;
1044
  }
1045
 
1046
-
1047
  /**
1048
- * convert image URL(s) to WebP
1049
  *
1050
- * @since 1.0.1
1051
- * @change 1.5.0
1052
  *
1053
- * @param array $matches pattern matches from parsed page contents
1054
- * @return string $conversion converted image URL(s) to WebP if applicable, default URL(s) otherwise
1055
  */
1056
-
1057
  private static function convert_webp( $matches ) {
1058
 
1059
- $full_match = $matches[0];
1060
  $image_extension_regex = '/(\.jpe?g|\.png)/i';
1061
- $image_found = preg_match( $image_extension_regex, $full_match );
 
 
 
 
1062
 
1063
- if ( $image_found ) {
1064
- // set image URL(s)
1065
- $image_urls = explode( ',', $full_match );
1066
 
1067
- foreach ( $image_urls as &$image_url ) {
1068
- $image_url = trim( $image_url, ' ' );
1069
- $image_url_webp = preg_replace( $image_extension_regex, '$1.webp', $image_url ); // append .webp extension
 
 
 
 
 
 
1070
  $image_path_webp = self::get_image_path( $image_url_webp );
1071
 
1072
- // check if WebP image exists
1073
  if ( is_file( $image_path_webp ) ) {
1074
  $image_url = $image_url_webp;
1075
- } else {
1076
- $image_url_webp = preg_replace( $image_extension_regex, '', $image_url_webp ); // remove default extension
1077
- $image_path_webp = self::get_image_path( $image_url_webp );
1078
-
1079
- // check if WebP image exists
1080
- if ( is_file( $image_path_webp ) ) {
1081
- $image_url = $image_url_webp;
1082
- }
1083
  }
1084
  }
 
1085
 
1086
- $conversion = implode( ', ', $image_urls );
1087
 
1088
- return $conversion;
1089
- }
1090
  }
1091
 
1092
-
1093
  /**
1094
- * minify HTML
1095
  *
1096
- * @since 1.0.0
 
 
 
1097
  * @change 1.7.0
1098
  *
1099
- * @param string $page_contents contents of a page from the output buffer
1100
- * @return string $page_contents|$minified_html minified page contents if applicable, unchanged otherwise
1101
  */
 
1102
 
1103
- private static function minify_html( $page_contents ) {
1104
-
1105
- // HTML character limit
1106
- if ( strlen( $page_contents ) > 700000 ) {
1107
- return $page_contents;
1108
  }
1109
 
1110
- // HTML tags to ignore hook
 
 
 
 
 
 
1111
  $ignore_tags = (array) apply_filters( 'cache_enabler_minify_html_ignore_tags', array( 'textarea', 'pre', 'code' ) );
1112
-
1113
- // deprecated HTML tags to ignore hook
1114
  $ignore_tags = (array) apply_filters_deprecated( 'cache_minify_ignore_tags', array( $ignore_tags ), '1.6.0', 'cache_enabler_minify_html_ignore_tags' );
1115
 
1116
- // if setting selected exclude inline CSS and JavaScript
1117
  if ( ! Cache_Enabler_Engine::$settings['minify_inline_css_js'] ) {
1118
  array_push( $ignore_tags, 'style', 'script' );
1119
  }
1120
 
1121
- // check if there are ignore tags
1122
  if ( ! $ignore_tags ) {
1123
- return $page_contents;
1124
  }
1125
 
1126
- // stringify
1127
  $ignore_tags_regex = implode( '|', $ignore_tags );
1128
 
1129
- // remove HTML comments
1130
- $minified_html = preg_replace( '#<!--[^\[><].*?-->#s', '', $page_contents );
1131
 
1132
- // if setting selected remove CSS and JavaScript comments
1133
  if ( Cache_Enabler_Engine::$settings['minify_inline_css_js'] ) {
 
1134
  $minified_html = preg_replace(
1135
  '#/\*(?!!)[\s\S]*?\*/|(?:^[ \t]*)//.*$|((?<!\()[ \t>;,{}[\]])//[^;\n]*$#m',
1136
  '$1',
@@ -1138,98 +1548,105 @@ final class Cache_Enabler_Disk {
1138
  );
1139
  }
1140
 
1141
- // minify HTML
1142
  $minified_html = preg_replace(
1143
  '#(?>[^\S ]\s*|\s{2,})(?=[^<]*+(?:<(?!/?(?:' . $ignore_tags_regex . ')\b)[^<]*+)*+(?:<(?>' . $ignore_tags_regex . ')\b|\z))#ix',
1144
  ' ',
1145
  $minified_html
1146
  );
1147
 
1148
- // something went wrong
1149
  if ( strlen( $minified_html ) <= 1 ) {
1150
- return $page_contents;
1151
  }
1152
 
1153
  return $minified_html;
1154
  }
1155
 
1156
-
1157
  /**
1158
- * delete empty parent directory
 
 
 
1159
  *
1160
- * @since 1.6.0
1161
- * @change 1.6.0
 
1162
  *
1163
- * @param string $dir directory path
 
1164
  */
 
1165
 
1166
- private static function delete_parent_dir( $dir ) {
1167
-
1168
- $parent_dir = dirname( $dir );
1169
- $parent_dir_objects = self::get_dir_objects( $parent_dir );
1170
-
1171
- if ( empty( $parent_dir_objects ) ) {
1172
- // delete empty parent directory
1173
- @rmdir( $parent_dir );
1174
-
1175
- // add deleted parent directory to directories cleared list
1176
- self::$dir_cleared[ $parent_dir ] = $parent_dir_objects;
1177
 
1178
- // delete parent directory if empty
1179
- self::delete_parent_dir( $parent_dir );
1180
  }
1181
  }
1182
 
1183
-
1184
  /**
1185
- * delete settings file
1186
  *
1187
- * @since 1.5.0
1188
- * @change 1.7.0
1189
  */
 
1190
 
1191
- private static function delete_settings_file() {
1192
-
1193
- // get settings file
1194
- $settings_file = self::get_settings_file();
1195
-
1196
- // delete settings file
1197
- @unlink( $settings_file );
1198
-
1199
- // delete settings directory if empty
1200
- @rmdir( self::$settings_dir );
1201
-
1202
- // delete parent directory of settings directory if empty
1203
- @rmdir( dirname( self::$settings_dir ) );
1204
  }
1205
 
1206
-
1207
  /**
1208
- * delete asset (deprecated)
1209
  *
1210
- * @since 1.0.0
1211
- * @deprecated 1.5.0
 
 
1212
  */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1213
 
1214
- public static function delete_asset( $url ) {
 
1215
 
1216
- if ( empty( $url ) ) {
1217
- wp_die( 'URL is empty.' );
1218
- }
 
1219
 
1220
- self::clear_dir( self::get_cache_file_dir( $url ) );
1221
- }
1222
 
 
 
 
 
 
 
1223
 
1224
- /**
1225
- * get cache size
1226
- *
1227
- * @since 1.0.0
1228
- * @deprecated 1.7.0
1229
- */
1230
 
1231
- public static function cache_size( $dir = null ) {
 
 
 
 
1232
 
1233
- return self::get_cache_size( $dir );
1234
  }
1235
  }
1
  <?php
2
  /**
3
+ * Class used for handling disk-related operations.
4
  *
5
  * @since 1.0.0
6
  */
10
  }
11
 
12
  final class Cache_Enabler_Disk {
 
13
  /**
14
+ * Plugin cache directory (deprecated).
 
 
 
15
  *
16
+ * @since 1.5.0
17
+ * @deprecated 1.8.0
18
  */
 
19
  public static $cache_dir = WP_CONTENT_DIR . '/cache/cache-enabler';
20
 
 
21
  /**
22
+ * File path to the cached page for the current request.
23
  *
24
+ * @since 1.8.0
 
25
  *
26
+ * @var string
27
  */
28
+ private static $cache_file;
 
 
29
 
30
  /**
31
+ * Add and configure files required by plugin.
 
 
 
 
 
 
 
 
 
 
 
 
32
  *
33
  * @since 1.5.0
34
+ * @change 1.8.0
35
  */
 
36
  public static function setup() {
37
 
38
+ self::create_advanced_cache_file();
 
 
 
39
  self::set_wp_cache_constant();
40
  }
41
 
 
42
  /**
43
+ * Delete and unconfigure files required by plugin.
44
  *
45
  * @since 1.5.0
46
+ * @change 1.8.0
47
  */
 
48
  public static function clean() {
49
 
 
50
  self::delete_settings_file();
51
 
52
+ if ( ! is_dir( CACHE_ENABLER_SETTINGS_DIR ) ) {
53
+ array_map( 'unlink', glob( WP_CONTENT_DIR . '/cache/cache-enabler-advcache-*.json' ) ); // < 1.4.0
54
+ array_map( 'unlink', glob( ABSPATH . 'CE_SETTINGS_PATH-*.json' ) ); // = 1.4.0
 
 
 
 
55
  @unlink( WP_CONTENT_DIR . '/advanced-cache.php' );
 
56
  self::set_wp_cache_constant( false );
57
  }
58
  }
59
 
 
60
  /**
61
+ * Create a static HTML file from the page contents received from the cache engine.
62
  *
63
+ * @since 1.5.0
64
  * @change 1.7.0
65
  *
66
+ * @param string $page_contents Page contents from the cache engine as raw HTML.
67
  */
 
68
  public static function cache_page( $page_contents ) {
69
 
70
+ /**
71
+ * Filters the page contents before a static HTML file is created.
72
+ *
73
+ * @since 1.6.0
74
+ *
75
+ * @param string $page_contents Page contents from the cache engine as raw HTML.
76
+ */
77
  $page_contents = apply_filters( 'cache_enabler_page_contents_before_store', $page_contents );
 
 
78
  $page_contents = apply_filters_deprecated( 'cache_enabler_before_store', array( $page_contents ), '1.6.0', 'cache_enabler_page_contents_before_store' );
79
 
 
80
  self::create_cache_file( $page_contents );
81
  }
82
 
 
83
  /**
84
+ * Whether a cached page exists.
85
  *
86
+ * @since 1.5.0
87
  * @change 1.7.0
88
  *
89
+ * @param string $cache_file File path to a cached page.
90
+ * @return bool True if the cached page exists and is readable, false otherwise.
91
  */
 
92
  public static function cache_exists( $cache_file ) {
93
 
94
  return is_readable( $cache_file );
95
  }
96
 
 
97
  /**
98
+ * Whether an existing cached page is expired.
99
  *
100
+ * @since 1.5.0
101
+ * @change 1.8.0
102
  *
103
+ * @param string $cache_file File path to an existing cached page.
104
+ * @return bool True if the cached page is expired, false otherwise.
105
  */
 
106
  public static function cache_expired( $cache_file ) {
107
 
 
108
  if ( ! Cache_Enabler_Engine::$settings['cache_expires'] || Cache_Enabler_Engine::$settings['cache_expiry_time'] === 0 ) {
109
  return false;
110
  }
111
 
112
+ $expires_seconds = 3600 * Cache_Enabler_Engine::$settings['cache_expiry_time'];
 
113
 
114
+ if ( ( filemtime( $cache_file ) + $expires_seconds ) <= time() ) {
 
115
  return true;
116
  }
117
 
118
  return false;
119
  }
120
 
 
121
  /**
122
+ * Iterate over cache objects to perform actions and/or gather data.
123
+ *
124
+ * The $args parameter either takes an associative array of arguments or a
125
+ * template string. The templates 'pagination' and 'subpages' are mainly for
126
+ * backward compatibility but are also helpful shortcuts.
127
+ *
128
+ * Array of arguments for iterating over cache objects:
129
+ *
130
+ * @type int $clear Whether to clear the cache files iterated over.
131
+ * Default 0.
132
+ * @type int $expired Whether to only iterate over expired cache files.
133
+ * Default 0.
134
+ * @type int|string[]|array[] $hooks The cache hooks to fire.
135
+ * Default 0.
136
+ * @type int|string[]|array[] $keys The cache file versions to iterate over.
137
+ * Default 0.
138
+ * @type string $root The root path all cache files iterated over must have.
139
+ * Default ''.
140
+ * @type int|string[]|array[] $subpages The subpages to iterate over.
141
+ *
142
+ * Until this can be improved, see PR #237 for more information.
143
+ *
144
+ * @since 1.8.0
145
+ * @access private
146
+ *
147
+ * @param string $url URL to a cached page (with or without scheme, wildcard path, and query string).
148
+ * @param array|string $args See description.
149
+ * @return array Cache data.
150
  */
151
+ public static function cache_iterator( $url, $args = array() ) {
152
 
153
+ $cache = array(
154
+ 'index' => array(),
155
+ 'size' => 0,
156
+ );
157
 
158
+ if ( ! is_string( $url ) || empty( $url ) ) {
159
+ return $cache;
 
160
  }
161
 
162
+ $url = esc_url_raw( $url, array( 'http', 'https' ) );
163
+ $cache_dir = self::get_cache_dir( $url );
164
 
165
+ if ( ! is_dir( $cache_dir ) ) {
166
+ return $cache;
 
167
  }
168
 
169
+ $switched = false;
170
+ if ( is_multisite() && ! ms_is_switched() ) {
171
+ $blog_domain = (string) parse_url( $url, PHP_URL_HOST );
172
+ $blog_path = is_subdomain_install() ? '/' : Cache_Enabler::get_blog_path_from_url( $url );
173
+ $blog_id = get_blog_id_from_url( $blog_domain, $blog_path );
174
+
175
+ if ( $blog_id !== 0 ) {
176
+ $switched = Cache_Enabler::switch_to_blog( $blog_id, true );
 
 
 
 
 
 
177
  }
178
  }
179
 
180
+ $args = self::get_cache_iterator_args( $url, $args );
181
+ $recursive = ( $args['subpages'] === 1 || ! empty( $args['subpages']['include'] ) || isset( $args['subpages']['exclude'] ) );
182
+ $filter = ( $recursive && $args['subpages'] !== 1 ) ? $args['subpages'] : null;
183
+ $cache_objects = self::get_dir_objects( $cache_dir, $recursive, $filter );
184
+ $cache_keys_regex = self::get_cache_keys_regex( $args['keys'] );
185
+
186
+ foreach ( $cache_objects as $cache_object ) {
187
+ if ( is_file( $cache_object ) ) {
188
+ if ( $args['root'] && strpos( $cache_object, $args['root'] ) !== 0 ) {
189
+ // Skip to the next object because the file does not start with the provided root path.
190
+ continue;
191
+ }
192
+
193
+ $cache_object_name = basename( $cache_object );
194
 
195
+ if ( $cache_keys_regex && ! preg_match( $cache_keys_regex, $cache_object_name ) ) {
196
+ // Skip to the next object because the file name does not match the provided cache keys.
197
+ continue;
198
+ }
199
+
200
+ if ( $args['expired'] && ! self::cache_expired( $cache_object ) ) {
201
+ // Skip to the next object because the file is not expired.
202
+ continue;
203
+ }
 
 
 
 
 
 
 
 
204
 
205
+ $cache_object_dir = dirname( $cache_object );
206
+ $cache_object_size = (int) @filesize( $cache_object );
207
+
208
+ if ( $args['clear'] ) {
209
+ if ( ! @unlink( $cache_object ) ) {
210
+ // Skip to the next object because the file deletion failed.
211
+ continue;
212
  }
213
+
214
+ // The cache size is negative when cleared.
215
+ $cache_object_size = -$cache_object_size;
216
+
217
+ // Remove the containing directory if empty along with any of its empty parents.
218
+ self::rmdir( $cache_object_dir, true );
219
+ }
220
+
221
+ if ( strpos( $cache_object_name, 'index' ) === false ) {
222
+ // Skip to the next object because the file is not a cache version and no longer
223
+ // needs to be handled, such as a hidden file.
224
+ continue;
225
+ }
226
+
227
+ if ( ! isset( $cache['index'][ $cache_object_dir ]['url'] ) ) {
228
+ $cache['index'][ $cache_object_dir ]['url'] = self::get_cache_url( $cache_object_dir );
229
+ $cache['index'][ $cache_object_dir ]['id'] = url_to_postid( $cache['index'][ $cache_object_dir ]['url'] );
230
  }
231
 
232
+ $cache['index'][ $cache_object_dir ]['versions'][ $cache_object_name ] = $cache_object_size;
233
+ $cache['size'] += $cache_object_size;
234
  }
235
  }
 
236
 
237
+ // Sort the cache index by forward slashes from the lowest to highest.
238
+ uksort( $cache['index'], 'self::sort_dir_objects' );
239
+
240
+ if ( $args['clear'] ) {
241
+ self::fire_cache_cleared_hooks( $cache['index'], $args['hooks'] );
242
+ }
243
+
244
+ if ( $switched ) {
245
+ Cache_Enabler::restore_current_blog( true );
246
+ }
247
+
248
+ return $cache;
249
+ }
250
 
251
  /**
252
+ * Get the cache size (deprecated).
 
 
 
253
  *
254
+ * @since 1.0.0
255
+ * @deprecated 1.7.0
256
  */
257
+ public static function cache_size( $dir = null ) {
258
 
259
+ return self::get_cache_size( $dir );
260
+ }
261
 
262
+ /**
263
+ * Clear the cache (deprecated).
264
+ *
265
+ * @since 1.0.0
266
+ * @deprecated 1.8.0
267
+ */
268
+ public static function clear_cache( $clear_url = null, $clear_type = 'page' ) {
269
 
270
+ Cache_Enabler::clear_page_cache_by_url( $clear_url, $clear_type );
271
+ }
 
 
272
 
273
+ /**
274
+ * Create the advanced-cache.php drop-in file.
275
+ *
276
+ * @since 1.8.0
277
+ *
278
+ * @return string|bool Path to the created file, false on failure.
279
+ */
280
+ public static function create_advanced_cache_file() {
281
 
282
+ $advanced_cache_sample_file = CACHE_ENABLER_DIR . '/advanced-cache.php';
 
 
283
 
284
+ if ( ! is_readable( $advanced_cache_sample_file ) ) {
285
+ return false;
 
 
 
 
286
  }
287
 
288
+ $advanced_cache_file = WP_CONTENT_DIR . '/advanced-cache.php';
289
+ $advanced_cache_file_contents = file_get_contents( $advanced_cache_sample_file );
290
+ $advanced_cache_file_contents = str_replace( '/your/path/to/wp-content/plugins/cache-enabler', CACHE_ENABLER_DIR, $advanced_cache_file_contents );
291
 
292
+ if ( ! self::mkdir_p( dirname( $advanced_cache_file ) ) ) {
293
+ return false;
294
+ }
295
 
296
+ $advanced_cache_file_created = file_put_contents( $advanced_cache_file, $advanced_cache_file_contents, LOCK_EX );
 
 
297
 
298
+ return ( $advanced_cache_file_created === false ) ? false : $advanced_cache_file;
299
+ }
300
 
301
  /**
302
+ * Create a static HTML file.
303
  *
304
+ * @since 1.5.0
305
+ * @change 1.8.0
306
  *
307
+ * @param string $page_contents Page contents from the cache engine as raw HTML.
308
  */
 
309
  private static function create_cache_file( $page_contents ) {
310
 
 
311
  if ( ! is_string( $page_contents ) || strlen( $page_contents ) === 0 ) {
312
  return;
313
  }
314
 
 
315
  $new_cache_file = self::get_cache_file();
316
  $new_cache_file_dir = dirname( $new_cache_file );
317
  $new_cache_file_name = basename( $new_cache_file );
318
 
 
319
  if ( Cache_Enabler_Engine::$settings['minify_html'] ) {
320
  $page_contents = self::minify_html( $page_contents );
321
  }
322
 
 
323
  $page_contents = $page_contents . self::get_cache_signature( $new_cache_file_name );
324
 
 
325
  if ( strpos( $new_cache_file_name, 'webp' ) !== false ) {
326
  $page_contents = self::converter( $page_contents );
327
  }
328
 
329
+ switch ( substr( $new_cache_file_name, -2, 2 ) ) {
330
+ case 'br':
331
+ $page_contents = brotli_compress( $page_contents );
332
+ break;
333
+ case 'gz':
334
+ $page_contents = gzencode( $page_contents, 9 );
335
+ break;
336
+ }
337
 
338
+ if ( $page_contents === false ) {
339
+ return; // Compression failed.
 
 
340
  }
341
 
 
342
  if ( ! self::mkdir_p( $new_cache_file_dir ) ) {
343
  return;
344
  }
345
 
346
+ $new_cache_file_created = file_put_contents( $new_cache_file, $page_contents, LOCK_EX );
347
+
348
+ if ( $new_cache_file_created !== false ) {
349
+ clearstatcache();
350
+ $new_cache_file_stats = @stat( $new_cache_file_dir );
351
+ $new_cache_file_perms = $new_cache_file_stats['mode'] & 0007777;
352
+ $new_cache_file_perms = $new_cache_file_perms & 0000666;
353
+ @chmod( $new_cache_file, $new_cache_file_perms );
354
+ clearstatcache();
355
+
356
+ $page_created_url = self::get_cache_url( $new_cache_file_dir );
357
+ $page_created_id = url_to_postid( $page_created_url );
358
+ $cache_created_index[ $new_cache_file_dir ]['url'] = $page_created_url;
359
+ $cache_created_index[ $new_cache_file_dir ]['id'] = $page_created_id;
360
+ $cache_created_index[ $new_cache_file_dir ]['versions'][ $new_cache_file_name ] = $new_cache_file_created;
361
+
362
+ /**
363
+ * Fires after the page cache has been created.
364
+ *
365
+ * @since 1.8.0
366
+ *
367
+ * @param string $page_created_url Full URL of the page created.
368
+ * @param int $page_created_id Post ID of the page created.
369
+ * @param array[] $cache_created_index Index of the cache created.
370
+ */
371
+ do_action( 'cache_enabler_page_cache_created', $page_created_url, $page_created_id, $cache_created_index );
372
+ }
373
  }
374
 
 
375
  /**
376
+ * Create a settings file.
377
  *
378
+ * @since 1.5.0
379
+ * @change 1.8.0
380
  *
381
+ * @param array $settings Plugin settings from the database.
382
+ * @return string|bool Path to the created file, false on failure.
383
  */
 
384
  public static function create_settings_file( $settings ) {
385
 
 
386
  if ( ! is_array( $settings ) || ! function_exists( 'home_url' ) ) {
387
+ return false;
388
  }
389
 
 
390
  $new_settings_file = self::get_settings_file();
391
 
 
392
  $new_settings_file_contents = '<?php' . PHP_EOL;
393
  $new_settings_file_contents .= '/**' . PHP_EOL;
394
+ $new_settings_file_contents .= ' * The settings file for Cache Enabler.' . PHP_EOL;
395
+ $new_settings_file_contents .= ' *' . PHP_EOL;
396
+ $new_settings_file_contents .= ' * This file is automatically created, mirroring the plugin settings saved in the' . PHP_EOL;
397
+ $new_settings_file_contents .= ' * database. It is used to cache and deliver pages.' . PHP_EOL;
398
  $new_settings_file_contents .= ' *' . PHP_EOL;
399
+ $new_settings_file_contents .= ' * @site ' . home_url() . PHP_EOL;
400
+ $new_settings_file_contents .= ' * @time ' . self::get_current_time() . PHP_EOL;
401
  $new_settings_file_contents .= ' *' . PHP_EOL;
402
+ $new_settings_file_contents .= ' * @since 1.5.0' . PHP_EOL;
403
+ $new_settings_file_contents .= ' * @since 1.6.0 The `clear_site_cache_on_saved_post` setting was added.' . PHP_EOL;
404
+ $new_settings_file_contents .= ' * @since 1.6.0 The `clear_complete_cache_on_saved_post` setting was removed.' . PHP_EOL;
405
+ $new_settings_file_contents .= ' * @since 1.6.0 The `clear_site_cache_on_new_comment` setting was added.' . PHP_EOL;
406
+ $new_settings_file_contents .= ' * @since 1.6.0 The `clear_complete_cache_on_new_comment` setting was removed.' . PHP_EOL;
407
+ $new_settings_file_contents .= ' * @since 1.6.0 The `clear_site_cache_on_changed_plugin` setting was added.' . PHP_EOL;
408
+ $new_settings_file_contents .= ' * @since 1.6.0 The `clear_complete_cache_on_changed_plugin` setting was removed.' . PHP_EOL;
409
+ $new_settings_file_contents .= ' * @since 1.6.1 The `clear_site_cache_on_saved_comment` setting was added.' . PHP_EOL;
410
+ $new_settings_file_contents .= ' * @since 1.6.1 The `clear_site_cache_on_new_comment` setting was removed.' . PHP_EOL;
411
+ $new_settings_file_contents .= ' * @since 1.7.0 The `mobile_cache` setting was added.' . PHP_EOL;
412
+ $new_settings_file_contents .= ' * @since 1.8.0 The `use_trailing_slashes` setting was added.' . PHP_EOL;
413
+ $new_settings_file_contents .= ' * @since 1.8.0 The `permalink_structure` setting was deprecated.' . PHP_EOL;
414
  $new_settings_file_contents .= ' */' . PHP_EOL;
415
  $new_settings_file_contents .= PHP_EOL;
416
  $new_settings_file_contents .= 'return ' . var_export( $settings, true ) . ';';
417
 
 
418
  if ( ! self::mkdir_p( dirname( $new_settings_file ) ) ) {
419
+ return false;
420
  }
421
 
422
+ $new_settings_file_created = file_put_contents( $new_settings_file, $new_settings_file_contents, LOCK_EX );
 
423
 
424
+ return ( $new_settings_file_created === false ) ? false : $new_settings_file;
425
  }
426
 
 
427
  /**
428
+ * Fire the cache cleared hooks.
429
  *
430
+ * @since 1.8.0
 
431
  *
432
+ * @param array[] $cache_cleared_index Index of the cache cleared.
433
+ * @param array[] $hooks Cache cleared hooks to 'include' and/or 'exclude' from being fired.
434
  */
435
+ private static function fire_cache_cleared_hooks( $cache_cleared_index, $hooks ) {
436
 
437
+ if ( empty( $cache_cleared_index ) || empty( $hooks ) ) {
438
+ return;
439
+ }
440
 
441
+ if ( isset( $hooks['include'] ) ) {
442
+ $hooks_to_fire = $hooks['include'];
443
+ } else {
444
+ $hooks_to_fire = array( 'cache_enabler_complete_cache_cleared', 'cache_enabler_site_cache_cleared', 'cache_enabler_page_cache_cleared' );
445
+ }
446
 
447
+ if ( ! empty( $hooks['exclude'] ) ) {
448
+ $hooks_to_fire = array_diff( $hooks_to_fire, $hooks['exclude'] );
449
+ }
450
+
451
+ if ( empty( $hooks_to_fire ) ) {
452
+ return;
453
+ }
454
+
455
+ if ( in_array( 'cache_enabler_page_cache_cleared', $hooks_to_fire, true ) ) {
456
+ foreach ( $cache_cleared_index as $cache_cleared_dir => $cache_cleared_data ) {
457
+ $page_cleared_url = $cache_cleared_data['url'];
458
+ $page_cleared_id = $cache_cleared_data['id'];
459
+
460
+ /**
461
+ * Fires after the page cache has been cleared.
462
+ *
463
+ * @since 1.6.0
464
+ * @since 1.8.0 The `$cache_cleared_index` parameter was added.
465
+ *
466
+ * @param string $page_cleared_url Full URL of the page cleared.
467
+ * @param int $page_cleared_id Post ID of the page cleared.
468
+ * @param array[] $cache_cleared_index Index of the cache cleared.
469
+ */
470
+ do_action( 'cache_enabler_page_cache_cleared', $page_cleared_url, $page_cleared_id, $cache_cleared_index );
471
+ do_action( 'ce_action_cache_by_url_cleared', $page_cleared_url ); // Deprecated in 1.6.0.
472
+ }
473
+ }
474
+
475
+ if ( in_array( 'cache_enabler_site_cache_cleared', $hooks_to_fire, true ) && empty( Cache_Enabler::get_cache_index() ) ) {
476
+ $site_cleared_url = user_trailingslashit( home_url() );
477
+ $site_cleared_id = get_current_blog_id();
478
+
479
+ /**
480
+ * Fires after the site cache has been cleared.
481
+ *
482
+ * @since 1.6.0
483
+ * @since 1.8.0 The `$cache_cleared_index` parameter was added.
484
+ *
485
+ * @param string $site_cleared_url Full URL of the site cleared.
486
+ * @param int $site_cleared_id Post ID of the site cleared.
487
+ * @param array[] $cache_cleared_index Index of the cache cleared.
488
+ */
489
+ do_action( 'cache_enabler_site_cache_cleared', $site_cleared_url, $site_cleared_id, $cache_cleared_index );
490
+ }
491
+
492
+ if ( in_array( 'cache_enabler_complete_cache_cleared', $hooks_to_fire, true ) && ! is_dir( CACHE_ENABLER_CACHE_DIR ) ) {
493
+ /**
494
+ * Fires after the complete cache has been cleared.
495
+ *
496
+ * @since 1.6.0
497
+ */
498
+ do_action( 'cache_enabler_complete_cache_cleared' );
499
+ do_action( 'ce_action_cache_cleared' ); // Deprecated in 1.6.0.
500
+ }
501
  }
502
 
503
+ /**
504
+ * Filters whether a file or directory should be included or excluded.
505
+ *
506
+ * @since 1.8.0
507
+ *
508
+ * @param string $dir_object File or directory path to filter (without trailing slash).
509
+ * @param array[] $filter File or directory path(s) to 'include' and/or 'exclude' (without trailing slash).
510
+ * @return bool True if directory object should be included, false if excluded.
511
+ */
512
+ private static function filter_dir_object( $dir_object, $filter ) {
513
+
514
+ if ( isset( $filter['exclude'] ) ) {
515
+ $match = in_array( $dir_object, $filter['exclude'], true );
516
+
517
+ if ( $match ) {
518
+ return false;
519
+ }
520
+ }
521
+
522
+ if ( isset( $filter['include'] ) ) {
523
+ $match = in_array( $dir_object, $filter['include'], true );
524
+
525
+ if ( $match ) {
526
+ return true;
527
+ }
528
+ }
529
+
530
+ if ( ! isset( $match ) ) {
531
+ return true;
532
+ }
533
+
534
+ ksort( $filter ); // Sort the keys in alphabetical order to check for an exclusion first.
535
+
536
+ if ( is_dir( $dir_object ) ) {
537
+ $dir_object = $dir_object . '/'; // Append a trailing slash to prevent a false match.
538
+ }
539
+
540
+ foreach ( $filter as $filter_type => $filter_value ) {
541
+ if ( $filter_type !== 'include' && $filter_type !== 'exclude' ) {
542
+ continue;
543
+ }
544
+
545
+ foreach ( $filter_value as $filter_object ) {
546
+ // If a trailing asterisk exists remove it to allow a wildcard match.
547
+ if ( substr( $filter_object, -1, 1 ) === '*' ) {
548
+ $filter_object = substr( $filter_object, 0, -1 );
549
+ // Otherwise, maybe append a trailing slash to force a strict match.
550
+ } elseif ( is_dir( $dir_object ) ) {
551
+ $filter_object = $filter_object . '/';
552
+ }
553
+
554
+ if ( str_replace( $filter_object, '', $dir_object ) !== $dir_object ) {
555
+ switch ( $filter_type ) {
556
+ case 'include':
557
+ return true; // Past inclusion or present wildcard inclusion.
558
+ case 'exclude':
559
+ return false; // Present wildcard exclusion.
560
+ }
561
+ }
562
+
563
+ if ( strpos( $filter_object, $dir_object ) === 0 && $filter_type === 'include' ) {
564
+ return true; // Future strict or wildcard inclusion.
565
+ }
566
+ }
567
+ }
568
+
569
+ if ( isset( $filter['include'] ) ) {
570
+ return false; // Match not found.
571
+ }
572
+
573
+ return true;
574
+ }
575
 
576
  /**
577
+ * Get the cache directory path for the current URL or from a given URL.
578
  *
579
+ * This does not check whether the returned cache directory path exists. The
580
+ * untrailingslashit() function is not being used to remove the trailing slash
581
+ * because it is not available when the cache engine is started early.
582
  *
583
+ * @since 1.8.0
584
+ *
585
+ * @param string $url (Optional) Full URL to a cached page (with or without wildcard path). Default
586
+ * is the current URL.
587
+ * @return string Cache directory path (without trailing slash), empty string if the URL is invalid.
588
  */
589
+ private static function get_cache_dir( $url = null ) {
590
 
591
+ if ( empty ( $url ) ) {
592
+ $url = 'http://' . Cache_Enabler_Engine::$request_headers['Host'] . $_SERVER['REQUEST_URI'];
593
+ }
594
 
595
+ $url_host = parse_url( $url, PHP_URL_HOST );
596
+ if ( ! is_string( $url_host ) ) {
597
+ return '';
598
+ }
599
 
600
+ $url_path = parse_url( $url, PHP_URL_PATH );
601
+ if ( ! is_string( $url_path ) ) {
602
+ $url_path = '';
603
+ } elseif ( substr( $url_path, -1, 1 ) === '*' ) {
604
+ $url_path = dirname( $url_path );
605
  }
606
 
607
+ $cache_dir = sprintf(
608
  '%s/%s%s',
609
+ CACHE_ENABLER_CACHE_DIR,
610
+ strtolower( $url_host ),
611
+ $url_path
612
  );
613
 
614
+ $cache_dir = rtrim( $cache_dir, '/\\' );
 
615
 
616
+ return $cache_dir;
617
  }
618
 
619
+ /**
620
+ * Get the cache iterator arguments.
621
+ *
622
+ * @since 1.8.0
623
+ *
624
+ * @global WP_Rewrite $wp_rewrite WordPress rewrite component.
625
+ *
626
+ * @param string $url (Optional) Full URL to a cached page (with or without wildcard path and query
627
+ * string). Default null.
628
+ * @param array|string $args (Optional) Cache iterator arguments or an arguments template. Default empty array.
629
+ * @return array Cache iterator arguments.
630
+ */
631
+ private static function get_cache_iterator_args( $url = null, $args = array() ) {
632
+
633
+ $default_args = array(
634
+ 'clear' => 0,
635
+ 'expired' => 0,
636
+ 'hooks' => 0,
637
+ 'keys' => 0,
638
+ 'root' => '',
639
+ 'subpages' => 0,
640
+ );
641
+
642
+ if ( ! is_array( $args ) ) {
643
+ $args_template = $args;
644
+ $args = array(
645
+ 'clear' => 1,
646
+ 'hooks' => array( 'include' => 'cache_enabler_page_cache_cleared' ),
647
+ );
648
+
649
+ switch ( $args_template ) {
650
+ case 'pagination':
651
+ global $wp_rewrite;
652
+ $included_subpages[] = isset( $wp_rewrite->pagination_base ) ? $wp_rewrite->pagination_base : '';
653
+ $included_subpages[] = isset( $wp_rewrite->comments_pagination_base ) ? $wp_rewrite->comments_pagination_base . '-*' : '';
654
+ $args['subpages']['include'] = $included_subpages;
655
+ break;
656
+ case 'subpages':
657
+ $args['subpages'] = 1;
658
+ break;
659
+ default:
660
+ $args = array();
661
+ }
662
+ }
663
+
664
+ $url_path = (string) parse_url( $url, PHP_URL_PATH );
665
+ if ( substr( $url_path, -1, 1 ) === '*' ) {
666
+ $args['root'] = CACHE_ENABLER_CACHE_DIR . '/' . substr( (string) parse_url( $url, PHP_URL_HOST ) . $url_path, 0, -1 );
667
+ $args['subpages']['include'] = basename( $url_path );
668
+ }
669
+
670
+ // Merge query string arguments into the parameter arguments and then the default arguments.
671
+ wp_parse_str( (string) parse_url( $url, PHP_URL_QUERY ), $query_string_args );
672
+ $args = wp_parse_args( $query_string_args, $args );
673
+ $args = wp_parse_args( $args, $default_args );
674
+ $args = self::validate_cache_iterator_args( $args );
675
+
676
+ return $args;
677
+ }
678
 
679
  /**
680
+ * Get the path to the cache file for the current request.
681
+ *
682
+ * This does not check whether the returned cache file exists. It sets the
683
+ * $cache_file property to prevent different paths being returned on the same
684
+ * request. This can occur because the $_SERVER['REQUEST_URI'] superglobal can be
685
+ * updated, like by another plugin, between trying to deliver a cached page and
686
+ * then actually creating it.
687
  *
688
  * @since 1.7.0
689
+ * @change 1.8.0
690
  *
691
+ * @return string Path to the cache file.
692
  */
693
+ public static function get_cache_file() {
694
+
695
+ if ( ! empty( self::$cache_file ) ) {
696
+ return self::$cache_file;
697
+ }
698
+
699
+ self::$cache_file = sprintf(
700
+ '%s/%s',
701
+ self::get_cache_dir(),
702
+ self::get_cache_file_name()
703
+ );
704
+
705
+ return self::$cache_file;
706
+ }
707
 
708
+ /**
709
+ * Get the name of the cache file for the current request.
710
+ *
711
+ * @since 1.7.0
712
+ *
713
+ * @return string Name of the cache file.
714
+ */
715
  private static function get_cache_file_name() {
716
 
717
+ $cache_keys = self::get_cache_keys();
718
  $cache_file_name = $cache_keys['scheme'] . 'index' . $cache_keys['device'] . $cache_keys['webp'] . '.html' . $cache_keys['compression'];
719
 
720
  return $cache_file_name;
721
  }
722
 
 
723
  /**
724
+ * Get the cache keys from the request headers for the cache file name.
725
+ *
726
+ * This has some functionality copied from is_ssl() and wp_is_mobile().
727
  *
728
  * @since 1.7.0
729
+ * @change 1.8.0
730
  *
731
+ * @return string[] An array of cache keys with names as the keys and keys as the values.
732
  */
 
733
  private static function get_cache_keys() {
734
 
 
735
  $cache_keys = array(
736
  'scheme' => 'http-',
737
  'device' => '',
739
  'compression' => '',
740
  );
741
 
 
742
  if ( isset( $_SERVER['HTTPS'] ) && ( strtolower( $_SERVER['HTTPS'] ) === 'on' || $_SERVER['HTTPS'] == '1' ) ) {
743
  $cache_keys['scheme'] = 'https-';
744
  } elseif ( isset( $_SERVER['SERVER_PORT'] ) && $_SERVER['SERVER_PORT'] == '443' ) {
745
  $cache_keys['scheme'] = 'https-';
746
+ } elseif ( Cache_Enabler_Engine::$request_headers['X-Forwarded-Proto'] === 'https'
747
+ || Cache_Enabler_Engine::$request_headers['X-Forwarded-Scheme'] === 'https'
748
+ ) {
749
  $cache_keys['scheme'] = 'https-';
750
  }
751
 
 
752
  if ( Cache_Enabler_Engine::$settings['mobile_cache'] ) {
753
  if ( strpos( Cache_Enabler_Engine::$request_headers['User-Agent'], 'Mobile' ) !== false
754
  || strpos( Cache_Enabler_Engine::$request_headers['User-Agent'], 'Android' ) !== false
762
  }
763
  }
764
 
 
765
  if ( Cache_Enabler_Engine::$settings['convert_image_urls_to_webp'] ) {
766
  if ( strpos( Cache_Enabler_Engine::$request_headers['Accept'], 'image/webp' ) !== false ) {
767
  $cache_keys['webp'] = '-webp';
768
  }
769
  }
770
 
 
771
  if ( Cache_Enabler_Engine::$settings['compress_cache'] ) {
772
+ if ( function_exists( 'brotli_compress' )
773
+ && $cache_keys['scheme'] === 'https-'
774
+ && strpos( Cache_Enabler_Engine::$request_headers['Accept-Encoding'], 'br' ) !== false
775
+ ) {
776
+ $cache_keys['compression'] = '.br';
777
+ } elseif ( strpos( Cache_Enabler_Engine::$request_headers['Accept-Encoding'], 'gzip' ) !== false ) {
778
  $cache_keys['compression'] = '.gz';
779
  }
780
  }
782
  return $cache_keys;
783
  }
784
 
 
785
  /**
786
+ * Get the cache keys regex for the cache iterator.
787
  *
788
+ * This uses positive and negative lookaheads to create a regex that will be used
789
+ * to check the name of the cache file in the cache iterator, for example:
790
+ * * #^(?=.*https)(?=.*webp).+$#
791
+ * * #^(?=.*https)(?!.*webp).+$#
792
+ * * #^.+$#
793
+ *
794
+ * @since 1.8.0
795
  *
796
+ * @param array[] $cache_keys Cache keys to 'include' and/or 'exclude'.
797
+ * @return string Cache keys regex, false on failure.
798
  */
799
+ private static function get_cache_keys_regex( $cache_keys ) {
800
 
801
+ if ( ! is_array( $cache_keys ) ) {
802
+ return false;
803
+ }
804
+
805
+ $cache_keys_regex = '#^';
806
+
807
+ foreach ( $cache_keys as $filter_type => $filter_value ) {
808
+ switch ( $filter_type ) {
809
+ case 'include':
810
+ $lookahead = '?=';
811
+ break;
812
+ case 'exclude':
813
+ $lookahead = '?!';
814
+ break;
815
+ default:
816
+ continue 2; // Skip to the next filter value.
817
+ }
818
+
819
+ foreach ( $filter_value as $cache_key ) {
820
+ $cache_keys_regex .= '(' . $lookahead . '.*' . preg_quote( $cache_key ) . ')';
821
+ }
822
+ }
823
+
824
+ $cache_keys_regex .= '.+$#';
825
+
826
+ return $cache_keys_regex;
827
+ }
828
+
829
+ /**
830
+ * Get the cache signature.
831
+ *
832
+ * This gets the HTML comment that is inserted at the bottom of a new cache file.
833
+ *
834
+ * @since 1.7.0
835
+ *
836
+ * @param string $cache_file_name Name of the new cache file.
837
+ * @return string HTML comment with the current time in HTTP-date format and the new cache file name.
838
+ */
839
  private static function get_cache_signature( $cache_file_name ) {
840
 
841
  $cache_signature = sprintf(
848
  return $cache_signature;
849
  }
850
 
 
851
  /**
852
+ * Get the cache size from the disk (deprecated).
853
  *
854
+ * @since 1.7.0
855
+ * @deprecated 1.8.0
 
 
 
856
  */
 
857
  public static function get_cache_size( $dir = null ) {
858
 
859
+ if ( empty( $dir ) ) {
860
+ $cache_size = Cache_Enabler::get_cache_size();
 
 
 
 
861
  } else {
862
+ $url = self::get_cache_url( $dir );
863
+ $cache = self::cache_iterator( $url, array( 'subpages' => 1 ) );
864
+ $cache_size = $cache['size'];
865
  }
866
 
867
+ return $cache_size;
868
+ }
 
 
869
 
870
+ /**
871
+ * Get the cache URL for a given directory path.
872
+ *
873
+ * This only checks if the given directory path is in the plugin cache directory. It
874
+ * does not check whether the URL returned is from a cache directory that exists.
875
+ *
876
+ * @since 1.8.0
877
+ *
878
+ * @param string $dir Directory path to a cached page.
879
+ * @return string Full cache URL (with trailing slash if set), empty string if the directory path
880
+ * is invalid.
881
+ */
882
+ private static function get_cache_url( $dir ) {
883
 
884
+ if ( strpos( $dir, CACHE_ENABLER_CACHE_DIR ) !== 0 ) {
885
+ return '';
 
 
 
886
  }
887
 
888
+ $cache_url = parse_url( home_url(), PHP_URL_SCHEME ) . '://' . str_replace( CACHE_ENABLER_CACHE_DIR . '/', '', $dir );
889
+ $cache_url = user_trailingslashit( $cache_url );
890
 
891
+ return $cache_url;
892
+ }
893
 
894
  /**
895
+ * Get the path to the settings file for the current site.
896
  *
897
  * @since 1.4.0
898
+ * @change 1.8.0
899
  *
900
+ * @param bool $fallback (Optional) Whether the fallback settings file should be returned. Default false.
901
+ * @return string Path to the settings file.
902
  */
 
903
  private static function get_settings_file( $fallback = false ) {
904
 
905
  $settings_file = sprintf(
906
  '%s/%s',
907
+ CACHE_ENABLER_SETTINGS_DIR,
908
  self::get_settings_file_name( $fallback )
909
  );
910
 
911
  return $settings_file;
912
  }
913
 
 
914
  /**
915
+ * Get the name of the settings file for the current site.
916
  *
917
  * @since 1.5.5
918
+ * @change 1.8.0
919
  *
920
+ * @param bool $fallback (Optional) Whether the fallback settings file name should be returned. Default false.
921
+ * @param bool $skip_blog_path (Optional) Whether the blog path should be included in the settings file name.
922
+ * Default false.
923
+ * @return string Name of the settings file.
924
  */
 
925
  private static function get_settings_file_name( $fallback = false, $skip_blog_path = false ) {
926
 
927
  $settings_file_name = '';
928
 
929
+ // When creating or deleting the settings file.
930
  if ( function_exists( 'home_url' ) ) {
931
  $settings_file_name = parse_url( home_url(), PHP_URL_HOST );
932
 
 
933
  if ( is_multisite() && defined( 'SUBDOMAIN_INSTALL' ) && ! SUBDOMAIN_INSTALL ) {
934
  $blog_path = Cache_Enabler::get_blog_path();
935
  $settings_file_name .= ( ! empty( $blog_path ) ) ? '.' . trim( $blog_path, '/' ) : '';
936
  }
937
 
938
  $settings_file_name .= '.php';
939
+ // When getting the plugin settings from the settings file.
940
+ } elseif ( is_dir( CACHE_ENABLER_SETTINGS_DIR ) ) {
941
  if ( $fallback ) {
942
+ $settings_files = array_map( 'basename', self::get_dir_objects( CACHE_ENABLER_SETTINGS_DIR ) );
943
  $settings_file_regex = '/\.php$/';
944
 
945
  if ( is_multisite() ) {
946
  $settings_file_regex = '/^' . strtolower( Cache_Enabler_Engine::$request_headers['Host'] );
947
  $settings_file_regex = str_replace( '.', '\.', $settings_file_regex );
948
 
 
949
  if ( defined( 'SUBDOMAIN_INSTALL' ) && ! SUBDOMAIN_INSTALL && ! $skip_blog_path ) {
950
  $url_path = trim( parse_url( $_SERVER['REQUEST_URI'], PHP_URL_PATH ), '/' );
951
 
971
  } else {
972
  $settings_file_name = strtolower( Cache_Enabler_Engine::$request_headers['Host'] );
973
 
 
974
  if ( is_multisite() && defined( 'SUBDOMAIN_INSTALL' ) && ! SUBDOMAIN_INSTALL && ! $skip_blog_path ) {
975
  $url_path = $_SERVER['REQUEST_URI'];
976
  $url_path_pieces = explode( '/', $url_path, 3 );
982
 
983
  $settings_file_name .= '.php';
984
 
985
+ // Check if the main site in a subdirectory network.
986
+ if ( ! is_file( CACHE_ENABLER_SETTINGS_DIR . '/' . $settings_file_name ) ) {
987
  $fallback = false;
988
  $skip_blog_path = true;
989
  $settings_file_name = self::get_settings_file_name( $fallback, $skip_blog_path );
997
  return $settings_file_name;
998
  }
999
 
 
1000
  /**
1001
+ * Get the plugin settings from the settings file for the current site.
1002
+ *
1003
+ * This will create the settings file if it does not exist and the cache engine
1004
+ * was started late. It can update the disk and backend requirements and then
1005
+ * clear the complete cache if the settings are outdated. Having the backend
1006
+ * updated will trigger a new settings file to be created, which if created would
1007
+ * result the settings from that new file being returned.
1008
  *
1009
  * @since 1.5.0
1010
+ * @since 1.8.0 The `$update` parameter was added.
1011
+ * @change 1.8.0
1012
  *
1013
+ * @param bool $update Whether to update the disk and backend requirements if the settings are
1014
+ * outdated. Default true.
1015
+ * @return array Plugin settings from the settings file, empty array on failure.
1016
  */
1017
+ public static function get_settings( $update = true ) {
1018
 
1019
+ $settings = array();
 
 
 
 
1020
  $settings_file = self::get_settings_file();
1021
 
 
1022
  if ( is_file( $settings_file ) ) {
1023
+ $settings = include $settings_file;
 
1024
  } else {
1025
+ $fallback = true;
1026
+ $settings_file = self::get_settings_file( $fallback );
1027
 
1028
+ if ( is_file( $settings_file ) ) {
1029
+ $settings = include $settings_file;
1030
  }
1031
  }
1032
 
1033
+ $outdated_settings = ( ! empty( $settings ) && ( ! defined( 'CACHE_ENABLER_VERSION' ) || ! isset( $settings['version'] ) || $settings['version'] !== CACHE_ENABLER_VERSION ) );
1034
+
1035
+ if ( $outdated_settings ) {
1036
+ $settings = array();
1037
+ }
1038
+
1039
  if ( empty( $settings ) && class_exists( 'Cache_Enabler' ) ) {
1040
+ if ( $outdated_settings ) {
1041
+ if ( $update ) {
1042
+ Cache_Enabler::update();
1043
+ wp_opcache_invalidate( $settings_file );
1044
+ $settings = self::get_settings( false );
1045
+ }
1046
+ } else {
1047
+ $settings_file = self::create_settings_file( Cache_Enabler::get_settings() );
1048
 
1049
+ if ( $settings_file !== false ) {
1050
+ $settings = include $settings_file;
1051
+ }
1052
  }
1053
  }
1054
 
1055
  return $settings;
1056
  }
1057
 
 
1058
  /**
1059
+ * Get the files and directories inside of a given directory.
1060
  *
1061
  * @since 1.4.7
1062
+ * @since 1.8.0 The `$recursive` parameter was added.
1063
+ * @since 1.8.0 The `$filter` parameter was added.
1064
+ * @change 1.8.0
1065
+ *
1066
+ * @param string $dir Directory path to scan (without trailing slash).
1067
+ * @param bool $recursive (Optional) Whether to recursively include directory objects in nested
1068
+ * directories. Default false.
1069
+ * @param array[] $filter (Optional) Directory paths relative to $dir (without leading and/or trailing
1070
+ * slashes) to 'include' and/or 'exclude'. Default null.
1071
+ * @return string[] File and directory paths to objects found, empty array if the directory path is invalid.
1072
  */
1073
+ private static function get_dir_objects( $dir, $recursive = false, $filter = null ) {
1074
 
1075
+ $dir_objects = array();
1076
 
1077
+ if ( ! is_dir( $dir ) ) {
1078
+ return $dir_objects;
1079
+ }
1080
 
1081
+ $dir_object_names = scandir( $dir ); // The sorted order is alphabetical in ascending order.
1082
+
1083
+ if ( is_array( $filter ) && empty( $filter['full_path'] ) ) {
1084
+ $filter['full_path'] = 1;
1085
+
1086
+ foreach ( $filter as $filter_type => &$filter_value ) {
1087
+ if ( $filter_type === 'include' || $filter_type === 'exclude' ) {
1088
+ foreach ( $filter_value as &$filter_object ) {
1089
+ $filter_object = $dir . '/' . $filter_object;
1090
+ }
1091
+ }
1092
+ }
1093
+ }
1094
+
1095
+ foreach ( $dir_object_names as $dir_object_name ) {
1096
+ if ( $dir_object_name === '.' || $dir_object_name === '..' ) {
1097
+ continue; // Skip object because it is the current or parent folder link.
1098
+ }
1099
+
1100
+ $dir_object = $dir . '/' . $dir_object_name;
1101
+
1102
+ if ( is_dir( $dir_object ) ) {
1103
+ if ( ! empty( $filter['full_path'] ) && ! self::filter_dir_object( $dir_object, $filter ) ) {
1104
+ continue; // Skip object because it is excluded.
1105
+ }
1106
+
1107
+ if ( $recursive ) {
1108
+ $dir_objects = array_merge( $dir_objects, self::get_dir_objects( $dir_object, $recursive, $filter ) );
1109
+ }
1110
+ }
1111
+
1112
+ $dir_objects[] = $dir_object;
1113
  }
1114
 
1115
  return $dir_objects;
1116
  }
1117
 
 
1118
  /**
1119
+ * Get the site objects (deprecated).
 
 
 
1120
  *
1121
+ * @since 1.6.0
1122
+ * @deprecated 1.8.0
1123
  */
 
1124
  public static function get_site_objects( $site_url ) {
1125
 
1126
  $site_objects = array();
1127
+ $dir = self::get_cache_dir( $site_url );
1128
 
 
 
 
 
1129
  if ( ! is_dir( $dir ) ) {
1130
  return $site_objects;
1131
  }
1132
 
1133
+ $site_objects = array_map( 'basename', self::get_dir_objects( $dir ) );
 
1134
 
1135
+ // Maybe filter the site objects.
1136
  if ( is_multisite() && ! is_subdomain_install() ) {
1137
  $blog_path = Cache_Enabler::get_blog_path();
1138
  $blog_paths = Cache_Enabler::get_blog_paths();
1139
 
1140
+ // Check if the main site in a subdirectory network.
1141
  if ( ! in_array( $blog_path, $blog_paths, true ) ) {
1142
  foreach ( $site_objects as $key => $site_object ) {
1143
+ // Delete the site object if it does not belong to the main site.
1144
  if ( in_array( '/' . $site_object . '/', $blog_paths, true ) ) {
1145
  unset( $site_objects[ $key ] );
1146
  }
1151
  return $site_objects;
1152
  }
1153
 
 
1154
  /**
1155
+ * Get the current time.
1156
  *
1157
+ * @since 1.7.0
 
1158
  *
1159
+ * @return string Current time in HTTP-date format.
1160
  */
 
1161
  private static function get_current_time() {
1162
 
1163
  $current_time = current_time( 'D, d M Y H:i:s', true ) . ' GMT';
1165
  return $current_time;
1166
  }
1167
 
 
1168
  /**
1169
+ * Get the image path from an image URL.
1170
+ *
1171
+ * This does not check whether the returned image exists.
1172
  *
1173
  * @since 1.4.8
1174
+ * @change 1.8.0
1175
  *
1176
+ * @param string $image_url Full or relative URL maybe with an intrinsic width or density descriptor.
1177
+ * @return string File path to the image.
1178
  */
 
1179
  private static function get_image_path( $image_url ) {
1180
 
1181
+ // In case there is an intrinsic width or density descriptor.
1182
+ $image_pieces = explode( ' ', $image_url );
1183
+ $image_url = $image_pieces[0];
1184
 
1185
+ // In case installation is in a subdirectory.
1186
+ $image_url_path = ltrim( parse_url( $image_url, PHP_URL_PATH ), '/' );
1187
  $installation_dir = ltrim( parse_url( site_url( '/' ), PHP_URL_PATH ), '/' );
1188
+ $image_path = str_replace( $installation_dir, '', ABSPATH ) . $image_url_path;
1189
 
1190
  return $image_path;
1191
  }
1192
 
 
1193
  /**
1194
+ * Get the current WordPress filesystem instance.
1195
+ *
1196
+ * This will initialize the WordPress filesystem if it has not yet been and will
1197
+ * cache the result afterward.
1198
  *
1199
  * @since 1.7.0
1200
+ * @change 1.7.1
1201
+ *
1202
+ * @throws \RuntimeException If the WordPress filesystem could not be initialized.
1203
+ *
1204
+ * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass.
1205
  *
1206
+ * @return WP_Filesystem_Base WordPress filesystem.
 
1207
  */
 
1208
  public static function get_filesystem() {
1209
 
1210
  global $wp_filesystem;
1211
 
 
1212
  if ( $wp_filesystem instanceof WP_Filesystem_Base ) {
1213
  return $wp_filesystem;
1214
  }
1215
 
 
1216
  try {
1217
  require_once ABSPATH . 'wp-admin/includes/file.php';
1218
 
1226
  if ( is_wp_error( $wp_filesystem->errors ) && $wp_filesystem->errors->has_errors() ) {
1227
  throw new \RuntimeException(
1228
  $wp_filesystem->errors->get_error_message(),
1229
+ is_numeric( $wp_filesystem->errors->get_error_code() ) ? (int) $wp_filesystem->errors->get_error_code() : 0
1230
  );
1231
  }
1232
 
1247
  return $wp_filesystem;
1248
  }
1249
 
 
1250
  /**
1251
+ * Make a directory recursively based on the directory path.
1252
+ *
1253
+ * This assumes that the directory (and its parent) should have 755 permissions,
1254
+ * and will attempt to update any existing directories accordingly.
1255
  *
1256
  * @since 1.7.0
1257
  * @change 1.7.2
1258
  *
1259
+ * @param string $dir Directory path to create.
1260
+ * @return bool True if the directory either already exists or was created *and* has the
1261
+ * correct permissions, false otherwise.
1262
  */
 
1263
  private static function mkdir_p( $dir ) {
1264
 
1265
+ /**
1266
+ * Filters the mode assigned to directories on creation.
1267
+ *
1268
+ * @since 1.7.2
1269
+ *
1270
+ * @param int $mode Mode that defines the access permissions for the created directory. The mode
1271
+ * must be an octal number, which means it should have a leading zero. Default is 0755.
1272
+ */
1273
  $mode_octal = apply_filters( 'cache_enabler_mkdir_mode', 0755 );
1274
+ $mode_string = decoct( $mode_octal ); // Get the last three digits (e.g. '755').
1275
  $parent_dir = dirname( $dir );
1276
+ $fs = self::get_filesystem();
1277
 
 
1278
  if ( $fs->is_dir( $dir ) && $fs->getchmod( $dir ) === $mode_string && $fs->getchmod( $parent_dir ) === $mode_string ) {
1279
  return true;
1280
  }
1281
 
 
1282
  if ( ! wp_mkdir_p( $dir ) ) {
1283
  return false;
1284
  }
1285
 
 
1286
  if ( $fs->getchmod( $parent_dir ) !== $mode_string ) {
1287
  return $fs->chmod( $parent_dir, $mode_octal, true );
1288
  }
1289
 
 
1290
  if ( $fs->getchmod( $dir ) !== $mode_string ) {
1291
  return $fs->chmod( $dir, $mode_octal );
1292
  }
1294
  return true;
1295
  }
1296
 
1297
+ /**
1298
+ * Remove an empty directory based on the directory path.
1299
+ *
1300
+ * This is a wrapper for rmdir() that can delete empty parent directories and will
1301
+ * call clearstatcache() when necessary. It suppresses errors on failure.
1302
+ *
1303
+ * @since 1.8.0
1304
+ *
1305
+ * @param string $dir Directory path to remove.
1306
+ * @param bool $parents (Optional) Whether empty parent directories should also be removed. Default false.
1307
+ * @return array[]|bool An array of removed directories with paths as the keys and objects as the
1308
+ * values. There are no directory objects because a directory has to be empty to
1309
+ * be removed, which is why it will always be an empty array. False if no
1310
+ * directories were removed.
1311
+ */
1312
+ private static function rmdir( $dir, $parents = false ) {
1313
+
1314
+ $removed_dir = @rmdir( $dir );
1315
+
1316
+ clearstatcache();
1317
+
1318
+ if ( $removed_dir ) {
1319
+ $removed_dir = array( $dir => array() );
1320
+
1321
+ if ( $parents ) {
1322
+ $parent_dir = dirname( $dir );
1323
+
1324
+ while ( @rmdir( $parent_dir ) ) {
1325
+ clearstatcache();
1326
+ $removed_dir[ $parent_dir ] = array();
1327
+ $parent_dir = dirname( $parent_dir );
1328
+ }
1329
+ }
1330
+ }
1331
+
1332
+ return $removed_dir;
1333
+ }
1334
 
1335
  /**
1336
+ * Set or unset the WP_CACHE constant in the wp-config.php file.
1337
  *
1338
+ * @since 1.5.0
1339
  * @change 1.7.0
1340
  *
1341
+ * @param bool $set (Optional) True to set the WP_CACHE constant, false to unset. Default true.
1342
  */
 
1343
  private static function set_wp_cache_constant( $set = true ) {
1344
 
 
1345
  if ( file_exists( ABSPATH . 'wp-config.php' ) ) {
1346
+ // The config file resides in ABSPATH.
1347
  $wp_config_file = ABSPATH . 'wp-config.php';
1348
  } elseif ( @file_exists( dirname( ABSPATH ) . '/wp-config.php' ) && ! @file_exists( dirname( ABSPATH ) . '/wp-settings.php' ) ) {
1349
+ // The config file resides one level above ABSPATH but is not part of another installation.
1350
  $wp_config_file = dirname( ABSPATH ) . '/wp-config.php';
1351
  } else {
1352
  $wp_config_file = false;
1353
  }
1354
 
 
1355
  if ( ! $wp_config_file || ! is_writable( $wp_config_file ) ) {
1356
  return;
1357
  }
1358
 
 
1359
  $wp_config_file_contents = file_get_contents( $wp_config_file );
1360
 
 
1361
  if ( ! is_string( $wp_config_file_contents ) ) {
1362
  return;
1363
  }
1364
 
 
1365
  $found_wp_cache_constant = preg_match( '/define\s*\(\s*[\'\"]WP_CACHE[\'\"]\s*,.+\);/', $wp_config_file_contents );
1366
 
 
1367
  if ( $set && ! $found_wp_cache_constant ) {
1368
  $ce_wp_config_lines = '/** Enables page caching for Cache Enabler. */' . PHP_EOL;
1369
  $ce_wp_config_lines .= "if ( ! defined( 'WP_CACHE' ) ) {" . PHP_EOL;
1373
  $wp_config_file_contents = preg_replace( '/(\/\*\* Sets up WordPress vars and included files\. \*\/)/', $ce_wp_config_lines . '$1', $wp_config_file_contents );
1374
  }
1375
 
 
1376
  if ( ! $set ) {
1377
  $wp_config_file_contents = preg_replace( '/.+Added by Cache Enabler\r\n/', '', $wp_config_file_contents ); // < 1.5.0
1378
  $wp_config_file_contents = preg_replace( '/\/\*\* Enables page caching for Cache Enabler\. \*\/' . PHP_EOL . '.+' . PHP_EOL . '.+' . PHP_EOL . '\}' . PHP_EOL . PHP_EOL . '/', '', $wp_config_file_contents );
1379
  }
1380
 
 
1381
  file_put_contents( $wp_config_file, $wp_config_file_contents, LOCK_EX );
1382
  }
1383
 
1384
+ /**
1385
+ * Sort file and directory paths by the number of forward slashes.
1386
+ *
1387
+ * This sorts paths by the lowest amount of forward slashes to the highest.
1388
+ *
1389
+ * @since 1.8.0
1390
+ *
1391
+ * @param string $a File or directory path to compare in sort.
1392
+ * @param string $b File or directory path to compare in sort.
1393
+ * @return int 1 if $a has more slashes than $b, 0 if equal, and -1 if less.
1394
+ */
1395
+ private static function sort_dir_objects( $a, $b ) {
1396
+
1397
+ $a = substr_count( $a, '/' );
1398
+ $b = substr_count( $b, '/' );
1399
+
1400
+ if ( $a === $b ) {
1401
+ return 0;
1402
+ }
1403
+
1404
+ return ( $a > $b ) ? 1 : -1;
1405
+ }
1406
 
1407
  /**
1408
+ * Convert the page contents.
1409
+ *
1410
+ * This handles converting inline image URLs for the WebP cache version.
1411
  *
1412
  * @since 1.7.0
1413
+ * @change 1.8.0
1414
  *
1415
+ * @param string $page_contents Page contents from the cache engine as raw HTML.
1416
+ * @return string Page contents after maybe being converted.
1417
  */
 
1418
  private static function converter( $page_contents ) {
1419
 
1420
+ /**
1421
+ * Filters the HTML attributes to convert during the WebP conversion.
1422
+ *
1423
+ * @since 1.6.1
1424
+ *
1425
+ * @param string[] $attributes HTML attributes to convert during the WebP conversion. Default are 'src',
1426
+ * 'srcset', and 'data-*'.
1427
+ */
1428
+ $attributes = (array) apply_filters( 'cache_enabler_convert_webp_attributes', array( 'src', 'srcset', 'data-[^=]+' ) );
1429
  $attributes_regex = implode( '|', $attributes );
1430
 
1431
+ /**
1432
+ * Filters whether inline image URLs with query strings should be ignored during the WebP conversion.
1433
+ *
1434
+ * @since 1.6.1
1435
+ *
1436
+ * @param bool $ignore_query_strings True if inline image URLs with query strings should be ignored during the WebP
1437
+ * conversion, false if not. Default true.
1438
+ */
1439
+ if ( apply_filters( 'cache_enabler_convert_webp_ignore_query_strings', true ) ) {
1440
+ $image_urls_regex = '#(?:(?:(' . $attributes_regex . ')\s*=|(url)\()\s*[\'\"]?\s*)\K(?:[^\?\"\'\s>]+)(?:\.jpe?g|\.png)(?:\s\d+[wx][^\"\'>]*)?(?=\/?[\"\'\s\)>])(?=[^<{]*(?:\)[^<{]*\}|>))#i';
1441
+ } else {
1442
  $image_urls_regex = '#(?:(?:(' . $attributes_regex . ')\s*=|(url)\()\s*[\'\"]?\s*)\K(?:[^\"\'\s>]+)(?:\.jpe?g|\.png)(?:\s\d+[wx][^\"\'>]*)?(?=\/?[\?\"\'\s\)>])(?=[^<{]*(?:\)[^<{]*\}|>))#i';
1443
  }
1444
 
1445
+ /**
1446
+ * Filters the page contents after the inline image URLs were maybe converted to WebP.
1447
+ *
1448
+ * @since 1.6.0
1449
+ *
1450
+ * @param string $page_contents Page contents from the cache engine as raw HTML.
1451
+ */
1452
  $converted_page_contents = apply_filters( 'cache_enabler_page_contents_after_webp_conversion', preg_replace_callback( $image_urls_regex, 'self::convert_webp', $page_contents ) );
 
 
1453
  $converted_page_contents = apply_filters_deprecated( 'cache_enabler_disk_webp_converted_data', array( $converted_page_contents ), '1.6.0', 'cache_enabler_page_contents_after_webp_conversion' );
1454
 
1455
  return $converted_page_contents;
1456
  }
1457
 
 
1458
  /**
1459
+ * Convert image URL(s) to WebP.
1460
  *
1461
+ * @since 1.5.0
1462
+ * @change 1.8.0
1463
  *
1464
+ * @param string[] $matches Pattern matches from parsed page contents.
1465
+ * @return string The image URL(s) after maybe being converted to WebP.
1466
  */
 
1467
  private static function convert_webp( $matches ) {
1468
 
1469
+ $full_match = $matches[0];
1470
  $image_extension_regex = '/(\.jpe?g|\.png)/i';
1471
+ $image_found = preg_match( $image_extension_regex, $full_match );
1472
+
1473
+ if ( ! $image_found ) {
1474
+ return $full_match;
1475
+ }
1476
 
1477
+ $image_urls = explode( ',', $full_match );
 
 
1478
 
1479
+ foreach ( $image_urls as &$image_url ) {
1480
+ $image_url = trim( $image_url, ' ' );
1481
+ $image_url_webp = preg_replace( $image_extension_regex, '$1.webp', $image_url ); // Append the .webp extension.
1482
+ $image_path_webp = self::get_image_path( $image_url_webp );
1483
+
1484
+ if ( is_file( $image_path_webp ) ) {
1485
+ $image_url = $image_url_webp;
1486
+ } else {
1487
+ $image_url_webp = preg_replace( $image_extension_regex, '', $image_url_webp ); // Remove the default extension.
1488
  $image_path_webp = self::get_image_path( $image_url_webp );
1489
 
 
1490
  if ( is_file( $image_path_webp ) ) {
1491
  $image_url = $image_url_webp;
 
 
 
 
 
 
 
 
1492
  }
1493
  }
1494
+ }
1495
 
1496
+ $conversion = implode( ', ', $image_urls );
1497
 
1498
+ return $conversion;
 
1499
  }
1500
 
 
1501
  /**
1502
+ * Minify HTML.
1503
  *
1504
+ * This removes HTML, CSS, and JavaScript comments. Whitespaces of any size are
1505
+ * replaced with a single space.
1506
+ *
1507
+ * @since 1.5.0
1508
  * @change 1.7.0
1509
  *
1510
+ * @param string $html Page contents from the cache engine as raw HTML.
1511
+ * @return string Page contents after maybe being minified.
1512
  */
1513
+ private static function minify_html( $html ) {
1514
 
1515
+ if ( strlen( $html ) > 700000 ) {
1516
+ return $html;
 
 
 
1517
  }
1518
 
1519
+ /**
1520
+ * Filters the HTML tags to ignore during HTML minification.
1521
+ *
1522
+ * @since 1.6.0
1523
+ *
1524
+ * @param string[] $ignore_tags The names of HTML tags to ignore. Default are 'textarea', 'pre', and 'code'.
1525
+ */
1526
  $ignore_tags = (array) apply_filters( 'cache_enabler_minify_html_ignore_tags', array( 'textarea', 'pre', 'code' ) );
 
 
1527
  $ignore_tags = (array) apply_filters_deprecated( 'cache_minify_ignore_tags', array( $ignore_tags ), '1.6.0', 'cache_enabler_minify_html_ignore_tags' );
1528
 
 
1529
  if ( ! Cache_Enabler_Engine::$settings['minify_inline_css_js'] ) {
1530
  array_push( $ignore_tags, 'style', 'script' );
1531
  }
1532
 
 
1533
  if ( ! $ignore_tags ) {
1534
+ return $html; // At least one HTML tag is required.
1535
  }
1536
 
 
1537
  $ignore_tags_regex = implode( '|', $ignore_tags );
1538
 
1539
+ // Remove HTML comments.
1540
+ $minified_html = preg_replace( '#<!--[^\[><].*?-->#s', '', $html );
1541
 
 
1542
  if ( Cache_Enabler_Engine::$settings['minify_inline_css_js'] ) {
1543
+ // Remove CSS and JavaScript comments.
1544
  $minified_html = preg_replace(
1545
  '#/\*(?!!)[\s\S]*?\*/|(?:^[ \t]*)//.*$|((?<!\()[ \t>;,{}[\]])//[^;\n]*$#m',
1546
  '$1',
1548
  );
1549
  }
1550
 
1551
+ // Replace whitespaces of any size with a single space.
1552
  $minified_html = preg_replace(
1553
  '#(?>[^\S ]\s*|\s{2,})(?=[^<]*+(?:<(?!/?(?:' . $ignore_tags_regex . ')\b)[^<]*+)*+(?:<(?>' . $ignore_tags_regex . ')\b|\z))#ix',
1554
  ' ',
1555
  $minified_html
1556
  );
1557
 
 
1558
  if ( strlen( $minified_html ) <= 1 ) {
1559
+ return $html; // HTML minification failed.
1560
  }
1561
 
1562
  return $minified_html;
1563
  }
1564
 
 
1565
  /**
1566
+ * Delete a settings file based on a given settings file path.
1567
+ *
1568
+ * This will try to remove the settings file directory and any of its empty parent
1569
+ * directories. It suppresses errors on failure.
1570
  *
1571
+ * @since 1.5.0
1572
+ * @since 1.8.0 The `$settings_file` parameter was added.
1573
+ * @change 1.8.0
1574
  *
1575
+ * @param string (Optional) Path to the settings file. Default is the settings file for the
1576
+ * current site.
1577
  */
1578
+ public static function delete_settings_file( $settings_file = null ) {
1579
 
1580
+ if ( empty( $settings_file ) ) {
1581
+ $settings_file = self::get_settings_file();
1582
+ }
 
 
 
 
 
 
 
 
1583
 
1584
+ if ( @unlink( $settings_file ) ) {
1585
+ self::rmdir( CACHE_ENABLER_SETTINGS_DIR, true );
1586
  }
1587
  }
1588
 
 
1589
  /**
1590
+ * Delete an asset (deprecated).
1591
  *
1592
+ * @since 1.0.0
1593
+ * @deprecated 1.5.0
1594
  */
1595
+ public static function delete_asset( $url ) {
1596
 
1597
+ Cache_Enabler::clear_page_cache_by_url( $url, 'subpages' );
 
 
 
 
 
 
 
 
 
 
 
 
1598
  }
1599
 
 
1600
  /**
1601
+ * Validate the cache iterator arguments.
1602
  *
1603
+ * @since 1.8.0
1604
+ *
1605
+ * @param array $args Cache iterator arguments.
1606
+ * @return array Validated cache iterator arguments.
1607
  */
1608
+ private static function validate_cache_iterator_args( $args ) {
1609
+
1610
+ $validated_args = array();
1611
+
1612
+ foreach ( $args as $arg_name => $arg_value ) {
1613
+ if ( $arg_name === 'root' ) {
1614
+ $validated_args[ $arg_name ] = (string) $arg_value;
1615
+ } elseif ( is_array( $arg_value ) ) {
1616
+ foreach ( $arg_value as $filter_type => $filter_value ) {
1617
+ if ( is_string( $filter_value ) ) {
1618
+ $filter_value = ( substr_count( $filter_value, '|' ) > 0 ) ? explode( '|', $filter_value ) : explode( ',', $filter_value );
1619
+ } elseif ( ! is_array( $filter_value ) ) {
1620
+ $filter_value = array(); // The type is not being converting to avoid unwanted values.
1621
+ }
1622
 
1623
+ foreach ( $filter_value as $filter_value_key => &$filter_value_item ) {
1624
+ $filter_value_item = trim( $filter_value_item, '/- ' );
1625
 
1626
+ if ( empty( $filter_value_item ) ) {
1627
+ unset( $filter_value[ $filter_value_key ] );
1628
+ }
1629
+ }
1630
 
1631
+ if ( $filter_type !== 'include' || $filter_type !== 'exclude' ) {
1632
+ unset( $arg_value[ $filter_type ] );
1633
 
1634
+ if ( $filter_type === 0 || $filter_type === 'i' ) {
1635
+ $filter_type = 'include';
1636
+ } elseif ( $filter_type === 1 || $filter_type === 'e' ) {
1637
+ $filter_type = 'exclude';
1638
+ }
1639
+ }
1640
 
1641
+ $arg_value[ $filter_type ] = $filter_value;
1642
+ }
 
 
 
 
1643
 
1644
+ $validated_args[ $arg_name ] = $arg_value;
1645
+ } else {
1646
+ $validated_args[ $arg_name ] = (int) $arg_value;
1647
+ }
1648
+ }
1649
 
1650
+ return $validated_args;
1651
  }
1652
  }
inc/cache_enabler_engine.class.php CHANGED
@@ -1,6 +1,6 @@
1
  <?php
2
  /**
3
- * Cache Enabler engine
4
  *
5
  * @since 1.5.0
6
  */
@@ -10,156 +10,135 @@ if ( ! defined( 'ABSPATH' ) ) {
10
  }
11
 
12
  final class Cache_Enabler_Engine {
13
-
14
  /**
15
- * start engine
16
  *
17
  * @since 1.5.2
18
- * @change 1.6.0
 
19
  *
20
- * @return boolean true if engine started, false otherwise
 
21
  */
 
22
 
23
- public static function start() {
24
-
25
- if ( self::should_start() ) {
26
  new self();
27
  }
28
 
29
  return self::$started;
30
  }
31
 
32
-
33
  /**
34
- * engine status
35
  *
36
- * @since 1.5.0
37
- * @change 1.5.0
38
  *
39
- * @var boolean
40
  */
41
-
42
  public static $started = false;
43
 
44
-
45
  /**
46
- * specific HTTP request headers from current request
47
  *
48
- * @since 1.7.0
49
- * @change 1.7.0
50
  *
51
- * @var array
52
  */
53
-
54
  public static $request_headers;
55
 
56
-
57
  /**
58
- * engine settings from disk or database
59
  *
60
- * @since 1.5.0
61
- * @change 1.5.0
62
  *
63
- * @var array
 
 
64
  */
65
-
66
  public static $settings;
67
 
68
-
69
  /**
70
- * constructor
 
 
 
 
 
 
 
 
71
  *
72
  * @since 1.5.0
73
- * @change 1.7.0
 
 
74
  */
75
-
76
  public function __construct() {
77
 
78
- // get request headers
 
 
 
 
79
  self::$request_headers = self::get_request_headers();
80
 
81
- // get settings from disk if directory index file
82
  if ( self::is_index() ) {
83
- self::$settings = Cache_Enabler_Disk::get_settings();
84
- // get settings from database in late engine start otherwise
85
  } elseif ( class_exists( 'Cache_Enabler' ) ) {
86
- self::$settings = Cache_Enabler::get_settings();
87
- // set deprecated settings
88
- Cache_Enabler::$options = self::$settings;
89
- Cache_Enabler::$options['webp'] = self::$settings['convert_image_urls_to_webp'];
90
  }
91
 
92
- // check engine status
93
- if ( ! empty( self::$settings ) ) {
94
- self::$started = true;
95
- }
96
  }
97
 
98
-
99
  /**
100
- * check if engine should start
101
  *
102
  * @since 1.5.2
103
- * @change 1.7.0
104
  *
105
- * @return boolean true if engine should start, false otherwise
106
  */
107
-
108
  public static function should_start() {
109
 
110
- // check if engine is running already
111
- if ( self::$started ) {
112
- return false;
113
- }
114
-
115
- // check if Ajax request in early engine start
116
- if ( defined( 'DOING_AJAX' ) && DOING_AJAX && ! class_exists( 'Cache_Enabler' ) ) {
117
- return false;
118
- }
119
-
120
- // check if REST API request
121
- if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) {
122
- return false;
123
- }
124
 
125
- // check if XMLRPC request
126
- if ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST ) {
127
- return false;
128
- }
129
-
130
- // check request URI
131
- if ( str_replace( array( '.ico', '.txt', '.xml', '.xsl' ), '', $_SERVER['REQUEST_URI'] ) !== $_SERVER['REQUEST_URI'] ) {
132
  return false;
133
  }
134
 
135
  return true;
136
  }
137
 
138
-
139
  /**
140
- * start output buffering
141
  *
142
  * @since 1.5.0
143
  * @change 1.6.0
144
  */
145
-
146
  public static function start_buffering() {
147
 
148
  ob_start( 'self::end_buffering' );
149
  }
150
 
151
-
152
  /**
153
- * end output buffering and cache page if applicable
154
  *
155
  * @since 1.0.0
156
  * @change 1.7.0
157
  *
158
- * @param string $contents contents from the output buffer
159
- * @param integer $phase bitmask of PHP_OUTPUT_HANDLER_* constants
160
- * @return string $contents unchanged contents from the output buffer
161
  */
162
-
163
  private static function end_buffering( $contents, $phase ) {
164
 
165
  if ( $phase & PHP_OUTPUT_HANDLER_FINAL || $phase & PHP_OUTPUT_HANDLER_END ) {
@@ -171,67 +150,65 @@ final class Cache_Enabler_Engine {
171
  return $contents;
172
  }
173
 
174
-
175
  /**
176
- * get specific HTTP request headers from current request
177
  *
178
  * @since 1.7.0
179
- * @change 1.7.0
180
  *
181
- * @return array $request_headers specific HTTP request headers from current request
182
  */
183
-
184
  private static function get_request_headers() {
185
 
186
- $request_headers = ( function_exists( 'apache_request_headers' ) ) ? apache_request_headers() : array();
 
 
 
 
187
 
188
  $request_headers = array(
189
- 'Accept' => ( isset( $request_headers['Accept'] ) ) ? $request_headers['Accept'] : ( ( isset( $_SERVER[ 'HTTP_ACCEPT' ] ) ) ? $_SERVER[ 'HTTP_ACCEPT' ] : '' ),
190
- 'Accept-Encoding' => ( isset( $request_headers['Accept-Encoding'] ) ) ? $request_headers['Accept-Encoding'] : ( ( isset( $_SERVER[ 'HTTP_ACCEPT_ENCODING' ] ) ) ? $_SERVER[ 'HTTP_ACCEPT_ENCODING' ] : '' ),
191
- 'Host' => ( isset( $request_headers['Host'] ) ) ? $request_headers['Host'] : ( ( isset( $_SERVER[ 'HTTP_HOST' ] ) ) ? $_SERVER[ 'HTTP_HOST' ] : '' ),
192
- 'If-Modified-Since' => ( isset( $request_headers['If-Modified-Since'] ) ) ? $request_headers['If-Modified-Since'] : ( ( isset( $_SERVER[ 'HTTP_IF_MODIFIED_SINCE' ] ) ) ? $_SERVER[ 'HTTP_IF_MODIFIED_SINCE' ] : '' ),
193
- 'User-Agent' => ( isset( $request_headers['User-Agent'] ) ) ? $request_headers['User-Agent'] : ( ( isset( $_SERVER[ 'HTTP_USER_AGENT' ] ) ) ? $_SERVER[ 'HTTP_USER_AGENT' ] : '' ),
194
- 'X-Forwarded-Proto' => ( isset( $request_headers['X-Forwarded-Proto'] ) ) ? $request_headers['X-Forwarded-Proto'] : ( ( isset( $_SERVER[ 'HTTP_X_FORWARDED_PROTO' ] ) ) ? $_SERVER[ 'HTTP_X_FORWARDED_PROTO' ] : '' ),
195
- 'X-Forwarded-Scheme' => ( isset( $request_headers['X-Forwarded-Scheme'] ) ) ? $request_headers['X-Forwarded-Scheme'] : ( ( isset( $_SERVER[ 'HTTP_X_FORWARDED_SCHEME' ] ) ) ? $_SERVER[ 'HTTP_X_FORWARDED_SCHEME' ] : '' ),
196
  );
197
 
198
  return $request_headers;
199
  }
200
 
201
-
202
  /**
203
- * check if directory index file
204
  *
205
- * @since 1.0.0
206
- * @change 1.5.0
207
  *
208
- * @return boolean true if directory index file, false otherwise
209
  */
210
-
211
  private static function is_index() {
212
 
213
- if ( strtolower( basename( $_SERVER['SCRIPT_NAME'] ) ) === 'index.php' ) {
214
  return true;
215
  }
216
 
217
  return false;
218
  }
219
 
220
-
221
  /**
222
- * check if contents from the output buffer can be cached
223
  *
224
  * @since 1.5.0
225
- * @change 1.7.0
226
  *
227
- * @param string $contents contents from the output buffer
228
- * @return boolean true if contents from the output buffer are cacheable, false otherwise
229
  */
230
-
231
  private static function is_cacheable( $contents ) {
232
 
233
  $has_html_tag = ( stripos( $contents, '<html' ) !== false );
234
- $has_html5_doctype = preg_match( '/^<!DOCTYPE.+html>/i', ltrim( $contents ) );
235
  $has_xsl_stylesheet = ( stripos( $contents, '<xsl:stylesheet' ) !== false || stripos( $contents, '<?xml-stylesheet' ) !== false );
236
 
237
  if ( $has_html_tag && $has_html5_doctype && ! $has_xsl_stylesheet ) {
@@ -241,53 +218,49 @@ final class Cache_Enabler_Engine {
241
  return false;
242
  }
243
 
244
-
245
  /**
246
- * check permalink structure
 
 
 
247
  *
248
  * @since 1.5.0
249
- * @change 1.5.0
250
  *
251
- * @return boolean true if request URI does not match permalink structure or if plain, false otherwise
252
  */
253
-
254
  private static function is_wrong_permalink_structure() {
255
 
256
- // check if trailing slash is set and missing (ignoring root index and file extensions)
257
- if ( self::$settings['permalink_structure'] === 'has_trailing_slash' ) {
258
  if ( preg_match( '/\/[^\.\/\?]+(\?.*)?$/', $_SERVER['REQUEST_URI'] ) ) {
259
  return true;
260
  }
261
- }
262
-
263
- // check if trailing slash is not set and appended (ignoring root index and file extensions)
264
- if ( self::$settings['permalink_structure'] === 'no_trailing_slash' ) {
265
- if ( preg_match( '/\/[^\.\/\?]+\/(\?.*)?$/', $_SERVER['REQUEST_URI'] ) ) {
266
- return true;
267
- }
268
- }
269
-
270
- // check if custom permalink structure is not set
271
- if ( self::$settings['permalink_structure'] === 'plain' ) {
272
  return true;
273
  }
274
 
275
  return false;
276
  }
277
 
278
-
279
  /**
280
- * check if page is excluded from cache
281
  *
282
  * @since 1.5.0
283
- * @change 1.7.0
284
  *
285
- * @return boolean true if page is excluded from the cache, false otherwise
286
  */
287
-
288
  private static function is_excluded() {
289
 
290
- // if post ID excluded
 
 
 
 
 
 
 
 
291
  if ( ! empty( self::$settings['excluded_post_ids'] ) && function_exists( 'is_singular' ) && is_singular() ) {
292
  $post_id = get_queried_object_id();
293
  $excluded_post_ids = array_map( 'absint', (array) explode( ',', self::$settings['excluded_post_ids'] ) );
@@ -297,7 +270,7 @@ final class Cache_Enabler_Engine {
297
  }
298
  }
299
 
300
- // if page path excluded
301
  if ( ! empty( self::$settings['excluded_page_paths'] ) ) {
302
  $page_path = parse_url( $_SERVER['REQUEST_URI'], PHP_URL_PATH );
303
 
@@ -306,9 +279,8 @@ final class Cache_Enabler_Engine {
306
  }
307
  }
308
 
309
- // if query string excluded
310
  if ( ! empty( $_GET ) ) {
311
- // set regex matching query strings that should bypass the cache
312
  if ( ! empty( self::$settings['excluded_query_strings'] ) ) {
313
  $query_string_regex = self::$settings['excluded_query_strings'];
314
  } else {
@@ -322,15 +294,14 @@ final class Cache_Enabler_Engine {
322
  }
323
  }
324
 
325
- // if cookie excluded
326
  if ( ! empty( $_COOKIE ) ) {
327
- // set regex matching cookies that should bypass the cache
328
  if ( ! empty( self::$settings['excluded_cookies'] ) ) {
329
  $cookies_regex = self::$settings['excluded_cookies'];
330
  } else {
331
  $cookies_regex = '/^(wp-postpass|wordpress_logged_in|comment_author)_/';
332
  }
333
- // bypass cache if an excluded cookie is found
334
  foreach ( $_COOKIE as $key => $value ) {
335
  if ( preg_match( $cookies_regex, $key ) ) {
336
  return true;
@@ -338,110 +309,96 @@ final class Cache_Enabler_Engine {
338
  }
339
  }
340
 
 
 
 
 
 
 
 
341
  return false;
342
  }
343
 
344
-
345
  /**
346
- * check if search page
347
  *
348
- * @since 1.6.0
349
- * @change 1.6.0
350
  *
351
- * @return boolean true if search page, false otherwise
352
  */
353
-
354
- private static function is_search() {
355
-
356
- if ( apply_filters( 'cache_enabler_exclude_search', is_search() ) ) {
357
- return true;
358
- }
359
-
360
- return false;
 
 
 
 
 
 
 
 
 
361
  }
362
 
363
-
364
  /**
365
- * check if cache should be bypassed
366
  *
367
- * @since 1.0.0
368
- * @change 1.7.0
369
  *
370
- * @return boolean true if cache should be bypassed, false otherwise
371
  */
372
-
373
  private static function bypass_cache() {
374
 
375
- // bypass cache hook
376
- if ( apply_filters( 'cache_enabler_bypass_cache', false ) ) {
377
- return true;
378
- }
379
-
380
- // deprecated bypass cache hook
381
- if ( apply_filters_deprecated( 'bypass_cache', array( false ), '1.6.0', 'cache_enabler_bypass_cache' ) ) {
382
- return true;
383
- }
384
-
385
- // check request method
386
- if ( ! isset( $_SERVER['REQUEST_METHOD'] ) || $_SERVER['REQUEST_METHOD'] !== 'GET' ) {
387
- return true;
388
- }
389
-
390
- // check HTTP status code
391
- if ( http_response_code() !== 200 ) {
392
- return true;
393
- }
394
-
395
- // check DONOTCACHEPAGE constant
396
- if ( defined( 'DONOTCACHEPAGE' ) && DONOTCACHEPAGE ) {
397
- return true;
398
- }
399
-
400
- // check conditional tags
401
- if ( self::is_wrong_permalink_structure() || self::is_excluded() ) {
402
- return true;
403
- }
404
-
405
- // check conditional tags when output buffering has ended
406
- if ( class_exists( 'WP' ) ) {
407
- if ( is_admin() || self::is_search() || is_feed() || is_trackback() || is_robots() || is_preview() || post_password_required() ) {
408
- return true;
409
- }
410
- }
411
-
412
- return false;
413
  }
414
 
415
-
416
  /**
417
- * deliver cache
418
  *
419
  * @since 1.5.0
420
- * @change 1.7.0
421
  *
422
- * @return boolean false if cached page was not delivered
423
  */
424
-
425
  public static function deliver_cache() {
426
 
427
  $cache_file = Cache_Enabler_Disk::get_cache_file();
428
 
429
- if ( Cache_Enabler_Disk::cache_exists( $cache_file ) && ! Cache_Enabler_Disk::cache_expired( $cache_file ) && ! self::bypass_cache() ) {
430
- // set X-Cache-Handler response header
431
  header( 'X-Cache-Handler: cache-enabler-engine' );
432
 
433
- // return 304 Not Modified with empty body if applicable
434
  if ( strtotime( self::$request_headers['If-Modified-Since'] >= filemtime( $cache_file ) ) ) {
435
  header( $_SERVER['SERVER_PROTOCOL'] . ' 304 Not Modified', true, 304 );
436
- exit;
437
  }
438
 
439
- // set Content-Encoding response header if applicable
440
- if ( strpos( basename( $cache_file ), 'gz' ) !== false ) {
441
- header( 'Content-Encoding: gzip' );
 
 
 
 
442
  }
443
 
444
- // deliver cache
445
  readfile( $cache_file );
446
  exit;
447
  }
1
  <?php
2
  /**
3
+ * Class used for handling engine-related operations.
4
  *
5
  * @since 1.5.0
6
  */
10
  }
11
 
12
  final class Cache_Enabler_Engine {
 
13
  /**
14
+ * Start the cache engine.
15
  *
16
  * @since 1.5.2
17
+ * @since 1.8.0 The `$force` parameter was added.
18
+ * @change 1.8.0
19
  *
20
+ * @param bool Whether the cache engine should be force started.
21
+ * @return bool True if the cache engine was started, false if not.
22
  */
23
+ public static function start( $force = false ) {
24
 
25
+ if ( $force || self::should_start() ) {
 
 
26
  new self();
27
  }
28
 
29
  return self::$started;
30
  }
31
 
 
32
  /**
33
+ * Whether the cache engine is started.
34
  *
35
+ * @since 1.5.0
 
36
  *
37
+ * @var bool
38
  */
 
39
  public static $started = false;
40
 
 
41
  /**
42
+ * Specific HTTP request headers from the current request.
43
  *
44
+ * @since 1.7.0
 
45
  *
46
+ * @var string[]
47
  */
 
48
  public static $request_headers;
49
 
 
50
  /**
51
+ * Plugin settings from the disk or database.
52
  *
53
+ * The settings will be from the disk when a frontend page is loaded and from the
54
+ * database when an admin page is loaded.
55
  *
56
+ * @since 1.5.0
57
+ *
58
+ * @var array
59
  */
 
60
  public static $settings;
61
 
 
62
  /**
63
+ * Constructor.
64
+ *
65
+ * This is called by self::start() and starts up the cache engine. If the cache
66
+ * engine is already started that means it is being restarted. This can occur when
67
+ * switching sites in a multisite network. If restarted, the WordPress rewrite
68
+ * component will be reinitialized. This is to pick up the correct data for
69
+ * url_to_postid(), user_trailingslashit(), and the pagination bases. The disk and
70
+ * backend requirements will not be updated if the cache engine is being restarted
71
+ * and the settings do not exist or are outdated.
72
  *
73
  * @since 1.5.0
74
+ * @change 1.8.0
75
+ *
76
+ * @global WP_Rewrite $wp_rewrite WordPress rewrite component.
77
  */
 
78
  public function __construct() {
79
 
80
+ if ( self::$started ) {
81
+ global $wp_rewrite;
82
+ $wp_rewrite->init();
83
+ }
84
+
85
  self::$request_headers = self::get_request_headers();
86
 
 
87
  if ( self::is_index() ) {
88
+ self::$settings = Cache_Enabler_Disk::get_settings( ! self::$started );
 
89
  } elseif ( class_exists( 'Cache_Enabler' ) ) {
90
+ self::$settings = Cache_Enabler::get_settings( ! self::$started );
91
+ Cache_Enabler::$options = self::$settings; // Deprecated in 1.5.0.
92
+ Cache_Enabler::$options['webp'] = self::$settings['convert_image_urls_to_webp']; // Deprecated in 1.5.0.
 
93
  }
94
 
95
+ self::$started = ( ! empty( self::$settings ) ) ? true : false;
 
 
 
96
  }
97
 
 
98
  /**
99
+ * Whether the cache engine should start.
100
  *
101
  * @since 1.5.2
102
+ * @change 1.8.0
103
  *
104
+ * @return bool True if the cache engine should start, false otherwise.
105
  */
 
106
  public static function should_start() {
107
 
108
+ $valid_engine_running = ( self::$started && ( ! is_multisite() || ! ms_is_switched() ) );
109
+ $early_ajax_request = ( defined( 'DOING_AJAX' ) && DOING_AJAX && ! class_exists( 'Cache_Enabler' ) );
110
+ $rest_request = ( defined( 'REST_REQUEST' ) && REST_REQUEST );
111
+ $xmlrpc_request = ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST );
112
+ $bad_request_uri = ( str_replace( array( '.ico', '.txt', '.xml', '.xsl' ), '', $_SERVER['REQUEST_URI'] ) !== $_SERVER['REQUEST_URI'] );
 
 
 
 
 
 
 
 
 
113
 
114
+ if ( $valid_engine_running || $early_ajax_request || $rest_request || $xmlrpc_request || $bad_request_uri ) {
 
 
 
 
 
 
115
  return false;
116
  }
117
 
118
  return true;
119
  }
120
 
 
121
  /**
122
+ * Start the output buffering.
123
  *
124
  * @since 1.5.0
125
  * @change 1.6.0
126
  */
 
127
  public static function start_buffering() {
128
 
129
  ob_start( 'self::end_buffering' );
130
  }
131
 
 
132
  /**
133
+ * End the output buffering and maybe cache the page.
134
  *
135
  * @since 1.0.0
136
  * @change 1.7.0
137
  *
138
+ * @param string $contents Contents from the output buffer.
139
+ * @param int $phase Bitmask of the PHP_OUTPUT_HANDLER_* constants.
140
+ * @return string Unmodified contents from the output buffer.
141
  */
 
142
  private static function end_buffering( $contents, $phase ) {
143
 
144
  if ( $phase & PHP_OUTPUT_HANDLER_FINAL || $phase & PHP_OUTPUT_HANDLER_END ) {
150
  return $contents;
151
  }
152
 
 
153
  /**
154
+ * Get the required HTTP request headers from the current request.
155
  *
156
  * @since 1.7.0
157
+ * @change 1.8.0
158
  *
159
+ * @return string[] An array of HTTP request headers with names as the keys.
160
  */
 
161
  private static function get_request_headers() {
162
 
163
+ if ( ! empty( self::$request_headers ) ) {
164
+ return self::$request_headers;
165
+ }
166
+
167
+ $request_headers = function_exists( 'apache_request_headers' ) ? apache_request_headers() : array();
168
 
169
  $request_headers = array(
170
+ 'Accept' => isset( $request_headers['Accept'] ) ? $request_headers['Accept'] : ( isset( $_SERVER[ 'HTTP_ACCEPT' ] ) ? $_SERVER[ 'HTTP_ACCEPT' ] : '' ),
171
+ 'Accept-Encoding' => isset( $request_headers['Accept-Encoding'] ) ? $request_headers['Accept-Encoding'] : ( isset( $_SERVER[ 'HTTP_ACCEPT_ENCODING' ] ) ? $_SERVER[ 'HTTP_ACCEPT_ENCODING' ] : '' ),
172
+ 'Host' => isset( $request_headers['Host'] ) ? $request_headers['Host'] : ( isset( $_SERVER[ 'HTTP_HOST' ] ) ? $_SERVER[ 'HTTP_HOST' ] : '' ),
173
+ 'If-Modified-Since' => isset( $request_headers['If-Modified-Since'] ) ? $request_headers['If-Modified-Since'] : ( isset( $_SERVER[ 'HTTP_IF_MODIFIED_SINCE' ] ) ? $_SERVER[ 'HTTP_IF_MODIFIED_SINCE' ] : '' ),
174
+ 'User-Agent' => isset( $request_headers['User-Agent'] ) ? $request_headers['User-Agent'] : ( isset( $_SERVER[ 'HTTP_USER_AGENT' ] ) ? $_SERVER[ 'HTTP_USER_AGENT' ] : '' ),
175
+ 'X-Forwarded-Proto' => isset( $request_headers['X-Forwarded-Proto'] ) ? $request_headers['X-Forwarded-Proto'] : ( isset( $_SERVER[ 'HTTP_X_FORWARDED_PROTO' ] ) ? $_SERVER[ 'HTTP_X_FORWARDED_PROTO' ] : '' ),
176
+ 'X-Forwarded-Scheme' => isset( $request_headers['X-Forwarded-Scheme'] ) ? $request_headers['X-Forwarded-Scheme'] : ( isset( $_SERVER[ 'HTTP_X_FORWARDED_SCHEME' ] ) ? $_SERVER[ 'HTTP_X_FORWARDED_SCHEME' ] : '' ),
177
  );
178
 
179
  return $request_headers;
180
  }
181
 
 
182
  /**
183
+ * Whether the script being executed is the installation directory index file.
184
  *
185
+ * @since 1.5.0
186
+ * @change 1.8.0
187
  *
188
+ * @return bool True if the script being executed is the index file, false if not.
189
  */
 
190
  private static function is_index() {
191
 
192
+ if ( defined( 'CACHE_ENABLER_INDEX_FILE' ) && $_SERVER['SCRIPT_FILENAME'] === CACHE_ENABLER_INDEX_FILE ) {
193
  return true;
194
  }
195
 
196
  return false;
197
  }
198
 
 
199
  /**
200
+ * Whether the contents from the output buffer can be cached.
201
  *
202
  * @since 1.5.0
203
+ * @change 1.8.0
204
  *
205
+ * @param string $contents Contents from the output buffer.
206
+ * @return bool True if contents from the output buffer are cacheable, false if not.
207
  */
 
208
  private static function is_cacheable( $contents ) {
209
 
210
  $has_html_tag = ( stripos( $contents, '<html' ) !== false );
211
+ $has_html5_doctype = preg_match( '/^<!DOCTYPE.+html\s*>/i', ltrim( $contents ) );
212
  $has_xsl_stylesheet = ( stripos( $contents, '<xsl:stylesheet' ) !== false || stripos( $contents, '<?xml-stylesheet' ) !== false );
213
 
214
  if ( $has_html_tag && $has_html5_doctype && ! $has_xsl_stylesheet ) {
218
  return false;
219
  }
220
 
 
221
  /**
222
+ * Whether the permalink structure is wrong.
223
+ *
224
+ * This checks whether the current site uses trailing slashes and then whether the
225
+ * request URI matches what is set. It ignores the root index and file extensions.
226
  *
227
  * @since 1.5.0
228
+ * @change 1.8.0
229
  *
230
+ * @return bool True if the request URI does not match the permalink structure, false otherwise.
231
  */
 
232
  private static function is_wrong_permalink_structure() {
233
 
234
+ if ( self::$settings['use_trailing_slashes'] ) {
 
235
  if ( preg_match( '/\/[^\.\/\?]+(\?.*)?$/', $_SERVER['REQUEST_URI'] ) ) {
236
  return true;
237
  }
238
+ } elseif ( preg_match( '/\/[^\.\/\?]+\/(\?.*)?$/', $_SERVER['REQUEST_URI'] ) ) {
 
 
 
 
 
 
 
 
 
 
239
  return true;
240
  }
241
 
242
  return false;
243
  }
244
 
 
245
  /**
246
+ * Whether the current request is excluded from the cache.
247
  *
248
  * @since 1.5.0
249
+ * @change 1.8.0
250
  *
251
+ * @return bool True if the current request is excluded from the cache, false otherwise.
252
  */
 
253
  private static function is_excluded() {
254
 
255
+ $bad_request_method = ( ! isset( $_SERVER['REQUEST_METHOD'] ) || $_SERVER['REQUEST_METHOD'] !== 'GET' );
256
+ $bad_response_code = ( http_response_code() !== 200 );
257
+ $donotcachepage = ( defined( 'DONOTCACHEPAGE' ) && DONOTCACHEPAGE );
258
+
259
+ if ( $bad_request_method || $bad_response_code || $donotcachepage || self::is_wrong_permalink_structure() ) {
260
+ return true;
261
+ }
262
+
263
+ // Post ID exclusions.
264
  if ( ! empty( self::$settings['excluded_post_ids'] ) && function_exists( 'is_singular' ) && is_singular() ) {
265
  $post_id = get_queried_object_id();
266
  $excluded_post_ids = array_map( 'absint', (array) explode( ',', self::$settings['excluded_post_ids'] ) );
270
  }
271
  }
272
 
273
+ // Page path exclusions.
274
  if ( ! empty( self::$settings['excluded_page_paths'] ) ) {
275
  $page_path = parse_url( $_SERVER['REQUEST_URI'], PHP_URL_PATH );
276
 
279
  }
280
  }
281
 
282
+ // Query string exclusions.
283
  if ( ! empty( $_GET ) ) {
 
284
  if ( ! empty( self::$settings['excluded_query_strings'] ) ) {
285
  $query_string_regex = self::$settings['excluded_query_strings'];
286
  } else {
294
  }
295
  }
296
 
297
+ // Cookie exclusions.
298
  if ( ! empty( $_COOKIE ) ) {
 
299
  if ( ! empty( self::$settings['excluded_cookies'] ) ) {
300
  $cookies_regex = self::$settings['excluded_cookies'];
301
  } else {
302
  $cookies_regex = '/^(wp-postpass|wordpress_logged_in|comment_author)_/';
303
  }
304
+
305
  foreach ( $_COOKIE as $key => $value ) {
306
  if ( preg_match( $cookies_regex, $key ) ) {
307
  return true;
309
  }
310
  }
311
 
312
+ // When the output buffering is ending.
313
+ if ( class_exists( 'WP' ) ) {
314
+ if ( is_admin() || is_feed() || is_trackback() || is_robots() || is_preview() || post_password_required() || self::exclude_search() ) {
315
+ return true;
316
+ }
317
+ }
318
+
319
  return false;
320
  }
321
 
 
322
  /**
323
+ * Whether to exclude search queries from the cache.
324
  *
325
+ * @since 1.8.0
 
326
  *
327
+ * @return bool True if search queries should be excluded from the cache, false if not.
328
  */
329
+ private static function exclude_search() {
330
+
331
+ /**
332
+ * Filters whether search queries should be excluded from the cache.
333
+ *
334
+ * This requires pretty search URLs. For example, https://example.com/search/query/
335
+ * instead of https://example.com/?s=query. The search cache will not be
336
+ * automatically cleared.
337
+ *
338
+ * @since 1.6.0
339
+ *
340
+ * @param bool $exclude_search True if search queries should be excluded from the cache, false if not. Default
341
+ * is the value returned by is_search().
342
+ */
343
+ $exclude_search = apply_filters( 'cache_enabler_exclude_search', is_search() );
344
+
345
+ return $exclude_search;
346
  }
347
 
 
348
  /**
349
+ * Whether the cache should be bypassed.
350
  *
351
+ * @since 1.5.0
352
+ * @change 1.8.0
353
  *
354
+ * @return bool True if the cache should be bypassed, false otherwise.
355
  */
 
356
  private static function bypass_cache() {
357
 
358
+ /**
359
+ * Filters whether the cache should be bypassed.
360
+ *
361
+ * @since 1.6.0
362
+ * @since 1.8.0 The default value for `$bypass_cache` was updated.
363
+ *
364
+ * @param bool $bypass_cache True if the cache should be bypassed, false if not. Default is the value
365
+ * returned by Cache_Enabler_Engine::is_excluded().
366
+ */
367
+ $bypass_cache = apply_filters( 'cache_enabler_bypass_cache', self::is_excluded() );
368
+ $bypass_cache = apply_filters_deprecated( 'bypass_cache', array( $bypass_cache ), '1.6.0', 'cache_enabler_bypass_cache' );
369
+
370
+ return $bypass_cache;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
371
  }
372
 
 
373
  /**
374
+ * Deliver the cached page for the current request.
375
  *
376
  * @since 1.5.0
377
+ * @change 1.8.0
378
  *
379
+ * @return bool False if the cached page was not delivered.
380
  */
 
381
  public static function deliver_cache() {
382
 
383
  $cache_file = Cache_Enabler_Disk::get_cache_file();
384
 
385
+ if ( Cache_Enabler_Disk::cache_exists( $cache_file ) && ! Cache_Enabler_Disk::cache_expired( $cache_file ) && ! self::bypass_cache() ) {
 
386
  header( 'X-Cache-Handler: cache-enabler-engine' );
387
 
 
388
  if ( strtotime( self::$request_headers['If-Modified-Since'] >= filemtime( $cache_file ) ) ) {
389
  header( $_SERVER['SERVER_PROTOCOL'] . ' 304 Not Modified', true, 304 );
390
+ exit; // Deliver empty body.
391
  }
392
 
393
+ switch ( substr( $cache_file, -2, 2 ) ) {
394
+ case 'br':
395
+ header( 'Content-Encoding: br' );
396
+ break;
397
+ case 'gz':
398
+ header( 'Content-Encoding: gzip' );
399
+ break;
400
  }
401
 
 
402
  readfile( $cache_file );
403
  exit;
404
  }
readme.txt CHANGED
@@ -1,19 +1,19 @@
1
  === Cache Enabler ===
2
  Contributors: keycdn
3
- Tags: cache, caching, performance, gzip, webp, speed
4
- Requires at least: 5.1
5
- Tested up to: 5.7
6
  Requires PHP: 5.6
7
  Stable tag: trunk
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
11
 
12
- A lightweight caching plugin for WordPress that makes your website faster by generating static HTML files. WebP and Gzip support included.
13
 
14
 
15
  == Description ==
16
- Cache Enabler is a simple, yet powerful WordPress caching plugin that is easy to use, needs minimal configuration, and best of all helps improve site performance for a faster load time. It creates static HTML files and stores them on the server's disk. This allows the web server to deliver the static HTML files avoiding resource intensive backend processes from the WordPress core, plugins, and database.
17
 
18
 
19
  = Features =
@@ -22,18 +22,18 @@ Cache Enabler is a simple, yet powerful WordPress caching plugin that is easy to
22
  * Manual cache clearing
23
  * WP-CLI cache clearing
24
  * Cache expiry
25
- * Cache size display in the WordPress dashboard
26
- * Minification of HTML excluding or including inline CSS and JavaScript
27
- * WordPress multisite network support
28
  * WebP support (convert images to WebP with [Optimus](https://optimus.io))
29
- * Gzip pre-compression support
 
 
 
30
  * Custom post type support
31
  * `304 Not Modified` support
32
  * Works perfectly with [Autoptimize](https://wordpress.org/plugins/autoptimize/) and the majority of other third party plugins
33
 
34
 
35
  = How does the caching work? =
36
- Cache Enabler captures page contents and saves it as a static HTML file on the servers disk. Converting inline image URLs to WebP as a separate static HTML file and pre-compressing both static HTML files with Gzip is possible. The accepted static HTML file is then delivered to users without any database queries or on the fly compression for a faster site load time.
37
 
38
 
39
  = Documentation =
@@ -55,6 +55,31 @@ Cache Enabler captures page contents and saves it as a static HTML file on the s
55
 
56
  == Changelog ==
57
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
  = 1.7.2 =
59
  * Update string to be translatable (#235 @timse201)
60
  * Add `cache_enabler_mkdir_mode` filter hook (#233)
1
  === Cache Enabler ===
2
  Contributors: keycdn
3
+ Tags: cache, caching, performance, webp, gzip, brotli, mobile, speed
4
+ Requires at least: 5.5
5
+ Tested up to: 5.8
6
  Requires PHP: 5.6
7
  Stable tag: trunk
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
11
 
12
+ A lightweight caching plugin for WordPress that makes your website faster by generating static HTML files.
13
 
14
 
15
  == Description ==
16
+ Cache Enabler is a simple, yet powerful WordPress caching plugin that is easy to use, needs minimal configuration, and best of all helps improve site performance for a faster load time. It creates static HTML files of frontend pages and stores them on the server's disk. This allows the static HTML files to be delivered instead of generating pages on the fly, avoiding resource intensive backend processes from the WordPress core, plugins, and database.
17
 
18
 
19
  = Features =
22
  * Manual cache clearing
23
  * WP-CLI cache clearing
24
  * Cache expiry
 
 
 
25
  * WebP support (convert images to WebP with [Optimus](https://optimus.io))
26
+ * Mobile support
27
+ * Brotli and Gzip pre-compression support
28
+ * Minification of HTML excluding or including inline CSS and JavaScript
29
+ * Real-time cache size display in the WordPress dashboard
30
  * Custom post type support
31
  * `304 Not Modified` support
32
  * Works perfectly with [Autoptimize](https://wordpress.org/plugins/autoptimize/) and the majority of other third party plugins
33
 
34
 
35
  = How does the caching work? =
36
+ Cache Enabler captures page contents and saves it as a static HTML file on the server's disk. The static HTML file created can be one of several possible cache versions depending on the plugin settings and HTTP request. Accepted static HTML files are then delivered without any database queries or on the fly compression, allowing for a quicker page load.
37
 
38
 
39
  = Documentation =
55
 
56
  == Changelog ==
57
 
58
+ = 1.8.0 =
59
+ * Update `advanced-cache.php` drop-in file handling to improve reliability and compatibility (#283 and #260)
60
+ * Update settings file to be deleted before the `home` option is updated to prevent a leftover settings file (#279)
61
+ * Update `cache_enabler_bypass_cache` filter hook default value to allow a complete override (#277)
62
+ * Update cache size transient to be in real time (#269 and #237)
63
+ * Update cache expiry time to always be a non-negative integer (#265)
64
+ * Update WP-CLI `clear` subcommand (#261)
65
+ * Update required WordPress version from 5.1 to 5.5 (#260)
66
+ * Update plugin upgrade process to improve reliability and compatibility (#260)
67
+ * Update getting the cache file path to improve creating cache files (#256)
68
+ * Update HTML5 doctype check to be less strict (#254)
69
+ * Update permalink structure handling (#263 and #251)
70
+ * Update requirements check to improve notices shown (#260 and #249)
71
+ * Update cache clearing structure to enhance the automatic cache clearing actions (#247)
72
+ * Add WP-Cron event to clear the expired cache on an hourly basis (#281, #268, and #237)
73
+ * Add new cache clearing structure for option actions (#280 and #272)
74
+ * Add cache engine restart support (#278 and #271)
75
+ * Add `constants.php` file to plugin directory to allow constant overrides (#260)
76
+ * Add wildcard cache clearing support (#246)
77
+ * Add Brotli compression support (#243 @nlemoine)
78
+ * Add new cache clearing structure for term actions (#234 @davelit)
79
+ * Add cache iterator to improve cache object handling (#237)
80
+ * Fix WebP URL conversion edge case (#275)
81
+ * Deprecate `cache_enabler_clear_site_cache_by_blog_id` and `cache_enabler_clear_page_cache_by_post_id` action hooks in favor of replacements (#274 and #247)
82
+
83
  = 1.7.2 =
84
  * Update string to be translatable (#235 @timse201)
85
  * Add `cache_enabler_mkdir_mode` filter hook (#233)