Cache Enabler – WordPress Cache - Version 1.5.0

Version Description

  • Update settings file type to PHP instead of JSON (#147)
  • Update settings file(s) storage location (#147)
  • Update plugin activation, deactivation, and uninstall handling (#147)
  • Update HTML minification to also include or exclude inline CSS (#147)
  • Update cache size handling for multisite networks (#147)
  • Update WP_CACHE constant handling (#140)
  • Update cache cleared admin notice (#139)
  • Update admin bar clear cache buttons (#139)
  • Update output buffer timing to start earlier on the init hook instead of template_redirect (#137)
  • Update default cache behavior to not bypass the cache for query strings (#129)
  • Update cache clearing setting for when any post type is published to include all post actions (#142)
  • Update cache clearing setting for post actions to clear the page and/or associated cache by default (#142)
  • Update settings page layout (#129 and #142)
  • Update WebP URL conversion for images with density descriptors (#125)
  • Add cache engine to improve handling and performance (#147)
  • Add cache bypass method for Ajax, REST API, and XMLRPC requests (#147)
  • Add new cache clearing structure for post publish, update, and trash actions (#129)
  • Add post type, taxonomies, author, and date archives to the new associated cache (#129)
  • Add new cache exclusions setting for query strings (#129)
  • Fix cache size file status edge case (#147)
  • Fix WP_CACHE constant not being set edge case (#140)
  • Fix settings file from using unvalidated data (#129)
  • Fix clear URL admin bar button for installations in a subdirectory (#127)
  • Fix WebP URL conversion for installations in a subdirectory (#125)
  • Remove cache clearing publishing action from post sidebar in favor of the new cache clearing structure for post actions (#129)
  • Remove cache clearing setting for WooCommerce stock updates in favor of the new cache clearing structure for post actions (#129)
  • Remove cache inclusions setting for URL query parameters because of the updated default cache behavior for query strings (#129)
Download this release

Release Info

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

Code changes from version 1.4.9 to 1.5.0

advanced-cache.php CHANGED
@@ -3,215 +3,17 @@
3
  * Cache Enabler advanced cache
4
  *
5
  * @since 1.2.0
6
- * @change 1.4.7
7
  */
8
 
9
- // check if request method is GET
10
- if ( ! isset( $_SERVER['REQUEST_METHOD'] ) || $_SERVER['REQUEST_METHOD'] !== 'GET' ) {
11
- return false;
12
- }
13
-
14
- // base path
15
- $path = _file_path();
16
-
17
- // scheme
18
- $scheme = ( ( isset( $_SERVER['HTTPS'] ) && $_SERVER['HTTPS'] !== 'off' ) || $_SERVER['SERVER_PORT'] === '443' ) ? 'https' : 'http';
19
-
20
- // path to cached variants
21
- $path_html = $path . $scheme . '-index.html';
22
- $path_gzip = $path . $scheme . '-index.html.gz';
23
- $path_webp_html = $path . $scheme . '-index-webp.html';
24
- $path_webp_gzip = $path . $scheme . '-index-webp.html.gz';
25
-
26
- // check if cached file exists
27
- if ( ! is_readable( $path_html ) ) {
28
- return false;
29
- }
30
-
31
- // check if there are settings
32
- $settings_file = sprintf(
33
- '%s-%s%s.json',
34
- WP_CONTENT_DIR . '/plugins/cache-enabler/settings/cache-enabler-advcache',
35
- parse_url(
36
- 'http://' . strtolower( $_SERVER['HTTP_HOST'] ),
37
- PHP_URL_HOST
38
- ),
39
- ( is_multisite() && ! SUBDOMAIN_INSTALL ) ? _get_blog_path() : ''
40
- );
41
- $settings = _read_settings( $settings_file );
42
-
43
- // check trailing slash
44
- if ( isset( $settings['permalink_trailing_slash'] ) && $settings['permalink_trailing_slash'] ) {
45
- // if trailing slash is missing, check if we have to bypass the cache to allow a redirect
46
- if ( ! preg_match( '/\/(|\?.*)$/', $_SERVER['REQUEST_URI'] ) ) {
47
- return false;
48
- }
49
- } elseif ( isset( $settings['permalink_trailing_slash'] ) && ! $settings['permalink_trailing_slash'] ) {
50
- // if trailing slash is appended, check if we have to bypass the cache to allow a redirect
51
- if ( preg_match( '/(?!^)\/(|\?.*)$/', $_SERVER['REQUEST_URI'] ) ) {
52
- return false;
53
- }
54
- }
55
-
56
- // if an expiry time is set, check the file against it
57
- if ( isset( $settings['expires'] ) && $settings['expires'] > 0 ) {
58
- $now = time();
59
- $expires_seconds = 3600 * $settings['expires'];
60
-
61
- // check if cached file has expired
62
- if ( ( filemtime( $path_html ) + $expires_seconds ) <= $now ) {
63
- return false;
64
- }
65
- }
66
-
67
- // if a cache timeout is set, check if we have to bypass the cache
68
- if ( ! empty( $settings['cache_timeout'] ) ) {
69
- $now = time();
70
-
71
- // check if timeout has been reached
72
- if ( $settings['cache_timeout'] <= $now ) {
73
- return false;
74
- }
75
- }
76
-
77
- // check cookies
78
- if ( ! empty( $_COOKIE ) ) {
79
- // set regex matching cookies that should cause the cache to be bypassed
80
- if ( ! empty( $settings['excl_cookies'] ) ) {
81
- $cookies_regex = $settings['excl_cookies'];
82
- } else {
83
- $cookies_regex = '/^(wp-postpass|wordpress_logged_in|comment_author)_/';
84
- }
85
- // bypass the cache if an excluded cookie is found
86
- foreach ( $_COOKIE as $key => $value) {
87
- if ( preg_match( $cookies_regex, $key ) ) {
88
- return false;
89
- }
90
- }
91
- }
92
-
93
- // check URL query parameters
94
- if ( ! empty( $_GET ) ) {
95
- // set regex matching URL query parameters that should not cause the cache to be bypassed
96
- if ( ! empty( $settings['incl_parameters'] ) ) {
97
- $parameters_regex = $settings['incl_parameters'];
98
- } else {
99
- $parameters_regex = '/^fbclid|utm_(source|medium|campaign|term|content)$/';
100
- }
101
- // bypass the cache if no included URL query parameters are found
102
- if ( sizeof( preg_grep( $parameters_regex, array_keys( $_GET ), PREG_GREP_INVERT ) ) > 0 ) {
103
- return false;
104
- }
105
- }
106
-
107
- // set X-Cache-Handler response header
108
- header( 'X-Cache-Handler: wp' );
109
-
110
- // get request headers
111
- if ( function_exists( 'apache_request_headers' ) ) {
112
- $headers = apache_request_headers();
113
- $http_if_modified_since = ( isset( $headers[ 'If-Modified-Since' ] ) ) ? $headers[ 'If-Modified-Since' ] : '';
114
- $http_accept = ( isset( $headers[ 'Accept' ] ) ) ? $headers[ 'Accept' ] : '';
115
- $http_accept_encoding = ( isset( $headers[ 'Accept-Encoding' ] ) ) ? $headers[ 'Accept-Encoding' ] : '';
116
- } else {
117
- $http_if_modified_since = ( isset( $_SERVER[ 'HTTP_IF_MODIFIED_SINCE' ] ) ) ? $_SERVER[ 'HTTP_IF_MODIFIED_SINCE' ] : '';
118
- $http_accept = ( isset( $_SERVER[ 'HTTP_ACCEPT' ] ) ) ? $_SERVER[ 'HTTP_ACCEPT' ] : '';
119
- $http_accept_encoding = ( isset( $_SERVER[ 'HTTP_ACCEPT_ENCODING' ] ) ) ? $_SERVER[ 'HTTP_ACCEPT_ENCODING' ] : '';
120
- }
121
-
122
- // check modified since with cached file and return 304 if no difference
123
- if ( $http_if_modified_since && ( strtotime( $http_if_modified_since ) >= filemtime( $path_html ) ) ) {
124
- header( $_SERVER['SERVER_PROTOCOL'] . ' 304 Not Modified', true, 304 );
125
  exit;
126
  }
127
 
128
- // set Last-Modified response header
129
- header( 'Last-Modified: ' . gmdate( 'D, d M Y H:i:s', filemtime( $path_html ) ) . ' GMT' );
130
-
131
- // check webp and deliver gzip webp file if supported
132
- if ( $http_accept && ( strpos( $http_accept, 'webp' ) !== false ) ) {
133
- if ( is_readable( $path_webp_gzip ) ) {
134
- header( 'Content-Encoding: gzip' );
135
- readfile( $path_webp_gzip );
136
- exit;
137
- } elseif ( is_readable( $path_webp_html ) ) {
138
- readfile( $path_webp_html );
139
- exit;
140
- }
141
- }
142
-
143
- // check encoding and deliver gzip file if supported
144
- if ( $http_accept_encoding && ( strpos($http_accept_encoding, 'gzip') !== false ) && is_readable( $path_gzip ) ) {
145
- header( 'Content-Encoding: gzip' );
146
- readfile( $path_gzip );
147
- exit;
148
- }
149
-
150
- // deliver cached file (default)
151
- readfile( $path_html );
152
- exit;
153
-
154
-
155
- // get cached file path
156
- function _file_path() {
157
-
158
- $path = sprintf(
159
- '%s%s%s%s',
160
- WP_CONTENT_DIR . '/cache/cache-enabler',
161
- DIRECTORY_SEPARATOR,
162
- parse_url(
163
- 'http://' . strtolower( $_SERVER['HTTP_HOST'] ),
164
- PHP_URL_HOST
165
- ),
166
- parse_url(
167
- $_SERVER['REQUEST_URI'],
168
- PHP_URL_PATH
169
- )
170
- );
171
-
172
- if ( is_file( $path ) ) {
173
- header( $_SERVER['SERVER_PROTOCOL'] . ' 404 Not Found', true, 404 );
174
- exit;
175
- }
176
-
177
- // add trailing slash
178
- $path = rtrim( $path, '/\\' ) . '/';
179
-
180
- return $path;
181
- }
182
-
183
- // get blog path
184
- function _get_blog_path() {
185
-
186
- // get blog path
187
- $path = explode( '/', $_SERVER['REQUEST_URI'], 3 );
188
- $path = $path[1];
189
 
190
- // check if blog path is empty
191
- if ( ! empty( $path ) ) {
192
- $path = '-' . $path;
193
- }
194
 
195
- return $path;
196
- }
197
-
198
- // read settings file
199
- function _read_settings( $settings_file ) {
200
-
201
- // check if settings file exists
202
- if ( ! file_exists( $settings_file ) ) {
203
- // check if settings file exists for main site in network with subdirectory configuration
204
- if ( is_multisite() && ! SUBDOMAIN_INSTALL && file_exists( str_replace( _get_blog_path(), '', $settings_file ) ) ) {
205
- $settings_file = str_replace( _get_blog_path(), '', $settings_file );
206
- } else {
207
- return array();
208
- }
209
- }
210
-
211
- // check if any errors occur when reading the settings file
212
- if ( ! $settings = json_decode( file_get_contents( $settings_file ), true ) ) {
213
- return array();
214
- }
215
-
216
- return $settings;
217
- }
3
  * Cache Enabler advanced cache
4
  *
5
  * @since 1.2.0
6
+ * @change 1.5.0
7
  */
8
 
9
+ if ( ! defined( 'ABSPATH' ) ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  exit;
11
  }
12
 
13
+ $ce_dir = ( ( defined( 'WP_PLUGIN_DIR' ) ) ? WP_PLUGIN_DIR : WP_CONTENT_DIR . '/plugins' ) . '/cache-enabler';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
 
15
+ require_once $ce_dir . '/inc/cache_enabler_engine.class.php';
16
+ require_once $ce_dir . '/inc/cache_enabler_disk.class.php';
 
 
17
 
18
+ $cache_engine = new Cache_Enabler_Engine;
19
+ $cache_engine->deliver_cache();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
cache-enabler.php CHANGED
@@ -2,89 +2,64 @@
2
  /*
3
  Plugin Name: Cache Enabler
4
  Text Domain: cache-enabler
5
- Description: Simple and fast WordPress disk caching plugin.
6
  Author: KeyCDN
7
  Author URI: https://www.keycdn.com
8
  License: GPLv2 or later
9
- Version: 1.4.9
10
  */
11
 
12
  /*
13
- Copyright (C) 2020 KeyCDN
14
- Copyright (C) 2015 Sergej Müller
15
  This program is free software; you can redistribute it and/or modify
16
  it under the terms of the GNU General Public License as published by
17
  the Free Software Foundation; either version 2 of the License, or
18
  (at your option) any later version.
 
19
  This program is distributed in the hope that it will be useful,
20
  but WITHOUT ANY WARRANTY; without even the implied warranty of
21
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22
  GNU General Public License for more details.
 
23
  You should have received a copy of the GNU General Public License along
24
  with this program; if not, write to the Free Software Foundation, Inc.,
25
  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26
  */
27
 
28
-
29
- // exit
30
- defined( 'ABSPATH' ) || exit;
31
-
32
 
33
  // constants
 
 
34
  define( 'CE_FILE', __FILE__ );
35
- define( 'CE_DIR', dirname( __FILE__ ) );
36
  define( 'CE_BASE', plugin_basename( __FILE__ ) );
37
- define( 'CE_CACHE_DIR', WP_CONTENT_DIR . '/cache/cache-enabler' );
38
- define( 'CE_MIN_WP', '5.1' );
39
 
40
  // hooks
41
- add_action(
42
- 'plugins_loaded',
43
- array(
44
- 'Cache_Enabler',
45
- 'instance',
46
- )
47
- );
48
- register_activation_hook(
49
- __FILE__,
50
- array(
51
- 'Cache_Enabler',
52
- 'on_activation',
53
- )
54
- );
55
- register_deactivation_hook(
56
- __FILE__,
57
- array(
58
- 'Cache_Enabler',
59
- 'on_deactivation',
60
- )
61
- );
62
- register_uninstall_hook(
63
- __FILE__,
64
- array(
65
- 'Cache_Enabler',
66
- 'on_uninstall',
67
- )
68
- );
69
-
70
 
71
- // autoload register
72
- spl_autoload_register( 'cache_autoload' );
73
 
74
- // autoload function
75
- function cache_autoload( $class ) {
76
- if ( in_array( $class, array( 'Cache_Enabler', 'Cache_Enabler_Disk' ) ) ) {
77
- require_once(
78
- sprintf(
79
- '%s/inc/%s.class.php',
80
- CE_DIR,
81
- strtolower( $class )
82
- )
83
  );
84
  }
85
  }
86
 
87
- // load the WP-CLI command
88
  if ( defined( 'WP_CLI' ) && WP_CLI && class_exists( 'WP_CLI' ) ) {
89
  require_once CE_DIR . '/inc/cache_enabler_cli.class.php';
90
  }
2
  /*
3
  Plugin Name: Cache Enabler
4
  Text Domain: cache-enabler
5
+ 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.5.0
10
  */
11
 
12
  /*
13
+ Copyright (C) 2020 KeyCDN
14
+
15
  This program is free software; you can redistribute it and/or modify
16
  it under the terms of the GNU General Public License as published by
17
  the Free Software Foundation; either version 2 of the License, or
18
  (at your option) any later version.
19
+
20
  This program is distributed in the hope that it will be useful,
21
  but WITHOUT ANY WARRANTY; without even the implied warranty of
22
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23
  GNU General Public License for more details.
24
+
25
  You should have received a copy of the GNU General Public License along
26
  with this program; if not, write to the Free Software Foundation, Inc.,
27
  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
28
  */
29
 
30
+ if ( ! defined( 'ABSPATH' ) ) {
31
+ exit;
32
+ }
 
33
 
34
  // constants
35
+ define( 'CE_VERSION', '1.5.0' );
36
+ define( 'CE_MIN_WP', '5.1' );
37
  define( 'CE_FILE', __FILE__ );
 
38
  define( 'CE_BASE', plugin_basename( __FILE__ ) );
39
+ define( 'CE_DIR', __DIR__ );
 
40
 
41
  // hooks
42
+ add_action( 'plugins_loaded', array( 'Cache_Enabler', 'init' ) );
43
+ register_activation_hook( __FILE__, array( 'Cache_Enabler', 'on_activation' ) );
44
+ register_deactivation_hook( __FILE__, array( 'Cache_Enabler', 'on_deactivation' ) );
45
+ register_uninstall_hook( __FILE__, array( 'Cache_Enabler', 'on_uninstall' ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
 
47
+ // register autoload
48
+ spl_autoload_register( 'cache_enabler_autoload' );
49
 
50
+ // load required classes
51
+ function cache_enabler_autoload( $class_name ) {
52
+ // check if classes were loaded in advanced-cache.php
53
+ if ( in_array( $class_name, array( 'Cache_Enabler', 'Cache_Enabler_Engine', 'Cache_Enabler_Disk' ) ) && ! class_exists( $class_name ) ) {
54
+ require_once sprintf(
55
+ '%s/inc/%s.class.php',
56
+ CE_DIR,
57
+ strtolower( $class_name )
 
58
  );
59
  }
60
  }
61
 
62
+ // load WP-CLI command
63
  if ( defined( 'WP_CLI' ) && WP_CLI && class_exists( 'WP_CLI' ) ) {
64
  require_once CE_DIR . '/inc/cache_enabler_cli.class.php';
65
  }
css/settings.css ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* settings */
2
+
3
+ .badge {
4
+ display: inline-block;
5
+ padding: .25em .4em;
6
+ margin-left: .5rem;
7
+ border-radius: .25rem;
8
+ font-size: 10px;
9
+ font-weight: 700;
10
+ text-transform: capitalize;
11
+ white-space: nowrap;
12
+ }
13
+
14
+ .badge--success {
15
+ background-color: #d4edda;
16
+ color: #155724;
17
+ }
18
+
19
+ .subheading {
20
+ font-size: 80% !important;
21
+ font-weight: 600;
22
+ text-transform: uppercase;
23
+ }
24
+
25
+ .subheading:not(:first-of-type) {
26
+ margin-top: 15px !important;
27
+ }
28
+
29
+ .checkbox--form-control {
30
+ height: 23px;
31
+ }
css/settings.min.css ADDED
@@ -0,0 +1 @@
 
1
+ .badge{display:inline-block;padding:.25em .4em;margin-left:.5rem;border-radius:.25rem;font-size:10px;font-weight:700;text-transform:capitalize;white-space:nowrap}.badge--success{background-color:#d4edda;color:#155724}.subheading{font-size:80%!important;font-weight:600;text-transform:uppercase}.subheading:not(:first-of-type){margin-top:15px!important}.checkbox--form-control{height:23px}
inc/cache_enabler.class.php CHANGED
@@ -1,59 +1,24 @@
1
  <?php
2
-
3
-
4
- // exit
5
- defined( 'ABSPATH' ) || exit;
6
-
7
-
8
  /**
9
- * Cache_Enabler
10
  *
11
  * @since 1.0.0
12
  */
13
 
14
- final class Cache_Enabler {
15
-
16
-
17
- /**
18
- * plugin options
19
- *
20
- * @since 1.0.0
21
- * @var array
22
- */
23
-
24
- public static $options;
25
-
26
-
27
- /**
28
- * disk cache object
29
- *
30
- * @since 1.0.0
31
- * @var object
32
- */
33
-
34
- private static $disk;
35
-
36
-
37
- /**
38
- * minify default settings
39
- *
40
- * @since 1.0.0
41
- * @var integer
42
- */
43
-
44
- const MINIFY_DISABLED = 0;
45
- const MINIFY_HTML_ONLY = 1;
46
- const MINIFY_HTML_JS = 2;
47
 
 
48
 
49
  /**
50
- * constructor wrapper
51
  *
52
  * @since 1.0.0
53
- * @change 1.0.0
54
  */
55
 
56
- public static function instance() {
57
 
58
  new self();
59
  }
@@ -63,41 +28,45 @@ final class Cache_Enabler {
63
  * constructor
64
  *
65
  * @since 1.0.0
66
- * @change 1.4.4
67
  */
68
 
69
  public function __construct() {
70
 
71
- // set default vars
72
- self::_set_default_vars();
 
 
73
 
74
  // init hooks
75
- add_action( 'init', array( __CLASS__, 'process_clear_request' ) );
 
76
  add_action( 'init', array( __CLASS__, 'register_textdomain' ) );
77
- add_action( 'init', array( __CLASS__, 'register_publish_hooks' ), 99 );
78
 
79
  // clear cache hooks
80
  add_action( 'ce_clear_post_cache', array( __CLASS__, 'clear_page_cache_by_post_id' ) );
81
- add_action( 'ce_clear_cache', array( __CLASS__, 'clear_total_cache' ) );
82
- add_action( '_core_updated_successfully', array( __CLASS__, 'clear_total_cache' ) );
83
  add_action( 'upgrader_process_complete', array( __CLASS__, 'on_upgrade' ), 10, 2 );
84
- add_action( 'switch_theme', array( __CLASS__, 'clear_total_cache' ) );
85
  add_action( 'activated_plugin', array( __CLASS__, 'on_plugin_activation_deactivation' ), 10, 2 );
86
  add_action( 'deactivated_plugin', array( __CLASS__, 'on_plugin_activation_deactivation' ), 10, 2 );
 
 
87
  add_action( 'wp_trash_post', array( __CLASS__, 'on_trash_post' ) );
88
- add_action( 'permalink_structure_changed', array( __CLASS__, 'clear_total_cache' ) );
 
 
89
  // third party
90
- add_action( 'autoptimize_action_cachepurged', array( __CLASS__, 'clear_total_cache' ) );
91
- add_action( 'woocommerce_product_set_stock', array( __CLASS__, 'on_woocommerce_stock_update' ) );
92
- add_action( 'woocommerce_product_set_stock_status', array( __CLASS__, 'on_woocommerce_stock_update' ) );
93
- add_action( 'woocommerce_variation_set_stock', array( __CLASS__, 'on_woocommerce_stock_update' ) );
94
- add_action( 'woocommerce_variation_set_stock_status', array( __CLASS__, 'on_woocommerce_stock_update' ) );
 
95
 
96
- // advanced cache hooks
97
- add_action( 'permalink_structure_changed', array( __CLASS__, 'create_advcache_settings' ) );
98
- add_action( 'save_post', array( __CLASS__, 'check_future_posts' ) );
99
 
100
- // admin bar hooks
101
  add_action( 'admin_bar_menu', array( __CLASS__, 'add_admin_links' ), 90 );
102
 
103
  // admin interface hooks
@@ -109,24 +78,18 @@ final class Cache_Enabler {
109
  add_action( 'admin_init', array( __CLASS__, 'register_settings' ) );
110
  add_action( 'admin_menu', array( __CLASS__, 'add_settings_page' ) );
111
  add_action( 'admin_enqueue_scripts', array( __CLASS__, 'add_admin_resources' ) );
112
- add_filter( 'plugin_row_meta', array( __CLASS__, 'row_meta' ), 10, 2 );
113
  // comments
114
  add_action( 'transition_comment_status', array( __CLASS__, 'change_comment' ), 10, 3 );
115
  add_action( 'comment_post', array( __CLASS__, 'comment_post' ), 99, 2 );
116
  add_action( 'edit_comment', array( __CLASS__, 'edit_comment' ) );
117
  // dashboard
118
- add_filter( 'dashboard_glance_items', array( __CLASS__, 'add_dashboard_count' ) );
119
- add_action( 'post_submitbox_misc_actions', array( __CLASS__, 'add_clear_dropdown' ) );
120
- add_filter( 'plugin_action_links_' . CE_BASE, array( __CLASS__, 'action_links' ) );
121
- // warnings and notices
122
- add_action( 'admin_notices', array( __CLASS__, 'warning_is_permalink' ) );
123
  add_action( 'admin_notices', array( __CLASS__, 'requirements_check' ) );
124
- // caching hooks
125
- } else {
126
- // comments
127
- add_action( 'pre_comment_approved', array( __CLASS__, 'new_comment' ), 99, 2 );
128
- // output buffer
129
- add_action( 'template_redirect', array( __CLASS__, 'handle_cache' ), 0 );
130
  }
131
  }
132
 
@@ -135,198 +98,119 @@ final class Cache_Enabler {
135
  * activation hook
136
  *
137
  * @since 1.0.0
138
- * @change 1.4.5
139
  *
140
  * @param boolean $network_wide network activated
141
  */
142
 
143
  public static function on_activation( $network_wide ) {
144
 
145
- // activation requirements
146
- self::on_ce_activation_deactivation( 'activated', $network_wide );
147
-
148
- // set WP_CACHE if not already set
149
- if ( defined( 'WP_CACHE' ) && ! WP_CACHE ) {
150
- self::_set_wp_cache();
151
- }
152
 
153
- // copy advanced cache file
154
- copy( CE_DIR . '/advanced-cache.php', WP_CONTENT_DIR . '/advanced-cache.php' );
155
  }
156
 
157
 
158
  /**
159
- * deactivation hook
160
  *
161
- * @since 1.0.0
162
- * @change 1.4.0
163
  *
164
- * @param boolean $network_wide network deactivated
 
165
  */
166
 
167
- public static function on_deactivation( $network_wide ) {
168
-
169
- // deactivation requirements
170
- self::on_ce_activation_deactivation( 'deactivated', $network_wide );
171
 
172
- // unset WP_CACHE
173
- if ( defined( 'WP_CACHE' ) && WP_CACHE ) {
174
- self::_set_wp_cache( false );
175
  }
176
 
177
- // delete advanced cache file
178
- unlink( WP_CONTENT_DIR . '/advanced-cache.php' );
179
- }
180
-
181
-
182
- /**
183
- * Cache Enabler activation and deactivation actions
184
- *
185
- * @since 1.4.0
186
- * @change 1.4.9
187
- *
188
- * @param string $action activated or deactivated
189
- * @param boolean $network_wide network activated or deactivated
190
- */
191
-
192
- public static function on_ce_activation_deactivation( $action, $network_wide ) {
193
-
194
- // network activated
195
- if ( is_multisite() && $network_wide ) {
196
- // blog IDs
197
- $blog_ids = self::_get_blog_ids();
198
-
199
- // switch to each blog in network
200
- foreach ( $blog_ids as $blog_id ) {
201
- switch_to_blog( $blog_id );
202
-
203
- if ( $action === 'activated' ) {
204
- // install requirements
205
- self::_install_backend();
206
- }
207
-
208
- if ( $action === 'deactivated' ) {
209
- // delete advanced cache settings file
210
- Cache_Enabler_Disk::delete_advcache_settings();
211
  }
212
-
213
- // restore blog
214
- restore_current_blog();
215
  }
216
- // site activated
217
- } else {
218
- if ( $action === 'activated') {
219
- // install requirements
220
- self::_install_backend();
221
- }
222
-
223
- if ( $action === 'deactivated') {
224
- // delete advanced cache settings file
225
- Cache_Enabler_Disk::delete_advcache_settings();
226
- }
227
- }
228
-
229
- if ( $action === 'deactivated') {
230
- // clear complete cache
231
- self::clear_total_cache();
232
  }
233
  }
234
 
235
 
236
  /**
237
- * plugin activation and deactivation hooks
238
  *
239
  * @since 1.4.0
240
- * @change 1.4.0
241
  */
242
 
243
- public static function on_plugin_activation_deactivation() {
244
-
245
- // if option enabled clear complete cache on any plugin activation or deactivation
246
- if ( self::$options['clear_on_upgrade'] ) {
247
- self::clear_total_cache();
248
- }
249
- }
250
 
 
 
251
 
252
- /**
253
- * upgrade hook
254
- *
255
- * @since 1.2.3
256
- * @change 1.4.0
257
- *
258
- * @param WP_Upgrader $obj upgrade instance
259
- * @param array $data update data
260
- */
261
 
262
- public static function on_upgrade( $obj, $data ) {
 
263
 
264
- // if option enabled clear complete cache on any plugin update
265
- if ( self::$options['clear_on_upgrade'] ) {
266
- self::clear_total_cache();
267
- }
268
 
269
- // check updated plugins
270
- if ( $data['action'] === 'update' && $data['type'] === 'plugin' && array_key_exists( 'plugins', $data ) ) {
271
- foreach ( (array) $data['plugins'] as $each_plugin ) {
272
- // if Cache Enabler has been updated
273
- if ( $each_plugin === CE_BASE ) {
274
- // update requirements
275
- if ( is_multisite() && is_plugin_active_for_network( CE_BASE ) ) {
276
- $network_wide = true;
277
- self::on_ce_update( $network_wide );
278
- } else {
279
- $network_wide = false;
280
- self::on_ce_update( $network_wide );
281
- }
282
- }
283
- }
284
- }
285
  }
286
 
287
 
288
  /**
289
- * Cache Enabler update actions
290
  *
291
- * @since 1.4.0
292
- * @change 1.4.5
293
  *
294
- * @param boolean $network_wide network activated
295
  */
296
 
297
- public static function on_ce_update( $network_wide ) {
298
-
299
- // delete advanced cache settings file(s) and clear complete cache
300
- self::on_ce_activation_deactivation( 'deactivated', $network_wide );
301
- // decom: delete old advanced cache settings file(s) (1.4.0)
302
- array_map( 'unlink', glob( WP_CONTENT_DIR . '/cache/cache-enabler-advcache-*.json' ) );
303
- // clean: delete incorrect advanced cache settings file(s) that may have been created (1.4.5)
304
- array_map( 'unlink', glob( ABSPATH . '/CE_SETTINGS_PATH-*.json' ) );
305
 
306
- // create advanced cache settings file(s)
307
- self::on_ce_activation_deactivation( 'activated', $network_wide );
308
 
309
- // update advanced cache file that might have changed
310
- copy( CE_DIR . '/advanced-cache.php', WP_CONTENT_DIR . '/advanced-cache.php' );
 
 
 
 
 
311
  }
312
 
313
 
314
  /**
315
- * create or update advanced cache settings
316
  *
317
- * @since 1.4.0
318
- * @change 1.4.3
319
  */
320
 
321
- public static function create_advcache_settings() {
 
 
 
322
 
323
- // ignore results and create advanced cache settings file
324
- self::validate_settings( self::_get_options() );
325
  }
326
 
327
 
328
  /**
329
- * install Cache Enabler on new site in multisite network
330
  *
331
  * @since 1.0.0
332
  * @change 1.4.0
@@ -344,8 +228,8 @@ final class Cache_Enabler {
344
  // switch to blog
345
  switch_to_blog( (int) $new_site->blog_id );
346
 
347
- // install requirements
348
- self::_install_backend();
349
 
350
  // restore blog
351
  restore_current_blog();
@@ -353,174 +237,196 @@ final class Cache_Enabler {
353
 
354
 
355
  /**
356
- * installation requirements
357
  *
358
  * @since 1.0.0
359
- * @change 1.4.0
360
  */
361
 
362
- private static function _install_backend() {
363
 
364
- // add default Cache Enabler option if not already added
365
- add_option( 'cache-enabler', array() );
366
-
367
- // create advanced cache settings file
368
- self::create_advcache_settings();
 
 
 
 
 
 
 
369
  }
370
 
371
 
372
  /**
373
- * uninstall Cache Enabler
374
  *
375
- * @since 1.0.0
376
- * @change 1.4.9
 
 
 
377
  */
378
 
379
- public static function on_uninstall() {
380
 
381
- // network
382
- if ( is_multisite() ) {
383
- // blog IDs
384
- $blog_ids = self::_get_blog_ids();
385
 
386
- // switch to each blog in network
387
- foreach ( $blog_ids as $blog_id ) {
388
- switch_to_blog( $blog_id );
389
- // uninstall requirements
390
- self::_uninstall_backend();
391
- // restore blog
392
- restore_current_blog();
393
- }
394
- // site
395
- } else {
396
- // uninstall requirements
397
- self::_uninstall_backend();
398
  }
399
 
400
- // clear complete cache
401
- self::clear_total_cache();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
402
  }
403
 
404
 
405
  /**
406
- * uninstall Cache Enabler on deleted site in multisite network
407
  *
408
  * @since 1.0.0
409
- * @change 1.4.0
410
  *
411
  * @param WP_Site $old_site old site instance
412
  */
413
 
414
  public static function uninstall_later( $old_site ) {
415
 
416
- // check if network activated
417
- if ( ! is_plugin_active_for_network( CE_BASE ) ) {
418
- return;
419
- }
420
 
421
- // delete advanced cache settings file
422
- Cache_Enabler_Disk::delete_advcache_settings();
423
 
424
- // clear complete cache of deleted site
425
- self::clear_blog_id_cache( (int) $old_site->blog_id );
426
  }
427
 
428
 
429
  /**
430
- * uninstall installation requirements
431
  *
432
  * @since 1.0.0
433
  * @change 1.4.0
434
  */
435
 
436
- private static function _uninstall_backend() {
437
 
438
- // delete Cache Enabler option
439
- delete_option( 'cache-enabler' );
440
  }
441
 
442
 
443
  /**
444
- * set or unset WP_CACHE constant
445
  *
446
- * @since 1.1.1
447
- * @change 1.4.7
448
  *
449
- * @param boolean $wp_cache_value true to set WP_CACHE constant in wp-config.php, false to unset
 
 
 
450
  */
451
 
452
- private static function _set_wp_cache( $wp_cache_value = true ) {
453
-
454
- // get config file
455
- if ( file_exists( ABSPATH . 'wp-config.php' ) ) {
456
- // config file resides in ABSPATH
457
- $wp_config_file = ABSPATH . 'wp-config.php';
458
- } elseif ( @file_exists( dirname( ABSPATH ) . '/wp-config.php' ) && ! @file_exists( dirname( ABSPATH ) . '/wp-settings.php' ) ) {
459
- // config file resides one level above ABSPATH but is not part of another installation
460
- $wp_config_file = dirname( ABSPATH ) . '/wp-config.php';
461
- }
462
 
463
- // check if config file can be written to
464
- if ( is_writable( $wp_config_file ) ) {
465
- // get config file as array
466
- $wp_config = file( $wp_config_file );
467
 
468
- // set Cache Enabler line
469
- if ( $wp_cache_value ) {
470
- $wp_cache_ce_line = "define( 'WP_CACHE', true ); // Added by Cache Enabler" . "\r\n";
471
- } else {
472
- $wp_cache_ce_line = '';
 
 
473
  }
 
 
 
474
 
475
- // search for WP_CACHE constant
476
- $found_wp_cache = false;
477
- foreach ( $wp_config as &$line ) {
478
- if ( preg_match( '/^\s*define\s*\(\s*[\'\"]WP_CACHE[\'\"]\s*,\s*(.*)\s*\);/', $line ) ) {
479
- // found WP_CACHE constant
480
- $found_wp_cache = true;
481
- // check if constant was set by Cache Enabler
482
- if ( preg_match( '/\/\/\sAdded\sby\sCache\sEnabler/', $line ) ) {
483
- // update Cache Enabler line
484
- $line = $wp_cache_ce_line;
485
- }
486
 
487
- break;
488
- }
489
- }
490
 
491
- // add WP_CACHE if not found
492
- if ( ! $found_wp_cache ) {
493
- array_shift( $wp_config );
494
- array_unshift( $wp_config, "<?php\r\n", $wp_cache_ce_line );
495
- }
 
496
 
497
- // write config file
498
- $fh = @fopen( $wp_config_file, 'w' );
499
- foreach( $wp_config as $ln ) {
500
- @fwrite( $fh, $ln );
501
- }
502
 
503
- @fclose( $fh );
 
 
504
  }
505
  }
506
 
507
 
508
  /**
509
- * set default vars
510
  *
511
  * @since 1.0.0
512
- * @change 1.0.0
 
 
513
  */
514
 
515
- private static function _set_default_vars() {
516
 
517
- // get Cache Enabler options
518
- self::$options = self::_get_options();
519
 
520
- // disk cache
521
- if ( Cache_Enabler_Disk::is_permalink() ) {
522
- self::$disk = new Cache_Enabler_Disk;
523
  }
 
 
524
  }
525
 
526
 
@@ -533,11 +439,11 @@ final class Cache_Enabler {
533
  * @return array blog IDs array
534
  */
535
 
536
- private static function _get_blog_ids() {
537
 
538
  global $wpdb;
539
 
540
- return $wpdb->get_col("SELECT blog_id FROM `$wpdb->blogs`");
541
  }
542
 
543
 
@@ -550,257 +456,308 @@ final class Cache_Enabler {
550
  * @return array blog paths array
551
  */
552
 
553
- private static function _get_blog_paths() {
554
 
555
  global $wpdb;
556
 
557
- return $wpdb->get_col("SELECT path FROM `$wpdb->blogs`");
558
  }
559
 
560
 
561
  /**
562
- * get Cache Enabler options
563
  *
564
- * @since 1.0.0
565
- * @change 1.4.0
566
  *
567
- * @return array Cache Enabler options
568
  */
569
 
570
- private static function _get_options() {
571
 
572
- // decom: rename option
573
- $ce_leg = get_option( 'cache' );
574
- if ( ! empty( $ce_leg ) ) {
575
- delete_option( 'cache' );
576
- add_option(
577
- 'cache-enabler',
578
- $ce_leg
579
- );
580
- }
581
 
582
- // decom: rename options
583
- $options = get_option( 'cache-enabler', array() );
584
- // excl_regexp to excl_paths (1.4.0)
585
- if ( ! empty( $options ) && array_key_exists( 'excl_regexp', $options ) ) {
586
- $options['excl_paths'] = $options['excl_regexp'];
587
- unset( $options['excl_regexp'] );
588
- update_option( 'cache-enabler', $options );
589
  }
590
- // incl_attributes to incl_parameters (1.4.0)
591
- if ( ! empty( $options ) && array_key_exists( 'incl_attributes', $options ) ) {
592
- $options['incl_parameters'] = $options['incl_attributes'];
593
- unset( $options['incl_attributes'] );
594
- update_option( 'cache-enabler', $options );
595
  }
596
 
597
- return wp_parse_args(
598
- get_option( 'cache-enabler' ),
599
- array(
600
- 'expires' => 0,
601
- 'clear_on_upgrade' => 0,
602
- 'new_post' => 0,
603
- 'new_comment' => 0,
604
- 'update_product_stock' => 0,
605
- 'compress' => 0,
606
- 'webp' => 0,
607
- 'excl_ids' => '',
608
- 'excl_paths' => '',
609
- 'excl_cookies' => '',
610
- 'incl_parameters' => '',
611
- 'minify_html' => self::MINIFY_DISABLED,
612
- )
613
- );
614
  }
615
 
616
 
617
  /**
618
- * warning if no custom permlink structure
619
  *
620
  * @since 1.0.0
621
- * @change 1.4.5
 
 
622
  */
623
 
624
- public static function warning_is_permalink() {
625
 
626
- if ( ! Cache_Enabler_Disk::is_permalink() && current_user_can( 'manage_options' ) ) {
627
 
628
- show_message(
629
- sprintf(
630
- '<div class="notice notice-error"><p>%s</p></div>',
631
- sprintf(
632
- // translators: 1. Cache Enabler 2. Permalink Settings
633
- esc_html__( 'The %1$s plugin requires a custom permalink structure to start caching properly. Please enable a custom structure in the %2$s.', 'cache-enabler' ),
634
- '<strong>Cache Enabler</strong>',
635
- sprintf(
636
- '<a href="%s">%s</a>',
637
- admin_url( 'options-permalink.php' ),
638
- esc_html__( 'Permalink Settings', 'cache-enabler' )
639
- )
640
- )
641
- )
642
- );
643
  }
 
 
644
  }
645
 
646
 
647
  /**
648
- * add action links
649
  *
650
- * @since 1.0.0
651
- * @change 1.0.0
652
  *
653
- * @param array $data existing links
654
- * @return array $data appended links
655
  */
656
 
657
- public static function action_links( $data ) {
658
 
659
- // check user role
660
- if ( ! current_user_can( 'manage_options' ) ) {
661
- return $data;
662
- }
663
 
664
- return array_merge(
665
- $data,
666
- array(
667
- sprintf(
668
- '<a href="%s">%s</a>',
669
- add_query_arg(
670
- array(
671
- 'page' => 'cache-enabler',
672
- ),
673
- admin_url( 'options-general.php' )
674
- ),
675
- esc_html__( 'Settings', 'cache-enabler' )
676
- )
677
- )
678
- );
679
  }
680
 
681
 
682
  /**
683
- * Cache Enabler meta links
684
  *
685
- * @since 1.0.0
686
- * @change 1.4.0
687
  *
688
- * @param array $input existing links
689
- * @param string $page page
690
- * @return array $data appended links
691
  */
692
 
693
- public static function row_meta( $input, $page ) {
694
 
695
- // check permissions
696
- if ( $page !== CE_BASE ) {
697
- return $input;
698
- }
699
 
700
- return array_merge(
701
- $input,
702
- array(
703
- '<a href="https://www.keycdn.com/support/wordpress-cache-enabler-plugin" target="_blank">Documentation</a>',
704
- )
705
- );
706
  }
707
 
708
 
709
  /**
710
- * add dashboard cache size count
711
  *
712
  * @since 1.0.0
713
- * @change 1.1.0
714
  *
715
- * @param array $items initial array with dashboard items
716
- * @return array $items merged array with dashboard items
717
  */
718
 
719
- public static function add_dashboard_count( $items = array() ) {
720
-
721
- // check user role
722
- if ( ! current_user_can( 'manage_options' ) ) {
723
- return $items;
724
- }
725
 
726
- // get cache size
727
- $size = self::get_cache_size();
 
 
728
 
729
- // display items
730
- $items = array(
731
- sprintf(
732
- '<a href="%s" title="%s">%s %s</a>',
733
- add_query_arg(
734
- array(
735
- 'page' => 'cache-enabler',
736
- ),
737
- admin_url( 'options-general.php' )
738
- ),
739
- esc_html__( 'Disk Cache', 'cache-enabler' ),
740
- ( empty( $size ) ? esc_html__( 'Empty', 'cache-enabler' ) : size_format( $size ) ),
741
- esc_html__( 'Cache Size', 'cache-enabler' )
742
- )
 
 
 
 
743
  );
744
 
745
- return $items;
 
 
 
746
  }
747
 
748
 
749
  /**
750
- * get cache size
751
  *
752
- * @since 1.0.0
753
- * @change 1.0.0
754
  *
755
- * @return integer $size cache size (bytes)
 
756
  */
757
 
758
- public static function get_cache_size() {
 
 
 
 
 
759
 
760
- if ( ! $size = get_transient( 'cache_size' ) ) {
 
 
 
761
 
762
- $size = ( is_object( self::$disk ) ) ? (int) self::$disk->cache_size( CE_CACHE_DIR ) : 0;
 
 
 
763
 
764
- // set transient
765
- set_transient(
766
- 'cache_size',
767
- $size,
768
- 60 * 15
769
- );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
770
  }
771
 
772
- return $size;
773
  }
774
 
775
 
776
  /**
777
- * get blog domain
778
  *
779
- * @since 1.4.0
780
- * @change 1.4.8
781
  *
782
- * @return string $domain current blog domain
 
783
  */
784
 
785
- public static function get_blog_domain() {
786
 
787
- // get current blog domain
788
- $domain = parse_url( get_home_url(), PHP_URL_HOST );
789
-
790
- // check if empty when creating new site in network
791
- if ( is_multisite() && empty( $domain ) ) {
792
- $domain = get_blog_details()->domain;
793
  }
794
 
795
- return $domain;
796
- }
797
-
798
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
799
  /**
800
  * add admin links
801
  *
802
  * @since 1.0.0
803
- * @change 1.4.0
804
  *
805
  * @param object menu properties
806
  *
@@ -817,24 +774,21 @@ final class Cache_Enabler {
817
  // get clear complete cache button title
818
  $title = ( is_multisite() && is_network_admin() ) ? esc_html__( 'Clear Network Cache', 'cache-enabler' ) : esc_html__( 'Clear Cache', 'cache-enabler' );
819
 
820
- // add Clear Cache or Clear Network Cache button in admin bar
821
  $wp_admin_bar->add_menu(
822
  array(
823
  'id' => 'clear-cache',
824
  'href' => wp_nonce_url( add_query_arg( array(
825
  '_cache' => 'cache-enabler',
826
  '_action' => 'clear',
827
- '_cid' => time(),
828
- ) ), '_cache__clear_nonce' ),
829
  'parent' => 'top-secondary',
830
  'title' => '<span class="ab-item">' . $title . '</span>',
831
- 'meta' => array(
832
- 'title' => $title,
833
- ),
834
  )
835
  );
836
 
837
- // add Clear URL Cache button in admin bar
838
  if ( ! is_admin() ) {
839
  $wp_admin_bar->add_menu(
840
  array(
@@ -842,13 +796,10 @@ final class Cache_Enabler {
842
  'href' => wp_nonce_url( add_query_arg( array(
843
  '_cache' => 'cache-enabler',
844
  '_action' => 'clearurl',
845
- '_cid' => time(),
846
- ) ), '_cache__clear_nonce' ),
847
  'parent' => 'top-secondary',
848
  'title' => '<span class="ab-item">' . esc_html__( 'Clear URL Cache', 'cache-enabler' ) . '</span>',
849
- 'meta' => array(
850
- 'title' => esc_html__( 'Clear URL Cache', 'cache-enabler' ),
851
- ),
852
  )
853
  );
854
  }
@@ -856,28 +807,56 @@ final class Cache_Enabler {
856
 
857
 
858
  /**
859
- * process clear request
860
  *
861
  * @since 1.0.0
862
- * @change 1.4.6
863
- *
864
- * @param array $data array of metadata
865
  */
866
 
867
- public static function process_clear_request( $data ) {
868
 
869
- // check if clear request
870
- if ( empty( $_GET['_cache'] ) || empty( $_GET['_action'] ) || $_GET['_cache'] !== 'cache-enabler' && ( $_GET['_action'] !== 'clear' || $_GET['_action'] !== 'clearurl' ) ) {
871
- return;
872
  }
 
 
873
 
874
- // validate clear ID (prevent duplicate processing)
875
- if ( empty( $_GET['_cid'] ) || ! empty( $_COOKIE['cache_enabler_clear_id'] ) && $_COOKIE['cache_enabler_clear_id'] === $_GET['_cid'] ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
876
  return;
877
  }
878
 
879
  // validate nonce
880
- if ( empty( $_GET['_wpnonce'] ) || ! wp_verify_nonce( $_GET['_wpnonce'], '_cache__clear_nonce' ) ) {
881
  return;
882
  }
883
 
@@ -886,34 +865,16 @@ final class Cache_Enabler {
886
  return;
887
  }
888
 
889
- // load if network activated
890
- if ( ! function_exists( 'is_plugin_active_for_network' ) ) {
891
- require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
892
- }
893
-
894
- // set clear ID cookie
895
- setcookie( 'cache_enabler_clear_id', $_GET['_cid'] );
896
-
897
- // set clear URL without query string
898
- $clear_url = preg_replace( '/\?.*/', '', home_url( add_query_arg( null, null ) ) );
899
 
900
  // network activated
901
- if ( is_multisite() && is_plugin_active_for_network( CE_BASE ) ) {
902
  // network admin
903
  if ( is_network_admin() && $_GET['_action'] === 'clear' ) {
904
  // clear complete cache
905
- self::clear_total_cache();
906
-
907
- // clear notice
908
- if ( is_admin() ) {
909
- add_action(
910
- 'network_admin_notices',
911
- array(
912
- __CLASS__,
913
- 'clear_notice',
914
- )
915
- );
916
- }
917
  // site admin
918
  } else {
919
  if ( $_GET['_action'] === 'clearurl' ) {
@@ -921,18 +882,7 @@ final class Cache_Enabler {
921
  self::clear_page_cache_by_url( $clear_url );
922
  } elseif ( $_GET['_action'] === 'clear' ) {
923
  // clear specific site complete cache
924
- self::clear_blog_id_cache( get_current_blog_id() );
925
-
926
- // clear notice
927
- if ( is_admin() ) {
928
- add_action(
929
- 'admin_notices',
930
- array(
931
- __CLASS__,
932
- 'clear_notice',
933
- )
934
- );
935
- }
936
  }
937
  }
938
  // site activated
@@ -942,54 +892,127 @@ final class Cache_Enabler {
942
  self::clear_page_cache_by_url( $clear_url );
943
  } elseif ( $_GET['_action'] === 'clear' ) {
944
  // clear complete cache
945
- self::clear_total_cache();
946
-
947
- // clear notice
948
- if ( is_admin() ) {
949
- add_action(
950
- 'admin_notices',
951
- array(
952
- __CLASS__,
953
- 'clear_notice',
954
- )
955
- );
956
- }
957
  }
958
  }
959
 
960
- if ( ! is_admin() ) {
961
- wp_safe_redirect(
962
- remove_query_arg(
963
- '_cache',
964
- wp_get_referer()
965
- )
966
- );
967
 
968
- exit();
 
 
969
  }
 
 
 
970
  }
971
 
972
 
973
  /**
974
- * notification after clear cache
975
  *
976
  * @since 1.0.0
977
- * @change 1.0.0
978
  *
979
  * @hook mixed user_can_clear_cache
980
  */
981
 
982
- public static function clear_notice() {
983
 
984
- // check if admin
985
  if ( ! is_admin_bar_showing() || ! apply_filters( 'user_can_clear_cache', current_user_can( 'manage_options' ) ) ) {
986
- return false;
987
  }
988
 
989
- echo sprintf(
990
- '<div class="notice notice-success is-dismissible"><p>%s</p></div>',
991
- esc_html__( 'The cache has been cleared.', 'cache-enabler' )
992
- );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
993
  }
994
 
995
 
@@ -1007,9 +1030,9 @@ final class Cache_Enabler {
1007
 
1008
  // check if comment is approved
1009
  if ( $approved === 1 ) {
1010
- // if option enabled clear complete cache on new comment
1011
- if ( self::$options['new_comment'] ) {
1012
- self::clear_total_cache();
1013
  } else {
1014
  self::clear_page_cache_by_post_id( get_comment( $comment_id )->comment_post_ID );
1015
  }
@@ -1028,9 +1051,9 @@ final class Cache_Enabler {
1028
 
1029
  public static function edit_comment( $comment_id ) {
1030
 
1031
- // if option enabled clear complete cache on new comment
1032
- if ( self::$options['new_comment'] ) {
1033
- self::clear_total_cache();
1034
  } else {
1035
  self::clear_page_cache_by_post_id( get_comment( $comment_id )->comment_post_ID );
1036
  }
@@ -1052,9 +1075,9 @@ final class Cache_Enabler {
1052
 
1053
  // check if comment is approved
1054
  if ( $approved === 1 ) {
1055
- // if option enabled clear complete cache on new comment
1056
- if ( self::$options['new_comment'] ) {
1057
- self::clear_total_cache();
1058
  } else {
1059
  self::clear_page_cache_by_post_id( $comment['comment_post_ID'] );
1060
  }
@@ -1079,9 +1102,9 @@ final class Cache_Enabler {
1079
 
1080
  // check if changes occured
1081
  if ( $after_status !== $before_status ) {
1082
- // if option enabled clear complete cache on new comment
1083
- if ( self::$options['new_comment'] ) {
1084
- self::clear_total_cache();
1085
  } else {
1086
  self::clear_page_cache_by_post_id( $comment->comment_post_ID );
1087
  }
@@ -1090,132 +1113,161 @@ final class Cache_Enabler {
1090
 
1091
 
1092
  /**
1093
- * register publish hooks for custom post types
1094
  *
1095
  * @since 1.0.0
1096
- * @change 1.2.3
1097
  */
1098
 
1099
- public static function register_publish_hooks() {
1100
 
1101
- // get post types
1102
- $post_types = get_post_types(
1103
- array(
1104
- 'public' => true,
1105
- )
1106
- );
1107
 
1108
- // check if empty
1109
- if ( empty( $post_types ) ) {
1110
- return;
1111
- }
1112
 
1113
- // post type actions
1114
- foreach ( $post_types as $post_type ) {
1115
- add_action(
1116
- 'publish_' . $post_type,
1117
- array(
1118
- __CLASS__,
1119
- 'publish_post_types',
1120
- ),
1121
- 10,
1122
- 2
1123
- );
1124
- add_action(
1125
- 'publish_future_' . $post_type,
1126
- function( $post_id ) {
1127
- // if option enabled clear complete cache on new post
1128
- if ( self::$options['new_post'] ) {
1129
- self::clear_total_cache();
1130
- } else {
1131
- self::clear_home_page_cache();
1132
- }
1133
- }
1134
- );
1135
- }
1136
  }
1137
 
1138
 
1139
  /**
1140
- * delete post type cache on post updates
1141
  *
1142
- * @since 1.0.0
1143
- * @change 1.4.0
 
 
 
 
 
 
 
 
 
1144
  *
1145
- * @param integer $post_id post ID
 
 
 
1146
  */
1147
 
1148
- public static function publish_post_types( $post_id, $post ) {
1149
 
1150
- // check if post ID or post is empty
1151
- if ( empty( $post_id ) || empty( $post ) ) {
1152
- return;
1153
- }
1154
 
1155
- // check post status
1156
- if ( ! in_array( $post->post_status, array( 'publish', 'future' ) ) ) {
1157
- return;
 
 
 
 
 
1158
  }
 
1159
 
1160
- // clear cache on post publish
1161
- if ( ! isset( $_POST['_clear_post_cache_on_update'] ) && $post->post_date_gmt === $post->post_modified_gmt ) {
1162
 
1163
- // if option enabled clear complete cache on new post
1164
- if ( self::$options['new_post'] ) {
1165
- return self::clear_total_cache();
1166
- } else {
1167
- return self::clear_home_page_cache();
1168
- }
 
 
1169
 
1170
- }
1171
 
1172
- // validate nonce
1173
- if ( ! isset( $_POST['_cache__status_nonce_' . $post_id] ) || ! wp_verify_nonce( $_POST['_cache__status_nonce_' . $post_id], CE_BASE ) ) {
1174
- return;
1175
- }
1176
 
1177
- // validate user role
1178
- if ( ! current_user_can( 'publish_posts' ) ) {
1179
- return;
1180
  }
 
1181
 
1182
- // get clear cache publishing action
1183
- $clear_post_cache = (int) $_POST['_clear_post_cache_on_update'];
1184
 
1185
- // save user metadata
1186
- update_user_meta(
1187
- get_current_user_id(),
1188
- '_clear_post_cache_on_update',
1189
- $clear_post_cache
1190
- );
 
 
1191
 
1192
- // clear cache on post publishing action
1193
- if ( $clear_post_cache ) {
1194
- self::clear_total_cache();
1195
- } else {
1196
- self::clear_page_cache_by_post_id( $post_id );
 
 
 
 
 
 
 
 
 
 
 
 
 
1197
  }
1198
  }
1199
 
1200
 
1201
  /**
1202
- * trash post hook
1203
  *
1204
- * @since 1.4.0
1205
- * @change 1.4.0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1206
  *
1207
  * @param integer $post_id post ID
1208
  */
1209
 
1210
- public static function on_trash_post( $post_id ) {
1211
 
1212
- // if any published post type is sent to the trash clear complete cache
1213
- if ( get_post_status( $post_id ) === 'publish' ) {
1214
- self::clear_total_cache();
1215
- }
1216
 
1217
- // check if cache timeout needs to be recorded
1218
- self::check_future_posts();
 
 
 
 
 
 
 
1219
  }
1220
 
1221
 
@@ -1223,12 +1275,13 @@ final class Cache_Enabler {
1223
  * clear page cache by post ID
1224
  *
1225
  * @since 1.0.0
1226
- * @change 1.4.8
1227
  *
1228
- * @param integer|string $post_id post ID
 
1229
  */
1230
 
1231
- public static function clear_page_cache_by_post_id( $post_id ) {
1232
 
1233
  // validate integer
1234
  if ( ! is_int( $post_id ) ) {
@@ -1241,7 +1294,7 @@ final class Cache_Enabler {
1241
  }
1242
 
1243
  // clear page cache
1244
- self::clear_page_cache_by_url( get_permalink( $post_id ) );
1245
  }
1246
 
1247
 
@@ -1249,26 +1302,21 @@ final class Cache_Enabler {
1249
  * clear page cache by URL
1250
  *
1251
  * @since 1.0.0
1252
- * @change 1.4.8
1253
  *
1254
- * @param string $clear_url full URL of a cached page
1255
- * @param string $clear_type clear all specific cached `page` variants or the entire `dir`
1256
  */
1257
 
1258
  public static function clear_page_cache_by_url( $clear_url, $clear_type = 'page' ) {
1259
 
1260
- // validate string
1261
- if ( ! is_string( $clear_url ) ) {
1262
- return;
1263
- }
1264
-
1265
  // validate URL
1266
  if ( ! filter_var( $clear_url, FILTER_VALIDATE_URL ) ) {
1267
  return;
1268
  }
1269
 
1270
  // clear URL
1271
- call_user_func( array( self::$disk, 'delete_asset' ), $clear_url, $clear_type );
1272
 
1273
  // clear cache by URL post hook
1274
  do_action( 'ce_action_cache_by_url_cleared', $clear_url );
@@ -1276,40 +1324,16 @@ final class Cache_Enabler {
1276
 
1277
 
1278
  /**
1279
- * clear home page cache
1280
- *
1281
- * @since 1.0.7
1282
- * @change 1.4.8
1283
- *
1284
- * @param integer $blog_id blog ID
1285
- */
1286
-
1287
- public static function clear_home_page_cache( $blog_id = null ) {
1288
-
1289
- // set blog ID if given, get current site otherwise
1290
- $blog_id = ( $blog_id ) ? $blog_id : get_current_blog_id();
1291
-
1292
- // get home page URL
1293
- $home_page_url = get_home_url( $blog_id );
1294
-
1295
- // clear home page cache
1296
- self::clear_page_cache_by_url( $home_page_url );
1297
-
1298
- // clear home page cache post hook
1299
- do_action( 'ce_action_home_page_cache_cleared' );
1300
- }
1301
-
1302
-
1303
- /**
1304
- * clear blog ID cache
1305
  *
1306
  * @since 1.4.0
1307
- * @change 1.4.8
1308
  *
1309
- * @param integer|string $blog_id blog ID
 
1310
  */
1311
 
1312
- public static function clear_blog_id_cache( $blog_id ) {
1313
 
1314
  // check if network
1315
  if ( ! is_multisite() ) {
@@ -1327,11 +1351,11 @@ final class Cache_Enabler {
1327
  }
1328
 
1329
  // check if blog ID exists
1330
- if ( ! in_array( $blog_id, self::_get_blog_ids() ) ) {
1331
  return;
1332
  }
1333
 
1334
- // set clear URL
1335
  $clear_url = get_home_url( $blog_id );
1336
 
1337
  // network with subdomain configuration
@@ -1345,12 +1369,9 @@ final class Cache_Enabler {
1345
 
1346
  // main site
1347
  if ( $blog_path === '/' ) {
1348
- // get blog paths
1349
- $blog_paths = self::_get_blog_paths();
1350
- // get blog domain
1351
- $blog_domain = self::get_blog_domain();
1352
- // glob path
1353
- $glob_path = CE_CACHE_DIR . '/' . $blog_domain;
1354
 
1355
  // get cached page paths
1356
  $page_paths = glob( $glob_path . '/*', GLOB_MARK | GLOB_ONLYDIR );
@@ -1364,630 +1385,122 @@ final class Cache_Enabler {
1364
  }
1365
 
1366
  // clear home page cache
1367
- self::clear_home_page_cache( $blog_id );
1368
  // subsite
1369
  } else {
1370
  // clear subsite cache
1371
  self::clear_page_cache_by_url( $clear_url, 'dir' );
1372
  }
1373
  }
 
 
 
 
 
1374
  }
1375
 
1376
 
1377
  /**
1378
- * check if index.php
1379
  *
1380
- * @since 1.0.0
1381
- * @change 1.0.0
1382
  *
1383
- * @return boolean true if index.php
 
1384
  */
1385
 
1386
- private static function _is_index() {
1387
-
1388
- return strtolower( basename( $_SERVER['SCRIPT_NAME'] ) ) !== 'index.php';
1389
- }
1390
 
 
 
1391
 
1392
- /**
1393
- * check if caching is disabled
1394
- *
1395
- * @since 1.4.5
1396
- * @change 1.4.5
1397
- *
1398
- * @return boolean true if WP_CACHE is set to disable caching, false otherwise
1399
- */
1400
-
1401
- private static function _is_wp_cache_disabled() {
1402
-
1403
- if ( defined( 'WP_CACHE' ) && ! WP_CACHE ) {
1404
- return true;
1405
- }
1406
-
1407
- return false;
1408
- }
1409
-
1410
-
1411
- /**
1412
- * check if sitemap
1413
- *
1414
- * @since 1.4.6
1415
- * @change 1.4.6
1416
- *
1417
- * @return boolean true if sitemap, false otherwise
1418
- */
1419
-
1420
- private static function _is_sitemap() {
1421
-
1422
- if ( preg_match( '/\.x(m|s)l$/', $_SERVER['REQUEST_URI'] ) ) {
1423
- return true;
1424
- }
1425
-
1426
- return false;
1427
- }
1428
-
1429
-
1430
-
1431
-
1432
- /**
1433
- * check if trailing slash redirect
1434
- *
1435
- * @since 1.4.7
1436
- * @change 1.4.7
1437
- *
1438
- * @return boolean true if a redirect is required, false otherwise
1439
- */
1440
-
1441
- private static function _is_trailing_slash_redirect() {
1442
-
1443
- // check if trailing slash is set and missing
1444
- if ( self::permalink_structure_has_trailing_slash() ) {
1445
- if ( ! preg_match( '/\/(|\?.*)$/', $_SERVER['REQUEST_URI'] ) ) {
1446
- return true;
1447
- }
1448
- // if trailing slash is not set and appended
1449
- } elseif ( preg_match( '/(?!^)\/(|\?.*)$/', $_SERVER['REQUEST_URI'] ) ) {
1450
- return true;
1451
- }
1452
-
1453
- return false;
1454
- }
1455
-
1456
-
1457
- /**
1458
- * check if mobile
1459
- *
1460
- * @since 1.0.0
1461
- * @change 1.0.0
1462
- *
1463
- * @return boolean true if mobile
1464
- */
1465
-
1466
- private static function _is_mobile() {
1467
-
1468
- return ( strpos( TEMPLATEPATH, 'wptouch' ) || strpos( TEMPLATEPATH, 'carrington' ) || strpos( TEMPLATEPATH, 'jetpack' ) || strpos( TEMPLATEPATH, 'handheld' ) );
1469
- }
1470
-
1471
-
1472
- /**
1473
- * check if there are posts to be published in the future
1474
- *
1475
- * @since 1.2.3
1476
- * @change 1.2.3
1477
- */
1478
-
1479
- public static function check_future_posts() {
1480
-
1481
- $future_posts = new WP_Query( array(
1482
- 'post_status' => array( 'future' ),
1483
- ) );
1484
-
1485
- if ( $future_posts->have_posts() ) {
1486
- $post_dates = array_column( $future_posts->get_posts(), 'post_date' );
1487
- sort( $post_dates );
1488
- // record cache timeout for advanced cache
1489
- Cache_Enabler_Disk::record_advcache_settings( array(
1490
- 'cache_timeout' => strtotime( $post_dates[0] )
1491
- ) );
1492
  } else {
1493
- Cache_Enabler_Disk::delete_advcache_settings( array( 'cache_timeout' ) );
1494
- }
1495
- }
1496
-
1497
-
1498
- /**
1499
- * check to bypass the cache
1500
- *
1501
- * @since 1.0.0
1502
- * @change 1.4.7
1503
- *
1504
- * @return boolean true if exception, false otherwise
1505
- *
1506
- * @hook boolean bypass_cache
1507
- */
1508
-
1509
- private static function _bypass_cache() {
1510
-
1511
- // bypass cache hook
1512
- if ( apply_filters( 'bypass_cache', false ) ) {
1513
- return true;
1514
- }
1515
-
1516
- // check if request method is GET
1517
- if ( ! isset( $_SERVER['REQUEST_METHOD'] ) || $_SERVER['REQUEST_METHOD'] !== 'GET' ) {
1518
- return true;
1519
- }
1520
-
1521
- // check HTTP status code
1522
- if ( http_response_code() !== 200 ) {
1523
- return true;
1524
- }
1525
-
1526
- // check conditional tags
1527
- if ( self::_is_wp_cache_disabled() || self::_is_index() || self::_is_sitemap() || self::_is_trailing_slash_redirect() || is_search() || is_feed() || is_trackback() || is_robots() || is_preview() || post_password_required() ) {
1528
- return true;
1529
- }
1530
-
1531
- // check DONOTCACHEPAGE
1532
- if ( defined( 'DONOTCACHEPAGE' ) && DONOTCACHEPAGE ) {
1533
- return true;
1534
- }
1535
-
1536
- // check mobile request
1537
- if ( self::_is_mobile() ) {
1538
- return true;
1539
- }
1540
-
1541
- // get Cache Enabler options
1542
- $options = self::$options;
1543
-
1544
- // if post ID excluded
1545
- if ( $options['excl_ids'] && is_singular() ) {
1546
- if ( in_array( $GLOBALS['wp_query']->get_queried_object_id(), (array) explode( ',', $options['excl_ids'] ) ) ) {
1547
- return true;
1548
- }
1549
- }
1550
-
1551
- // if page path excluded
1552
- if ( ! empty( $options['excl_paths'] ) ) {
1553
- $url_path = parse_url( $_SERVER['REQUEST_URI'], PHP_URL_PATH );
1554
-
1555
- if ( preg_match( $options['excl_paths'], $url_path ) ) {
1556
- return true;
1557
  }
1558
- }
1559
-
1560
- // check cookies
1561
- if ( ! empty( $_COOKIE ) ) {
1562
- // set regex matching cookies that should cause the cache to be bypassed
1563
- if ( ! empty( $options['excl_cookies'] ) ) {
1564
- $cookies_regex = $options['excl_cookies'];
1565
- } else {
1566
- $cookies_regex = '/^(wp-postpass|wordpress_logged_in|comment_author)_/';
1567
- }
1568
- // bypass the cache if an excluded cookie is found
1569
- foreach ( $_COOKIE as $key => $value) {
1570
- if ( preg_match( $cookies_regex, $key ) ) {
1571
- return true;
1572
- }
1573
- }
1574
- }
1575
-
1576
- // check URL query parameters
1577
- if ( ! empty( $_GET ) ) {
1578
- // set regex matching URL query parameters that should not cause the cache to be bypassed
1579
- if ( ! empty( $options['incl_parameters'] ) ) {
1580
- $parameters_regex = $options['incl_parameters'];
1581
- } else {
1582
- $parameters_regex = '/^fbclid|utm_(source|medium|campaign|term|content)$/';
1583
- }
1584
- // bypass the cache if no included URL query parameters are found
1585
- if ( sizeof( preg_grep( $parameters_regex, array_keys( $_GET ), PREG_GREP_INVERT ) ) > 0 ) {
1586
- return true;
1587
- }
1588
- }
1589
-
1590
- return false;
1591
- }
1592
-
1593
-
1594
- /**
1595
- * minify HTML
1596
- *
1597
- * @since 1.0.0
1598
- * @change 1.0.0
1599
- *
1600
- * @param string $data minify request data
1601
- * @return string $data minify response data
1602
- *
1603
- * @hook array cache_minify_ignore_tags
1604
- */
1605
-
1606
- private static function _minify_cache( $data ) {
1607
-
1608
- // check if disabled
1609
- if ( ! self::$options['minify_html'] ) {
1610
- return $data;
1611
- }
1612
-
1613
- // HTML character limit
1614
- if ( strlen( $data ) > 700000) {
1615
- return $data;
1616
- }
1617
-
1618
- // HTML tags to ignore
1619
- $ignore_tags = (array) apply_filters(
1620
- 'cache_minify_ignore_tags',
1621
- array(
1622
- 'textarea',
1623
- 'pre',
1624
- )
1625
- );
1626
-
1627
- // ignore JS if selected
1628
- if ( self::$options['minify_html'] !== self::MINIFY_HTML_JS ) {
1629
- $ignore_tags = array( 'script' );
1630
- }
1631
-
1632
- // return of no ignore tags
1633
- if ( ! $ignore_tags ) {
1634
- return $data;
1635
- }
1636
-
1637
- // stringify
1638
- $ignore_regex = implode( '|', $ignore_tags );
1639
-
1640
- // regex minification
1641
- $cleaned = preg_replace(
1642
- array(
1643
- '/<!--[^\[><](.*?)-->/s',
1644
- '#(?ix)(?>[^\S ]\s*|\s{2,})(?=(?:(?:[^<]++|<(?!/?(?:' . $ignore_regex . ')\b))*+)(?:<(?>' . $ignore_regex . ')\b|\z))#',
1645
- ),
1646
- array(
1647
- '',
1648
- ' ',
1649
- ),
1650
- $data
1651
- );
1652
-
1653
- // something went wrong
1654
- if ( strlen( $cleaned ) <= 1 ) {
1655
- return $data;
1656
- }
1657
-
1658
- return $cleaned;
1659
- }
1660
-
1661
-
1662
- /**
1663
- * clear complete cache
1664
- *
1665
- * @since 1.0.0
1666
- * @change 1.4.0
1667
- */
1668
-
1669
- public static function clear_total_cache() {
1670
-
1671
- // clear disk cache
1672
- Cache_Enabler_Disk::clear_cache();
1673
-
1674
- // delete transient
1675
- delete_transient( 'cache_size' );
1676
-
1677
- // clear cache post hook
1678
- do_action( 'ce_action_cache_cleared' );
1679
- }
1680
-
1681
-
1682
- /**
1683
- * WooCommerce stock hooks
1684
- *
1685
- * @since 1.3.0
1686
- * @change 1.4.0
1687
- *
1688
- * @param integer|WC_Product $product product ID or product instance
1689
- */
1690
-
1691
- public static function on_woocommerce_stock_update( $product ) {
1692
-
1693
- // get product ID
1694
- if ( is_int( $product ) ) {
1695
- $product_id = $product;
1696
- } else {
1697
- $product_id = $product->get_id();
1698
- }
1699
-
1700
- // if option enabled clear complete cache on product stock update
1701
- if ( self::$options['update_product_stock'] ) {
1702
- self::clear_total_cache();
1703
- } else {
1704
- self::clear_page_cache_by_post_id( $product_id );
1705
- }
1706
- }
1707
-
1708
-
1709
- /**
1710
- * set cache
1711
- *
1712
- * @since 1.0.0
1713
- * @change 1.3.1
1714
- *
1715
- * @param string $data content of a page
1716
- * @return string $data content of a page
1717
- *
1718
- * @hook string cache_enabler_before_store
1719
- */
1720
-
1721
- public static function set_cache( $data ) {
1722
-
1723
- // check if page is empty
1724
- if ( empty( $data ) ) {
1725
- return '';
1726
- }
1727
-
1728
- $data = apply_filters( 'cache_enabler_before_store', $data );
1729
-
1730
- // store as asset
1731
- call_user_func( array( self::$disk, 'store_asset' ), self::_minify_cache( $data ) );
1732
-
1733
- return $data;
1734
- }
1735
-
1736
-
1737
- /**
1738
- * handle cache
1739
- *
1740
- * @since 1.0.0
1741
- * @change 1.4.3
1742
- */
1743
-
1744
- public static function handle_cache() {
1745
-
1746
- // check if cache needs to be bypassed
1747
- if ( self::_bypass_cache() ) {
1748
- return;
1749
- }
1750
-
1751
- // get asset cache status
1752
- $cached = call_user_func( array( self::$disk, 'check_asset' ) );
1753
-
1754
- // check if cache is empty
1755
- if ( empty( $cached ) ) {
1756
- ob_start( 'Cache_Enabler::set_cache' );
1757
- return;
1758
- }
1759
-
1760
- // get cache expiry status
1761
- $expired = call_user_func( array( self::$disk, 'check_expiry' ) );
1762
-
1763
- // check if cache has expired
1764
- if ( $expired ) {
1765
- ob_start( 'Cache_Enabler::set_cache' );
1766
- return;
1767
- }
1768
-
1769
- // return cached asset
1770
- call_user_func( array( self::$disk, 'get_asset' ) );
1771
- }
1772
-
1773
-
1774
- /**
1775
- * add clear option dropdown on post publish widget
1776
- *
1777
- * @since 1.0.0
1778
- * @change 1.4.5
1779
- *
1780
- * @param WP_Post $post post instance
1781
- */
1782
-
1783
- public static function add_clear_dropdown( $post ) {
1784
-
1785
- // on published post/page only
1786
- if ( empty( $GLOBALS['pagenow'] ) || $GLOBALS['pagenow'] !== 'post.php' || empty( $post ) || ! is_object( $post ) || $post->post_status !== 'publish' ) {
1787
- return;
1788
- }
1789
-
1790
- // check user role
1791
- if ( ! current_user_can( 'publish_posts' ) ) {
1792
- return;
1793
- }
1794
-
1795
- // validate nonce
1796
- wp_nonce_field( CE_BASE, '_cache__status_nonce_' . $post->ID );
1797
-
1798
- // get current publishing action
1799
- $current_action = (int) get_user_meta(
1800
- get_current_user_id(),
1801
- '_clear_post_cache_on_update',
1802
- true
1803
- );
1804
-
1805
- // init variables
1806
- $dropdown_options = '';
1807
- $available_options = array(
1808
- esc_html__( 'Page specific', 'cache-enabler' ),
1809
- esc_html__( 'Completely', 'cache-enabler' ),
1810
- );
1811
-
1812
- // set dropdown options
1813
- foreach ( $available_options as $key => $value ) {
1814
- $dropdown_options .= sprintf(
1815
- '<option value="%1$d" %3$s>%2$s</option>',
1816
- $key,
1817
- $value,
1818
- selected( $key, $current_action, false )
1819
- );
1820
- }
1821
-
1822
- // output dropdown
1823
- echo sprintf(
1824
- '<div class="misc-pub-section" style="border-top:1px solid #eee">
1825
- <label for="cache_action">
1826
- %1$s: <strong id="output-cache-action">%2$s</strong>
1827
- </label>
1828
- <a href="#" class="edit-cache-action hide-if-no-js">%3$s</a>
1829
-
1830
- <div class="hide-if-js">
1831
- <select name="_clear_post_cache_on_update" id="cache_action">
1832
- %4$s
1833
- </select>
1834
-
1835
- <a href="#" class="save-cache-action hide-if-no-js button">%5$s</a>
1836
- <a href="#" class="cancel-cache-action hide-if-no-js button-cancel">%6$s</a>
1837
- </div>
1838
- </div>',
1839
- esc_html__( 'Clear cache', 'cache-enabler' ),
1840
- $available_options[ $current_action ],
1841
- esc_html__( 'Edit', 'cache-enabler' ),
1842
- $dropdown_options,
1843
- esc_html__( 'OK', 'cache-enabler' ),
1844
- esc_html__( 'Cancel', 'cache-enabler' )
1845
- );
1846
- }
1847
-
1848
-
1849
- /**
1850
- * enqueue scripts
1851
- *
1852
- * @since 1.0.0
1853
- * @change 1.0.0
1854
- */
1855
-
1856
- public static function add_admin_resources( $hook ) {
1857
-
1858
- // hook check
1859
- if ( $hook !== 'index.php' && $hook !== 'post.php' ) {
1860
- return;
1861
- }
1862
-
1863
- // plugin data
1864
- $plugin_data = get_plugin_data( CE_FILE );
1865
-
1866
- // enqueue scripts
1867
- switch( $hook ) {
1868
-
1869
- case 'post.php':
1870
- wp_enqueue_script(
1871
- 'cache-post',
1872
- plugins_url( 'js/post.js', CE_FILE ),
1873
- array( 'jquery' ),
1874
- $plugin_data['Version'],
1875
- true
1876
- );
1877
- break;
1878
-
1879
- default:
1880
- break;
1881
  }
1882
  }
1883
 
1884
 
1885
- /**
1886
- * add settings page
1887
- *
1888
- * @since 1.0.0
1889
- * @change 1.0.0
1890
- */
1891
-
1892
- public static function add_settings_page() {
1893
-
1894
- add_options_page(
1895
- 'Cache Enabler',
1896
- 'Cache Enabler',
1897
- 'manage_options',
1898
- 'cache-enabler',
1899
- array(
1900
- __CLASS__,
1901
- 'settings_page',
1902
- )
1903
- );
1904
- }
1905
-
1906
-
1907
- /**
1908
- * minify caching dropdown
1909
- *
1910
- * @since 1.0.0
1911
- * @change 1.0.0
1912
- *
1913
- * @return array cache minification options
1914
- */
1915
-
1916
- private static function _minify_select() {
1917
-
1918
- return array(
1919
- self::MINIFY_DISABLED => esc_html__( 'Disabled', 'cache-enabler' ),
1920
- self::MINIFY_HTML_ONLY => esc_html__( 'HTML', 'cache-enabler' ),
1921
- self::MINIFY_HTML_JS => esc_html__( 'HTML and Inline JS', 'cache-enabler' ),
1922
- );
1923
- }
1924
-
1925
-
1926
  /**
1927
  * check plugin requirements
1928
  *
1929
  * @since 1.1.0
1930
- * @change 1.4.8
1931
  */
1932
 
1933
  public static function requirements_check() {
1934
 
1935
- // WordPress version check
1936
  if ( version_compare( $GLOBALS['wp_version'], CE_MIN_WP . 'alpha', '<' ) ) {
1937
- show_message(
 
1938
  sprintf(
1939
- '<div class="notice notice-error"><p>%s</p></div>',
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1940
  sprintf(
1941
- // translators: 1. Cache Enabler 2. WordPress version (e.g. 5.1)
1942
- esc_html__( 'The %1$s plugin is optimized for WordPress %2$s. Please disable the plugin or upgrade your WordPress installation (recommended).', 'cache-enabler' ),
1943
- '<strong>Cache Enabler</strong>',
1944
- CE_MIN_WP
1945
  )
1946
  )
1947
  );
1948
  }
1949
 
1950
- // permission check
1951
- if ( file_exists( CE_CACHE_DIR ) && ! is_writable( CE_CACHE_DIR ) ) {
1952
- show_message(
 
1953
  sprintf(
1954
- '<div class="notice notice-error"><p>%s</p></div>',
 
 
 
 
1955
  sprintf(
1956
- // translators: 1. Cache Enabler 2. 755 3. wp-content/cache 4. file permissions
1957
- esc_html__( 'The %1$s plugin requires write permissions %2$s in %3$s. Please change the %4$s.', 'cache-enabler' ),
1958
- '<strong>Cache Enabler</strong>',
1959
- '<code>755</code>',
1960
- '<code>wp-content/cache</code>',
1961
- sprintf(
1962
- '<a href="%s" target="_blank">%s</a>',
1963
- 'https://wordpress.org/support/article/changing-file-permissions/',
1964
- esc_html__( 'file permissions', 'cache-enabler' )
1965
- )
1966
  )
1967
  )
1968
  );
1969
  }
1970
 
1971
- // autoptimize minification check
1972
- if ( defined( 'AUTOPTIMIZE_PLUGIN_DIR' ) && self::$options['minify_html'] && get_option( 'autoptimize_html', '' ) !== '' ) {
1973
- show_message(
 
1974
  sprintf(
1975
- '<div class="notice notice-error"><p>%s</p></div>',
 
 
1976
  sprintf(
1977
- // translators: 1. Autoptimize 2. Cache Minification 3. Cache Enabler Settings
1978
- esc_html__( 'The %1$s plugin is already active. Please disable %2$s in the %3$s.', 'cache-enabler' ),
1979
- '<strong>Autoptimize</strong>',
1980
- esc_html__( 'Cache Minification', 'cache-enabler' ),
1981
- sprintf(
1982
- '<a href="%s">%s</a>',
1983
- add_query_arg(
1984
- array(
1985
- 'page' => 'cache-enabler',
1986
- ),
1987
- admin_url( 'options-general.php' )
1988
- ),
1989
- esc_html__( 'Cache Enabler Settings', 'cache-enabler' )
1990
- )
1991
  )
1992
  )
1993
  );
@@ -2005,11 +1518,7 @@ final class Cache_Enabler {
2005
  public static function register_textdomain() {
2006
 
2007
  // load translated strings
2008
- load_plugin_textdomain(
2009
- 'cache-enabler',
2010
- false,
2011
- 'cache-enabler/lang'
2012
- );
2013
  }
2014
 
2015
 
@@ -2017,43 +1526,12 @@ final class Cache_Enabler {
2017
  * register settings
2018
  *
2019
  * @since 1.0.0
2020
- * @change 1.0.0
2021
  */
2022
 
2023
  public static function register_settings() {
2024
 
2025
- register_setting(
2026
- 'cache-enabler',
2027
- 'cache-enabler',
2028
- array(
2029
- __CLASS__,
2030
- 'validate_settings',
2031
- )
2032
- );
2033
- }
2034
-
2035
-
2036
- /**
2037
- * permalink structure has trailing slash
2038
- *
2039
- * @since 1.4.3
2040
- * @change 1.4.3
2041
- *
2042
- * @return boolean true if permalink structure has a trailing slash, false otherwise
2043
- */
2044
-
2045
- public static function permalink_structure_has_trailing_slash() {
2046
-
2047
- $permalink_structure = get_option( 'permalink_structure' );
2048
-
2049
- // check permalink structure
2050
- if ( $permalink_structure && preg_match( '/\/$/', $permalink_structure ) ) {
2051
- // permalink structure has a trailing slash
2052
- return true;
2053
- } else {
2054
- // permalink structure does not have a trailing slash
2055
- return false;
2056
- }
2057
  }
2058
 
2059
 
@@ -2061,103 +1539,75 @@ final class Cache_Enabler {
2061
  * validate regex
2062
  *
2063
  * @since 1.2.3
2064
- * @change 1.2.3
2065
  *
2066
- * @param string $re string containing regex
2067
- * @return string string containing regex or empty string if input is invalid
2068
  */
2069
 
2070
- public static function validate_regex( $re ) {
2071
 
2072
- if ( $re !== '' ) {
2073
- if ( ! preg_match( '/^\/.*\/$/', $re ) ) {
2074
- $re = '/' . $re . '/';
2075
  }
2076
 
2077
- if ( @preg_match( $re, null ) === false ) {
2078
  return '';
2079
  }
2080
 
2081
- return sanitize_text_field( $re );
 
 
2082
  }
2083
 
2084
  return '';
2085
  }
2086
 
 
2087
  /**
2088
  * validate settings
2089
  *
2090
  * @since 1.0.0
2091
- * @change 1.4.0
2092
  *
2093
- * @param array $data form data array
2094
- * @return array valid form data array
2095
  */
2096
 
2097
- public static function validate_settings( $data ) {
2098
 
2099
- // check if empty
2100
- if ( empty( $data ) ) {
2101
  return;
2102
  }
2103
 
2104
- // check if cache should be cleared
2105
- if ( isset( $data['clear_cache'] ) && $data['clear_cache'] ) {
2106
- self::clear_total_cache();
2107
- }
2108
-
2109
- // record permalink structure for advanced cache
2110
- if ( self::permalink_structure_has_trailing_slash() ) {
2111
- Cache_Enabler_Disk::record_advcache_settings( array(
2112
- 'permalink_trailing_slash' => true,
2113
- ) );
2114
- } else {
2115
- Cache_Enabler_Disk::record_advcache_settings( array(
2116
- 'permalink_trailing_slash' => false,
2117
- ) );
2118
- }
2119
-
2120
- // record cache expiry for advanced cache
2121
- if ( $data['expires'] > 0 ) {
2122
- Cache_Enabler_Disk::record_advcache_settings( array(
2123
- 'expires' => $data['expires'],
2124
- ) );
2125
- } else {
2126
- Cache_Enabler_Disk::delete_advcache_settings( array( 'expires' ) );
2127
- }
2128
 
2129
- // record cookies cache exclusion for advanced cache
2130
- if ( strlen( $data['excl_cookies'] ) > 0 ) {
2131
- Cache_Enabler_Disk::record_advcache_settings( array(
2132
- 'excl_cookies' => $data['excl_cookies'],
2133
- ) );
2134
- } else {
2135
- Cache_Enabler_Disk::delete_advcache_settings( array( 'excl_cookies' ) );
2136
- }
2137
 
2138
- // record URL query parameters inclusion for advanced cache
2139
- if ( strlen( $data['incl_parameters'] ) > 0 ) {
2140
- Cache_Enabler_Disk::record_advcache_settings( array(
2141
- 'incl_parameters' => $data['incl_parameters'],
2142
- ) );
2143
- } else {
2144
- Cache_Enabler_Disk::delete_advcache_settings( array( 'incl_parameters' ) );
2145
  }
2146
 
2147
- return array(
2148
- 'expires' => (int) $data['expires'],
2149
- 'clear_on_upgrade' => (int) ( ! empty( $data['clear_on_upgrade'] ) ),
2150
- 'new_post' => (int) ( ! empty( $data['new_post'] ) ),
2151
- 'new_comment' => (int) ( ! empty( $data['new_comment'] ) ),
2152
- 'update_product_stock' => (int) ( ! empty( $data['update_product_stock'] ) ),
2153
- 'webp' => (int) ( ! empty( $data['webp'] ) ),
2154
- 'compress' => (int) ( ! empty( $data['compress'] ) ),
2155
- 'excl_ids' => (string) sanitize_text_field( @$data['excl_ids'] ),
2156
- 'excl_paths' => (string) self::validate_regex( @$data['excl_paths'] ),
2157
- 'excl_cookies' => (string) self::validate_regex( @$data['excl_cookies'] ),
2158
- 'incl_parameters' => (string) self::validate_regex( @$data['incl_parameters'] ),
2159
- 'minify_html' => (int) $data['minify_html'],
2160
- );
2161
  }
2162
 
2163
 
@@ -2165,15 +1615,21 @@ final class Cache_Enabler {
2165
  * settings page
2166
  *
2167
  * @since 1.0.0
2168
- * @change 1.4.5
2169
  */
2170
 
2171
  public static function settings_page() {
2172
 
2173
- // check WP_CACHE constant
2174
- if ( self::_is_wp_cache_disabled() ) {
2175
- show_message(
2176
- sprintf(
 
 
 
 
 
 
2177
  '<div class="notice notice-warning"><p>%s</p></div>',
2178
  sprintf(
2179
  // translators: 1. define( 'WP_CACHE', true ); 2. wp-config.php
@@ -2181,21 +1637,15 @@ final class Cache_Enabler {
2181
  "<code>define( 'WP_CACHE', true );</code>",
2182
  '<code>wp-config.php</code>'
2183
  )
2184
- )
2185
- );
2186
- }
2187
-
2188
- ?>
2189
-
2190
- <div id="cache-enabler-settings" class="wrap">
2191
- <h2>
2192
- <?php esc_html_e( 'Cache Enabler Settings', 'cache-enabler' ); ?>
2193
- </h2>
2194
 
2195
- <div class="notice notice-info" style="margin-bottom: 35px;">
2196
  <p>
2197
  <?php
2198
  printf(
 
2199
  esc_html__( 'Combine %s with Cache Enabler for even better WordPress performance and achieve the next level of caching with a CDN.', 'cache-enabler' ),
2200
  '<strong><a href="https://www.keycdn.com?utm_source=wp-admin&utm_medium=plugins&utm_campaign=cache-enabler">KeyCDN</a></strong>'
2201
  );
@@ -2204,66 +1654,96 @@ final class Cache_Enabler {
2204
  </div>
2205
 
2206
  <form method="post" action="options.php">
2207
- <?php settings_fields( 'cache-enabler' ); ?>
2208
- <?php $options = self::_get_options(); ?>
2209
  <table class="form-table">
2210
- <tr valign="top">
2211
- <th scope="row">
2212
- <?php esc_html_e( 'Cache Expiry', 'cache-enabler' ); ?>
2213
- </th>
2214
- <td>
2215
- <input name="cache-enabler[expires]" type="number" id="cache_expires" value="<?php echo esc_attr( $options['expires'] ); ?>" class="small-text" /> <?php esc_html_e( 'hours', 'cache-enabler' ); ?>
2216
- <p class="description"><?php esc_html_e( 'An expiry time of 0 means that the cache never expires.', 'cache-enabler' ); ?></p>
2217
- </td>
2218
- </tr>
2219
  <tr valign="top">
2220
  <th scope="row">
2221
  <?php esc_html_e( 'Cache Behavior', 'cache-enabler' ); ?>
2222
  </th>
2223
  <td>
2224
  <fieldset>
2225
- <label for="cache_clear_on_upgrade">
2226
- <input name="cache-enabler[clear_on_upgrade]" type="checkbox" id="cache_clear_on_upgrade" value="1" <?php checked( '1', $options['clear_on_upgrade'] ); ?> />
2227
- <?php esc_html_e( 'Clear the complete cache if a plugin has been activated, updated, or deactivated.', 'cache-enabler' ); ?>
2228
- <span style="display: inline-block; color: #155724; background-color: #d4edda; font-size: 75%; font-weight: 700; white-space: nowrap; border-radius: .25rem; padding: .25em .4em; margin-left: .5rem;"><?php esc_html_e( 'Updated', 'cache-enabler' ); ?></span>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2229
  </label>
2230
 
2231
  <br />
2232
 
2233
- <label for="cache_new_post">
2234
- <input name="cache-enabler[new_post]" type="checkbox" id="cache_new_post" value="1" <?php checked( '1', $options['new_post'] ); ?> />
2235
- <?php esc_html_e( 'Clear the complete cache if any new post type has been published (instead of only the home page cache).', 'cache-enabler' ); ?>
2236
  </label>
2237
 
2238
  <br />
2239
 
2240
- <label for="cache_new_comment">
2241
- <input name="cache-enabler[new_comment]" type="checkbox" id="cache_new_comment" value="1" <?php checked( '1', $options['new_comment'] ); ?> />
2242
- <?php esc_html_e( 'Clear the complete cache if a new comment has been posted (instead of only the page specific cache).', 'cache-enabler' ); ?>
2243
  </label>
2244
 
2245
  <br />
2246
 
2247
- <?php if ( is_plugin_active( 'woocommerce/woocommerce.php') ): ?>
2248
- <label for="cache_update_product_stock">
2249
- <input name="cache-enabler[update_product_stock]" type="checkbox" id="cache_update_product_stock" value="1" <?php checked( '1', $options['update_product_stock'] ); ?> />
2250
- <?php esc_html_e( 'Clear the complete cache if a product stock has been updated (instead of only the page specific cache).', 'cache-enabler' ); ?>
2251
- <span style="display: inline-block; color: #155724; background-color: #d4edda; font-size: 75%; font-weight: 700; white-space: nowrap; border-radius: .25rem; padding: .25em .4em; margin-left: .5rem;"><?php esc_html_e( 'New', 'cache-enabler' ); ?></span>
 
 
 
 
 
2252
  </label>
2253
 
2254
  <br />
2255
- <?php endif; ?>
2256
 
2257
- <label for="cache_compress">
2258
- <input name="cache-enabler[compress]" type="checkbox" id="cache_compress" value="1" <?php checked( '1', $options['compress'] ); ?> />
2259
- <?php esc_html_e( 'Pre-compression of cached pages. Needs to be disabled if the decoding fails in the web browser.', 'cache-enabler' ); ?>
2260
  </label>
2261
 
2262
  <br />
2263
 
2264
- <label for="cache_webp">
2265
- <input name="cache-enabler[webp]" type="checkbox" id="cache_webp" value="1" <?php checked( '1', $options['webp'] ); ?> />
2266
- <?php printf( esc_html__( 'Create an additional cached version for WebP image support. Convert your images to WebP with %s.', 'cache-enabler' ), '<a href="https://optimus.io" target="_blank">Optimus</a>' ); ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2267
  </label>
2268
  </fieldset>
2269
  </td>
@@ -2275,70 +1755,53 @@ final class Cache_Enabler {
2275
  </th>
2276
  <td>
2277
  <fieldset>
2278
- <label for="cache_excl_ids">
2279
- <input name="cache-enabler[excl_ids]" type="text" id="cache_excl_ids" value="<?php echo esc_attr( $options['excl_ids'] ) ?>" class="regular-text" />
2280
- <p class="description"><?php printf( esc_html__( 'Post and page IDs separated by a %s that should not be cached.', 'cache-enabler' ), '<code>,</code>' ); ?>
2281
- <p><?php esc_html_e( 'Example:', 'cache-enabler' ); ?> <code>2,43,65</code></p>
 
 
 
 
2282
  </p>
 
2283
  </label>
2284
 
2285
  <br />
2286
 
2287
- <label for="cache_excl_paths">
2288
- <input name="cache-enabler[excl_paths]" type="text" id="cache_excl_paths" value="<?php echo esc_attr( $options['excl_paths'] ) ?>" class="regular-text code" />
2289
- <p class="description"><?php esc_html_e( 'A regex matching page paths that should not be cached.', 'cache-enabler' ); ?></p>
2290
- <p><?php esc_html_e( 'Example:', 'cache-enabler' ); ?> <code>/(^\/$|\/robot\/$|^\/2018\/.*\/test\/)/</code></p>
 
2291
  </label>
2292
 
2293
  <br />
2294
 
2295
- <label for="cache_excl_cookies">
2296
- <input name="cache-enabler[excl_cookies]" type="text" id="cache_excl_cookies" value="<?php echo esc_attr( $options['excl_cookies'] ) ?>" class="regular-text code" />
2297
- <p class="description"><?php esc_html_e( 'A regex matching cookies that should cause the cache to be bypassed.', 'cache-enabler' ); ?></p>
2298
- <p><?php esc_html_e( 'Example:', 'cache-enabler' ); ?> <code>/^(wp-postpass|wordpress_logged_in|comment_author|woocommerce_items_in_cart|wp_woocommerce_session)_?/</code></p>
2299
- <p><?php esc_html_e( 'Default if unset:', 'cache-enabler' ); ?> <code>/^(wp-postpass|wordpress_logged_in|comment_author)_/</code></p>
2300
  </label>
2301
- </fieldset>
2302
- </td>
2303
- </tr>
2304
 
2305
- <tr valign="top">
2306
- <th scope="row">
2307
- <?php esc_html_e( 'Cache Inclusions', 'cache-enabler' ); ?>
2308
- </th>
2309
- <td>
2310
- <fieldset>
2311
- <label for="cache_incl_parameters">
2312
- <input name="cache-enabler[incl_parameters]" type="text" id="cache_incl_parameters" value="<?php echo esc_attr( $options['incl_parameters'] ) ?>" class="regular-text code" />
2313
- <p class="description"><?php esc_html_e( 'A regex matching URL query parameters that should not cause the cache to be bypassed.', 'cache-enabler' ); ?></p>
2314
- <p><?php esc_html_e( 'Example:', 'cache-enabler' ); ?> <code>/^fbclid|pk_(source|medium|campaign|kwd|content)$/</code></p>
2315
- <p><?php esc_html_e( 'Default if unset:', 'cache-enabler' ); ?> <code>/^fbclid|utm_(source|medium|campaign|term|content)$/</code><span style="display: inline-block; color: #155724; background-color: #d4edda; font-size: 75%; font-weight: 700; white-space: nowrap; border-radius: .25rem; padding: .25em .4em; margin-left: .5rem;"><?php esc_html_e( 'Updated', 'cache-enabler' ); ?></span></p>
2316
  </label>
2317
  </fieldset>
2318
  </td>
2319
  </tr>
2320
-
2321
- <tr valign="top">
2322
- <th scope="row">
2323
- <?php esc_html_e( 'Cache Minification', 'cache-enabler' ); ?>
2324
- </th>
2325
- <td>
2326
- <label for="cache_minify_html">
2327
- <select name="cache-enabler[minify_html]" id="cache_minify_html">
2328
- <?php foreach ( self::_minify_select() as $key => $value ): ?>
2329
- <option value="<?php echo esc_attr( $key ) ?>" <?php selected( $options['minify_html'], $key ); ?>>
2330
- <?php echo esc_html( $value ) ?>
2331
- </option>
2332
- <?php endforeach; ?>
2333
- </select>
2334
- </label>
2335
- </td>
2336
- </tr>
2337
  </table>
2338
 
2339
  <p class="submit">
2340
  <input type="submit" class="button-secondary" value="<?php esc_html_e( 'Save Changes', 'cache-enabler' ); ?>" />
2341
- <input name="cache-enabler[clear_cache]" type="submit" class="button-primary" value="<?php esc_html_e( 'Save Changes and Clear Cache', 'cache-enabler' ); ?>" />
2342
  </p>
2343
  </form>
2344
  </div>
1
  <?php
 
 
 
 
 
 
2
  /**
3
+ * Cache Enabler base
4
  *
5
  * @since 1.0.0
6
  */
7
 
8
+ if ( ! defined( 'ABSPATH' ) ) {
9
+ exit;
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
  }
28
  * constructor
29
  *
30
  * @since 1.0.0
31
+ * @change 1.5.0
32
  */
33
 
34
  public function __construct() {
35
 
36
+ // check if engine is running
37
+ if ( ! Cache_Enabler_Engine::$started ) {
38
+ new Cache_Enabler_Engine;
39
+ }
40
 
41
  // init hooks
42
+ add_action( 'init', array( 'Cache_Enabler_Engine', 'start_buffering' ) );
43
+ add_action( 'init', array( __CLASS__, 'process_clear_cache_request' ) );
44
  add_action( 'init', array( __CLASS__, 'register_textdomain' ) );
 
45
 
46
  // clear cache hooks
47
  add_action( 'ce_clear_post_cache', array( __CLASS__, 'clear_page_cache_by_post_id' ) );
48
+ add_action( 'ce_clear_cache', array( __CLASS__, 'clear_complete_cache' ) );
49
+ add_action( '_core_updated_successfully', array( __CLASS__, 'clear_complete_cache' ) );
50
  add_action( 'upgrader_process_complete', array( __CLASS__, 'on_upgrade' ), 10, 2 );
51
+ add_action( 'switch_theme', array( __CLASS__, 'clear_complete_cache' ) );
52
  add_action( 'activated_plugin', array( __CLASS__, 'on_plugin_activation_deactivation' ), 10, 2 );
53
  add_action( 'deactivated_plugin', array( __CLASS__, 'on_plugin_activation_deactivation' ), 10, 2 );
54
+ add_action( 'save_post', array( __CLASS__, 'on_save_post' ) );
55
+ add_action( 'post_updated', array( __CLASS__, 'on_post_updated' ), 10, 3 );
56
  add_action( 'wp_trash_post', array( __CLASS__, 'on_trash_post' ) );
57
+ add_action( 'transition_post_status', array( __CLASS__, 'on_transition_post_status' ), 10, 3 );
58
+ add_action( 'pre_comment_approved', array( __CLASS__, 'new_comment' ), 99, 2 );
59
+ add_action( 'permalink_structure_changed', array( __CLASS__, 'clear_complete_cache' ) );
60
  // third party
61
+ add_action( 'autoptimize_action_cachepurged', array( __CLASS__, 'clear_complete_cache' ) );
62
+
63
+ // settings hooks
64
+ add_action( 'permalink_structure_changed', array( __CLASS__, 'update_backend' ) );
65
+ add_action( 'add_option_cache_enabler', array( __CLASS__, 'on_update_backend' ), 10, 2 );
66
+ add_action( 'update_option_cache_enabler', array( __CLASS__, 'on_update_backend' ), 10, 2 );
67
 
 
 
 
68
 
69
+ // admin bar hook
70
  add_action( 'admin_bar_menu', array( __CLASS__, 'add_admin_links' ), 90 );
71
 
72
  // admin interface hooks
78
  add_action( 'admin_init', array( __CLASS__, 'register_settings' ) );
79
  add_action( 'admin_menu', array( __CLASS__, 'add_settings_page' ) );
80
  add_action( 'admin_enqueue_scripts', array( __CLASS__, 'add_admin_resources' ) );
81
+ add_filter( 'plugin_row_meta', array( __CLASS__, 'add_plugin_row_meta' ), 10, 2 );
82
  // comments
83
  add_action( 'transition_comment_status', array( __CLASS__, 'change_comment' ), 10, 3 );
84
  add_action( 'comment_post', array( __CLASS__, 'comment_post' ), 99, 2 );
85
  add_action( 'edit_comment', array( __CLASS__, 'edit_comment' ) );
86
  // dashboard
87
+ add_filter( 'dashboard_glance_items', array( __CLASS__, 'add_dashboard_cache_size' ) );
88
+ add_filter( 'plugin_action_links_' . CE_BASE, array( __CLASS__, 'add_plugin_action_links' ) );
89
+ // notices
 
 
90
  add_action( 'admin_notices', array( __CLASS__, 'requirements_check' ) );
91
+ add_action( 'admin_notices', array( __CLASS__, 'cache_cleared_notice' ) );
92
+ add_action( 'network_admin_notices', array( __CLASS__, 'cache_cleared_notice' ) );
 
 
 
 
93
  }
94
  }
95
 
98
  * activation hook
99
  *
100
  * @since 1.0.0
101
+ * @change 1.5.0
102
  *
103
  * @param boolean $network_wide network activated
104
  */
105
 
106
  public static function on_activation( $network_wide ) {
107
 
108
+ // install backend requirements, triggering the settings file(s) to be created
109
+ self::each_site( $network_wide, 'self::install_backend' );
 
 
 
 
 
110
 
111
+ // configure system files
112
+ Cache_Enabler_Disk::setup();
113
  }
114
 
115
 
116
  /**
117
+ * upgrade hook
118
  *
119
+ * @since 1.2.3
120
+ * @change 1.5.0
121
  *
122
+ * @param WP_Upgrader $obj upgrade instance
123
+ * @param array $data update data
124
  */
125
 
126
+ public static function on_upgrade( $obj, $data ) {
 
 
 
127
 
128
+ // if setting enabled clear complete cache on any plugin update
129
+ if ( Cache_Enabler_Engine::$settings['clear_complete_cache_on_changed_plugin'] ) {
130
+ self::clear_complete_cache();
131
  }
132
 
133
+ // check updated plugins
134
+ if ( $data['action'] === 'update' && $data['type'] === 'plugin' && array_key_exists( 'plugins', $data ) ) {
135
+ foreach ( (array) $data['plugins'] as $plugin_file ) {
136
+ // check if Cache Enabler has been updated
137
+ if ( $plugin_file === CE_BASE ) {
138
+ self::on_ce_update();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
139
  }
 
 
 
140
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
141
  }
142
  }
143
 
144
 
145
  /**
146
+ * Cache Enabler update actions
147
  *
148
  * @since 1.4.0
149
+ * @change 1.5.0
150
  */
151
 
152
+ public static function on_ce_update() {
 
 
 
 
 
 
153
 
154
+ $network_wide = is_multisite();
155
+ $plugin_update = true;
156
 
157
+ // clean system files
158
+ self::each_site( $network_wide, 'Cache_Enabler_Disk::clean' );
 
 
 
 
 
 
 
159
 
160
+ // configure system files
161
+ Cache_Enabler_Disk::setup();
162
 
163
+ // update backend requirements, triggering new settings file(s) to be created
164
+ self::each_site( $network_wide, 'self::update_backend', array( $plugin_update ) );
 
 
165
 
166
+ // clear complete cache
167
+ self::clear_complete_cache();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
168
  }
169
 
170
 
171
  /**
172
+ * deactivation hook
173
  *
174
+ * @since 1.0.0
175
+ * @change 1.5.0
176
  *
177
+ * @param boolean $network_wide network deactivated
178
  */
179
 
180
+ public static function on_deactivation( $network_wide ) {
 
 
 
 
 
 
 
181
 
182
+ // clean system files
183
+ self::each_site( $network_wide, 'Cache_Enabler_Disk::clean' );
184
 
185
+ // clear site cache of deactivated site
186
+ if ( is_multisite() && ! $network_wide ) {
187
+ self::clear_site_cache_by_blog_id( get_current_blog_id() );
188
+ // clear complete cache otherwise
189
+ } else {
190
+ self::clear_complete_cache();
191
+ }
192
  }
193
 
194
 
195
  /**
196
+ * uninstall hook
197
  *
198
+ * @since 1.0.0
199
+ * @change 1.5.0
200
  */
201
 
202
+ public static function on_uninstall() {
203
+
204
+ // uninstall backend requirements
205
+ self::each_site( is_multisite(), 'self::uninstall_backend' );
206
 
207
+ // clear complete cache
208
+ self::clear_complete_cache();
209
  }
210
 
211
 
212
  /**
213
+ * install on new site in multisite network
214
  *
215
  * @since 1.0.0
216
  * @change 1.4.0
228
  // switch to blog
229
  switch_to_blog( (int) $new_site->blog_id );
230
 
231
+ // install backend requirements, triggering the settings file to be created
232
+ self::install_backend();
233
 
234
  // restore blog
235
  restore_current_blog();
237
 
238
 
239
  /**
240
+ * install backend requirements
241
  *
242
  * @since 1.0.0
243
+ * @change 1.5.0
244
  */
245
 
246
+ private static function install_backend() {
247
 
248
+ // if old or current database option exists update backend requirements
249
+ if ( get_option( 'cache-enabler' ) || get_option( 'cache_enabler' ) ) {
250
+ self::update_backend();
251
+ // add default database option otherwise
252
+ } else {
253
+ $default_settings = self::get_default_settings();
254
+ add_option( 'cache_enabler', $default_settings );
255
+ // create settings file if action was not added, like when in activation hook
256
+ if ( ! has_action( 'add_option_cache_enabler' ) ) {
257
+ self::on_update_backend( 'cache_enabler', $default_settings );
258
+ }
259
+ }
260
  }
261
 
262
 
263
  /**
264
+ * update backend requirements
265
  *
266
+ * @since 1.5.0
267
+ * @change 1.5.0
268
+ *
269
+ * @param $plugin_update whether or not an update is in progress
270
+ * @return $new_option_value new or current database option value
271
  */
272
 
273
+ public static function update_backend( $plugin_update = false ) {
274
 
275
+ // check if backend should be updated
276
+ if ( $plugin_update && ! is_plugin_active( CE_BASE ) ) {
277
+ return;
278
+ }
279
 
280
+ // delete user(s) meta key from deleted publishing action (1.5.0)
281
+ delete_metadata( 'user', 0, '_clear_post_cache_on_update', '', true );
282
+
283
+ // rename database option (1.5.0)
284
+ $old_option_value = get_option( 'cache-enabler' );
285
+ if ( $old_option_value !== false ) {
286
+ delete_option( 'cache-enabler' );
287
+ add_option( 'cache_enabler', $old_option_value );
 
 
 
 
288
  }
289
 
290
+ // get defined settings, fall back to empty array if not found
291
+ $old_option_value = get_option( 'cache_enabler', array() );
292
+
293
+ // maybe convert old settings to new settings
294
+ $old_option_value = self::convert_settings( $old_option_value );
295
+
296
+ // update default system settings
297
+ $old_option_value = wp_parse_args( self::get_default_settings( 'system' ), $old_option_value );
298
+
299
+ // merge defined settings into default settings
300
+ $new_option_value = wp_parse_args( $old_option_value, self::get_default_settings() );
301
+
302
+ // if database did not need to be updated create settings file anyway
303
+ if ( ! update_option( 'cache_enabler', $new_option_value ) ) {
304
+ self::on_update_backend( $old_option_value, $new_option_value );
305
+ }
306
+
307
+ return $new_option_value;
308
+ }
309
+
310
+
311
+ /**
312
+ * add or update database option hook
313
+ *
314
+ * @since 1.5.0
315
+ * @change 1.5.0
316
+ *
317
+ * @param mixed $option old database option value or name of the option to add
318
+ * @param mixed $new_option_value new database option value
319
+ */
320
+
321
+ public static function on_update_backend( $option, $new_option_value ) {
322
+
323
+ Cache_Enabler_Disk::create_settings_file( $new_option_value );
324
  }
325
 
326
 
327
  /**
328
+ * uninstall on deleted site in multisite network
329
  *
330
  * @since 1.0.0
331
+ * @change 1.5.0
332
  *
333
  * @param WP_Site $old_site old site instance
334
  */
335
 
336
  public static function uninstall_later( $old_site ) {
337
 
338
+ $delete_cache_size_transient = false;
 
 
 
339
 
340
+ // clean system files
341
+ Cache_Enabler_Disk::clean();
342
 
343
+ // clear site cache of deleted site
344
+ self::clear_site_cache_by_blog_id( (int) $old_site->blog_id, $delete_cache_size_transient );
345
  }
346
 
347
 
348
  /**
349
+ * uninstall backend requirements
350
  *
351
  * @since 1.0.0
352
  * @change 1.4.0
353
  */
354
 
355
+ private static function uninstall_backend() {
356
 
357
+ // delete database option
358
+ delete_option( 'cache_enabler' );
359
  }
360
 
361
 
362
  /**
363
+ * enter each site
364
  *
365
+ * @since 1.5.0
366
+ * @change 1.5.0
367
  *
368
+ * @param boolean $network whether or not each site in network
369
+ * @param string $callback callback function
370
+ * @param array $callback_params callback function parameters
371
+ * @return array $callback_return returned value(s) from callback function
372
  */
373
 
374
+ private static function each_site( $network, $callback, $callback_params = array() ) {
 
 
 
 
 
 
 
 
 
375
 
376
+ $callback_return = array();
 
 
 
377
 
378
+ if ( $network ) {
379
+ $blog_ids = self::get_blog_ids();
380
+ // switch to each site in network
381
+ foreach ( $blog_ids as $blog_id ) {
382
+ switch_to_blog( $blog_id );
383
+ $callback_return[] = (int) call_user_func_array( $callback, $callback_params );
384
+ restore_current_blog();
385
  }
386
+ } else {
387
+ $callback_return[] = (int) call_user_func_array( $callback, $callback_params );
388
+ }
389
 
390
+ return $callback_return;
391
+ }
 
 
 
 
 
 
 
 
 
392
 
 
 
 
393
 
394
+ /**
395
+ * plugin activation and deactivation hooks
396
+ *
397
+ * @since 1.4.0
398
+ * @change 1.4.0
399
+ */
400
 
401
+ public static function on_plugin_activation_deactivation() {
 
 
 
 
402
 
403
+ // if setting enabled clear complete cache on any plugin activation or deactivation
404
+ if ( Cache_Enabler_Engine::$settings['clear_complete_cache_on_changed_plugin'] ) {
405
+ self::clear_complete_cache();
406
  }
407
  }
408
 
409
 
410
  /**
411
+ * get settings from database
412
  *
413
  * @since 1.0.0
414
+ * @change 1.5.0
415
+ *
416
+ * @return array $settings current settings from database
417
  */
418
 
419
+ public static function get_settings() {
420
 
421
+ // get database option value
422
+ $settings = get_option( 'cache_enabler' );
423
 
424
+ // if database option does not exist or settings are outdated
425
+ if ( $settings === false || isset( $settings['version'] ) && $settings['version'] !== CE_VERSION ) {
426
+ $settings = self::update_backend();
427
  }
428
+
429
+ return $settings;
430
  }
431
 
432
 
439
  * @return array blog IDs array
440
  */
441
 
442
+ private static function get_blog_ids() {
443
 
444
  global $wpdb;
445
 
446
+ return $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs" );
447
  }
448
 
449
 
456
  * @return array blog paths array
457
  */
458
 
459
+ private static function get_blog_paths() {
460
 
461
  global $wpdb;
462
 
463
+ return $wpdb->get_col( "SELECT path FROM $wpdb->blogs" );
464
  }
465
 
466
 
467
  /**
468
+ * get permalink structure
469
  *
470
+ * @since 1.5.0
471
+ * @change 1.5.0
472
  *
473
+ * @return string permalink structure
474
  */
475
 
476
+ private static function get_permalink_structure() {
477
 
478
+ // get permalink structure
479
+ $permalink_structure = get_option( 'permalink_structure' );
 
 
 
 
 
 
 
480
 
481
+ // permalink structure is custom and has a trailing slash
482
+ if ( $permalink_structure && preg_match( '/\/$/', $permalink_structure ) ) {
483
+ return 'has_trailing_slash';
 
 
 
 
484
  }
485
+
486
+ // permalink structure is custom and does not have a trailing slash
487
+ if ( $permalink_structure && ! preg_match( '/\/$/', $permalink_structure ) ) {
488
+ return 'no_trailing_slash';
 
489
  }
490
 
491
+ // permalink structure is not custom
492
+ if ( empty( $permalink_structure ) ) {
493
+ return 'plain';
494
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
495
  }
496
 
497
 
498
  /**
499
+ * get cache size
500
  *
501
  * @since 1.0.0
502
+ * @change 1.5.0
503
+ *
504
+ * @return integer $size cache size (bytes)
505
  */
506
 
507
+ public static function get_cache_size() {
508
 
509
+ $cache_size = get_transient( self::get_cache_size_transient_name() );
510
 
511
+ if ( ! $cache_size ) {
512
+ $cache_size = Cache_Enabler_Disk::cache_size();
513
+ set_transient( self::get_cache_size_transient_name(), $cache_size, MINUTE_IN_SECONDS * 15 );
 
 
 
 
 
 
 
 
 
 
 
 
514
  }
515
+
516
+ return $cache_size;
517
  }
518
 
519
 
520
  /**
521
+ * get the cache size transient name
522
  *
523
+ * @since 1.5.0
524
+ * @change 1.5.0
525
  *
526
+ * @param integer $blog_id blog ID
527
+ * @return string $transient_name transient name
528
  */
529
 
530
+ private static function get_cache_size_transient_name( $blog_id = null ) {
531
 
532
+ // set blog ID if provided, get current blog ID otherwise
533
+ $blog_id = ( $blog_id ) ? $blog_id : get_current_blog_id();
 
 
534
 
535
+ $transient_name = 'cache_enabler_cache_size_' . $blog_id;
536
+
537
+ return $transient_name;
 
 
 
 
 
 
 
 
 
 
 
 
538
  }
539
 
540
 
541
  /**
542
+ * get the cache cleared transient name used for the clear notice
543
  *
544
+ * @since 1.5.0
545
+ * @change 1.5.0
546
  *
547
+ * @return string $transient_name transient name
 
 
548
  */
549
 
550
+ private static function get_cache_cleared_transient_name() {
551
 
552
+ $transient_name = 'cache_enabler_cache_cleared_' . get_current_user_id();
 
 
 
553
 
554
+ return $transient_name;
 
 
 
 
 
555
  }
556
 
557
 
558
  /**
559
+ * get default settings
560
  *
561
  * @since 1.0.0
562
+ * @change 1.5.0
563
  *
564
+ * @param string $settings_type default `system` settings
565
+ * @return array $system_default_settings|$default_settings system or all default settings
566
  */
567
 
568
+ private static function get_default_settings( $settings_type = null ) {
 
 
 
 
 
569
 
570
+ $system_default_settings = array(
571
+ 'version' => (string) CE_VERSION,
572
+ 'permalink_structure' => (string) self::get_permalink_structure(),
573
+ );
574
 
575
+ if ( $settings_type === 'system' ) {
576
+ return $system_default_settings;
577
+ }
578
+
579
+ $user_default_settings = array(
580
+ 'cache_expires' => 0,
581
+ 'cache_expiry_time' => 0,
582
+ 'clear_complete_cache_on_saved_post' => 0,
583
+ 'clear_complete_cache_on_new_comment' => 0,
584
+ 'clear_complete_cache_on_changed_plugin' => 0,
585
+ 'compress_cache' => 0,
586
+ 'convert_image_urls_to_webp' => 0,
587
+ 'excluded_post_ids' => '',
588
+ 'excluded_page_paths' => '',
589
+ 'excluded_query_strings' => '',
590
+ 'excluded_cookies' => '',
591
+ 'minify_html' => 0,
592
+ 'minify_inline_css_js' => 0,
593
  );
594
 
595
+ // merge default settings
596
+ $default_settings = wp_parse_args( $user_default_settings, $system_default_settings );
597
+
598
+ return $default_settings;
599
  }
600
 
601
 
602
  /**
603
+ * convert settings to new structure
604
  *
605
+ * @since 1.5.0
606
+ * @change 1.5.0
607
  *
608
+ * @param array $settings settings
609
+ * @return array $settings converted settings if applicable, unchanged otherwise
610
  */
611
 
612
+ private static function convert_settings( $settings ) {
613
+
614
+ // check if there are any settings to convert
615
+ if ( empty( $settings ) ) {
616
+ return $settings;
617
+ }
618
 
619
+ // updated settings
620
+ if ( isset( $settings['expires'] ) && $settings['expires'] > 0 ) {
621
+ $settings['cache_expires'] = 1;
622
+ }
623
 
624
+ if ( isset( $settings['minify_html'] ) && $settings['minify_html'] === 2 ) {
625
+ $settings['minify_html'] = 1;
626
+ $settings['minify_inline_css_js'] = 1;
627
+ }
628
 
629
+ // renamed or removed settings
630
+ $settings_names = array(
631
+ 'expires' => 'cache_expiry_time',
632
+ 'new_post' => 'clear_complete_cache_on_saved_post',
633
+ 'update_product_stock' => '', // deprecated in 1.5.0
634
+ 'new_comment' => 'clear_complete_cache_on_new_comment',
635
+ 'clear_on_upgrade' => 'clear_complete_cache_on_changed_plugin',
636
+ 'compress' => 'compress_cache',
637
+ 'webp' => 'convert_image_urls_to_webp',
638
+ 'excl_ids' => 'excluded_post_ids',
639
+ 'excl_regexp' => 'excluded_page_paths', // < 1.4.0
640
+ 'excl_paths' => 'excluded_page_paths',
641
+ 'excl_cookies' => 'excluded_cookies',
642
+ 'incl_parameters' => '', // deprecated in 1.5.0
643
+ );
644
+
645
+ foreach ( $settings_names as $old_name => $new_name ) {
646
+ if ( array_key_exists( $old_name, $settings ) ) {
647
+ if ( ! empty( $new_name ) ) {
648
+ $settings[ $new_name ] = $settings[ $old_name ];
649
+ }
650
+ unset( $settings[ $old_name ] );
651
+ }
652
  }
653
 
654
+ return $settings;
655
  }
656
 
657
 
658
  /**
659
+ * add plugin action links in the plugins list table
660
  *
661
+ * @since 1.0.0
662
+ * @change 1.5.0
663
  *
664
+ * @param array $action_links action links
665
+ * @return array $action_links updated action links if applicable, unchanged otherwise
666
  */
667
 
668
+ public static function add_plugin_action_links( $action_links ) {
669
 
670
+ // check user role
671
+ if ( ! current_user_can( 'manage_options' ) ) {
672
+ return $action_links;
 
 
 
673
  }
674
 
675
+ // append action link
676
+ $action_links = wp_parse_args(
677
+ array(
678
+ sprintf(
679
+ '<a href="%s">%s</a>',
680
+ admin_url( 'options-general.php?page=cache-enabler' ),
681
+ esc_html__( 'Settings', 'cache-enabler' )
682
+ )
683
+ ),
684
+ $action_links
685
+ );
686
+
687
+ return $action_links;
688
+ }
689
+
690
+
691
+ /**
692
+ * add plugin metadata in the plugins list table
693
+ *
694
+ * @since 1.0.0
695
+ * @change 1.5.0
696
+ *
697
+ * @param array $plugin_meta plugin metadata, including the version, author, author URI, and plugin URI
698
+ * @param string $plugin_file path to the plugin file relative to the plugins directory
699
+ * @return array $plugin_meta updated action links if applicable, unchanged otherwise
700
+ */
701
+
702
+ public static function add_plugin_row_meta( $plugin_meta, $plugin_file ) {
703
+
704
+ // check if Cache Enabler row
705
+ if ( $plugin_file !== CE_BASE ) {
706
+ return $plugin_meta;
707
+ }
708
+
709
+ // append metadata
710
+ $plugin_meta = wp_parse_args(
711
+ array(
712
+ '<a href="https://www.keycdn.com/support/wordpress-cache-enabler-plugin" target="_blank" rel="nofollow noopener">Documentation</a>',
713
+ ),
714
+ $plugin_meta
715
+ );
716
+
717
+ return $plugin_meta;
718
+ }
719
+
720
+
721
+ /**
722
+ * add dashboard cache size count
723
+ *
724
+ * @since 1.0.0
725
+ * @change 1.5.0
726
+ *
727
+ * @param array $items initial array with dashboard items
728
+ * @return array $items merged array with dashboard items
729
+ */
730
+
731
+ public static function add_dashboard_cache_size( $items = array() ) {
732
+
733
+ // check user role
734
+ if ( ! current_user_can( 'manage_options' ) ) {
735
+ return $items;
736
+ }
737
+
738
+ // get cache size
739
+ $size = self::get_cache_size();
740
+
741
+ // display items
742
+ $items = array(
743
+ sprintf(
744
+ '<a href="%s" title="%s">%s %s</a>',
745
+ admin_url( 'options-general.php?page=cache-enabler' ),
746
+ esc_html__( 'Refreshes every 15 minutes', 'cache-enabler' ),
747
+ ( empty( $size ) ) ? esc_html__( 'Empty', 'cache-enabler' ) : size_format( $size ),
748
+ esc_html__( 'Cache Size', 'cache-enabler' )
749
+ )
750
+ );
751
+
752
+ return $items;
753
+ }
754
+
755
+
756
  /**
757
  * add admin links
758
  *
759
  * @since 1.0.0
760
+ * @change 1.5.0
761
  *
762
  * @param object menu properties
763
  *
774
  // get clear complete cache button title
775
  $title = ( is_multisite() && is_network_admin() ) ? esc_html__( 'Clear Network Cache', 'cache-enabler' ) : esc_html__( 'Clear Cache', 'cache-enabler' );
776
 
777
+ // add "Clear Network Cache" or "Clear Cache" button in admin bar
778
  $wp_admin_bar->add_menu(
779
  array(
780
  'id' => 'clear-cache',
781
  'href' => wp_nonce_url( add_query_arg( array(
782
  '_cache' => 'cache-enabler',
783
  '_action' => 'clear',
784
+ ) ), 'cache_enabler_clear_cache_nonce' ),
 
785
  'parent' => 'top-secondary',
786
  'title' => '<span class="ab-item">' . $title . '</span>',
787
+ 'meta' => array( 'title' => $title ),
 
 
788
  )
789
  );
790
 
791
+ // add "Clear URL Cache" button in admin bar
792
  if ( ! is_admin() ) {
793
  $wp_admin_bar->add_menu(
794
  array(
796
  'href' => wp_nonce_url( add_query_arg( array(
797
  '_cache' => 'cache-enabler',
798
  '_action' => 'clearurl',
799
+ ) ), 'cache_enabler_clear_cache_nonce' ),
 
800
  'parent' => 'top-secondary',
801
  'title' => '<span class="ab-item">' . esc_html__( 'Clear URL Cache', 'cache-enabler' ) . '</span>',
802
+ 'meta' => array( 'title' => esc_html__( 'Clear URL Cache', 'cache-enabler' ) ),
 
 
803
  )
804
  );
805
  }
807
 
808
 
809
  /**
810
+ * enqueue styles and scripts
811
  *
812
  * @since 1.0.0
813
+ * @change 1.5.0
 
 
814
  */
815
 
816
+ public static function add_admin_resources( $hook ) {
817
 
818
+ // settings page
819
+ if ( $hook === 'settings_page_cache-enabler' ) {
820
+ wp_enqueue_style( 'cache-enabler-settings', plugins_url( 'css/settings.min.css', CE_FILE ), array(), CE_VERSION );
821
  }
822
+ }
823
+
824
 
825
+ /**
826
+ * add settings page
827
+ *
828
+ * @since 1.0.0
829
+ * @change 1.0.0
830
+ */
831
+
832
+ public static function add_settings_page() {
833
+
834
+ add_options_page(
835
+ 'Cache Enabler',
836
+ 'Cache Enabler',
837
+ 'manage_options',
838
+ 'cache-enabler',
839
+ array( __CLASS__, 'settings_page' )
840
+ );
841
+ }
842
+
843
+
844
+ /**
845
+ * process clear cache request
846
+ *
847
+ * @since 1.0.0
848
+ * @change 1.5.0
849
+ */
850
+
851
+ public static function process_clear_cache_request() {
852
+
853
+ // check if clear cache request
854
+ if ( empty( $_GET['_cache'] ) || empty( $_GET['_action'] ) || $_GET['_cache'] !== 'cache-enabler' && ( $_GET['_action'] !== 'clear' || $_GET['_action'] !== 'clearurl' ) ) {
855
  return;
856
  }
857
 
858
  // validate nonce
859
+ if ( empty( $_GET['_wpnonce'] ) || ! wp_verify_nonce( $_GET['_wpnonce'], 'cache_enabler_clear_cache_nonce' ) ) {
860
  return;
861
  }
862
 
865
  return;
866
  }
867
 
868
+ // set clear URL without query string and check if installation is in a subdirectory
869
+ $installation_dir = parse_url( home_url(), PHP_URL_PATH );
870
+ $clear_url = str_replace( $installation_dir, '', home_url() ) . preg_replace( '/\?.*/', '', $_SERVER['REQUEST_URI'] );
 
 
 
 
 
 
 
871
 
872
  // network activated
873
+ if ( is_multisite() ) {
874
  // network admin
875
  if ( is_network_admin() && $_GET['_action'] === 'clear' ) {
876
  // clear complete cache
877
+ self::clear_complete_cache();
 
 
 
 
 
 
 
 
 
 
 
878
  // site admin
879
  } else {
880
  if ( $_GET['_action'] === 'clearurl' ) {
882
  self::clear_page_cache_by_url( $clear_url );
883
  } elseif ( $_GET['_action'] === 'clear' ) {
884
  // clear specific site complete cache
885
+ self::clear_site_cache_by_blog_id( get_current_blog_id() );
 
 
 
 
 
 
 
 
 
 
 
886
  }
887
  }
888
  // site activated
892
  self::clear_page_cache_by_url( $clear_url );
893
  } elseif ( $_GET['_action'] === 'clear' ) {
894
  // clear complete cache
895
+ self::clear_complete_cache();
 
 
 
 
 
 
 
 
 
 
 
896
  }
897
  }
898
 
899
+ // redirect to same page
900
+ wp_safe_redirect( wp_get_referer() );
 
 
 
 
 
901
 
902
+ // set transient for clear notice
903
+ if ( is_admin() ) {
904
+ set_transient( self::get_cache_cleared_transient_name(), 1 );
905
  }
906
+
907
+ // clear cache request completed
908
+ exit;
909
  }
910
 
911
 
912
  /**
913
+ * admin notice after cache has been cleared
914
  *
915
  * @since 1.0.0
916
+ * @change 1.5.0
917
  *
918
  * @hook mixed user_can_clear_cache
919
  */
920
 
921
+ public static function cache_cleared_notice() {
922
 
923
+ // check user role
924
  if ( ! is_admin_bar_showing() || ! apply_filters( 'user_can_clear_cache', current_user_can( 'manage_options' ) ) ) {
925
+ return;
926
  }
927
 
928
+ if ( get_transient( self::get_cache_cleared_transient_name() ) ) {
929
+ echo sprintf(
930
+ '<div class="notice notice-success is-dismissible"><p><strong>%s</strong></p></div>',
931
+ ( is_multisite() && is_network_admin() ) ? esc_html__( 'Network cache cleared.', 'cache-enabler' ) : esc_html__( 'Cache cleared.', 'cache-enabler' )
932
+ );
933
+
934
+ delete_transient( self::get_cache_cleared_transient_name() );
935
+ }
936
+ }
937
+
938
+
939
+ /**
940
+ * save post hook
941
+ *
942
+ * @since 1.5.0
943
+ * @change 1.5.0
944
+ *
945
+ * @param integer $post_id post ID
946
+ */
947
+
948
+ public static function on_save_post( $post_id ) {
949
+
950
+ // if any published post type is created or updated
951
+ if ( get_post_status( $post_id ) === 'publish' ) {
952
+ self::clear_cache_on_post_save( $post_id );
953
+ }
954
+ }
955
+
956
+
957
+ /**
958
+ * post updated hook
959
+ *
960
+ * @since 1.5.0
961
+ * @change 1.5.0
962
+ *
963
+ * @param integer $post_id post ID
964
+ * @param WP_Post $post_after post instance following the update
965
+ * @param WP_Post $post_before post instance before the update
966
+ */
967
+
968
+ public static function on_post_updated( $post_id, $post_after, $post_before ) {
969
+
970
+ // if setting disabled and any published post type author changes
971
+ if ( $post_before->post_author !== $post_after->post_author ) {
972
+ if ( ! Cache_Enabler_Engine::$settings['clear_complete_cache_on_saved_post'] ) {
973
+ // clear before the update author archives
974
+ self::clear_author_archives_cache_by_user_id( $post_before->post_author );
975
+ }
976
+ }
977
+ }
978
+
979
+
980
+ /**
981
+ * trash post hook
982
+ *
983
+ * @since 1.4.0
984
+ * @change 1.5.0
985
+ *
986
+ * @param integer $post_id post ID
987
+ */
988
+
989
+ public static function on_trash_post( $post_id ) {
990
+
991
+ // if any published post type is trashed
992
+ if ( get_post_status( $post_id ) === 'publish' ) {
993
+ $trashed = true;
994
+ self::clear_cache_on_post_save( $post_id, $trashed );
995
+ }
996
+ }
997
+
998
+
999
+ /**
1000
+ * transition post status hook
1001
+ *
1002
+ * @since 1.5.0
1003
+ * @change 1.5.0
1004
+ *
1005
+ * @param string $new_status new post status
1006
+ * @param string $old_status old post status
1007
+ * @param WP_Post $post post instance
1008
+ */
1009
+
1010
+ public static function on_transition_post_status( $new_status, $old_status, $post ) {
1011
+
1012
+ // if any published post type status has changed
1013
+ if ( $old_status === 'publish' && in_array( $new_status, array( 'future', 'draft', 'pending', 'private') ) ) {
1014
+ self::clear_cache_on_post_save( $post->ID );
1015
+ }
1016
  }
1017
 
1018
 
1030
 
1031
  // check if comment is approved
1032
  if ( $approved === 1 ) {
1033
+ // if setting enabled clear complete cache on new comment
1034
+ if ( Cache_Enabler_Engine::$settings['clear_complete_cache_on_new_comment'] ) {
1035
+ self::clear_complete_cache();
1036
  } else {
1037
  self::clear_page_cache_by_post_id( get_comment( $comment_id )->comment_post_ID );
1038
  }
1051
 
1052
  public static function edit_comment( $comment_id ) {
1053
 
1054
+ // if setting enabled clear complete cache on new comment
1055
+ if ( Cache_Enabler_Engine::$settings['clear_complete_cache_on_new_comment'] ) {
1056
+ self::clear_complete_cache();
1057
  } else {
1058
  self::clear_page_cache_by_post_id( get_comment( $comment_id )->comment_post_ID );
1059
  }
1075
 
1076
  // check if comment is approved
1077
  if ( $approved === 1 ) {
1078
+ // if setting enabled clear complete cache on new comment
1079
+ if ( Cache_Enabler_Engine::$settings['clear_complete_cache_on_new_comment'] ) {
1080
+ self::clear_complete_cache();
1081
  } else {
1082
  self::clear_page_cache_by_post_id( $comment['comment_post_ID'] );
1083
  }
1102
 
1103
  // check if changes occured
1104
  if ( $after_status !== $before_status ) {
1105
+ // if setting enabled clear complete cache on new comment
1106
+ if ( Cache_Enabler_Engine::$settings['clear_complete_cache_on_new_comment'] ) {
1107
+ self::clear_complete_cache();
1108
  } else {
1109
  self::clear_page_cache_by_post_id( $comment->comment_post_ID );
1110
  }
1113
 
1114
 
1115
  /**
1116
+ * clear complete cache
1117
  *
1118
  * @since 1.0.0
1119
+ * @change 1.5.0
1120
  */
1121
 
1122
+ public static function clear_complete_cache() {
1123
 
1124
+ // clear complete cache
1125
+ Cache_Enabler_Disk::clear_cache();
 
 
 
 
1126
 
1127
+ // delete cache size transient
1128
+ delete_transient( self::get_cache_size_transient_name() );
 
 
1129
 
1130
+ // clear cache post hook
1131
+ do_action( 'ce_action_cache_cleared' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1132
  }
1133
 
1134
 
1135
  /**
1136
+ * clear complete cache (deprecated)
1137
  *
1138
+ * @deprecated 1.5.0
1139
+ */
1140
+
1141
+ public static function clear_total_cache() {
1142
+
1143
+ self::clear_complete_cache();
1144
+ }
1145
+
1146
+
1147
+ /**
1148
+ * clear cached pages that might have changed from any new or updated post
1149
  *
1150
+ * @since 1.5.0
1151
+ * @change 1.5.0
1152
+ *
1153
+ * @param WP_Post $post post instance
1154
  */
1155
 
1156
+ public static function clear_associated_cache( $post ) {
1157
 
1158
+ // clear post type archives
1159
+ self::clear_post_type_archives_cache( $post->post_type );
 
 
1160
 
1161
+ // clear taxonomies archives
1162
+ self::clear_taxonomies_archives_cache_by_post_id( $post->ID );
1163
+
1164
+ if ( $post->post_type === 'post' ) {
1165
+ // clear author archives
1166
+ self::clear_author_archives_cache_by_user_id( $post->post_author );
1167
+ // date archives
1168
+ self::clear_date_archives_cache_by_post_id( $post->ID );
1169
  }
1170
+ }
1171
 
 
 
1172
 
1173
+ /**
1174
+ * clear post type archives page cache
1175
+ *
1176
+ * @since 1.5.0
1177
+ * @change 1.5.0
1178
+ *
1179
+ * @param string $post_type post type
1180
+ */
1181
 
1182
+ public static function clear_post_type_archives_cache( $post_type ) {
1183
 
1184
+ // get post type archives URL
1185
+ $post_type_archives_url = get_post_type_archive_link( $post_type );
 
 
1186
 
1187
+ if ( ! empty( $post_type_archives_url ) ) {
1188
+ // clear post type archives page and its pagination page(s) cache
1189
+ self::clear_page_cache_by_url( $post_type_archives_url, 'pagination' );
1190
  }
1191
+ }
1192
 
 
 
1193
 
1194
+ /**
1195
+ * clear taxonomies archives pages cache by post ID
1196
+ *
1197
+ * @since 1.5.0
1198
+ * @change 1.5.0
1199
+ *
1200
+ * @param integer $post_id post ID
1201
+ */
1202
 
1203
+ public static function clear_taxonomies_archives_cache_by_post_id( $post_id ) {
1204
+
1205
+ // get taxonomies
1206
+ $taxonomies = get_taxonomies();
1207
+
1208
+ foreach ( $taxonomies as $taxonomy ) {
1209
+ if ( wp_count_terms( $taxonomy ) > 0 ) {
1210
+ // get terms attached to post
1211
+ $term_ids = wp_get_post_terms( $post_id, $taxonomy, array( 'fields' => 'ids' ) );
1212
+ foreach ( $term_ids as $term_id ) {
1213
+ $term_archives_url = get_term_link( (int) $term_id, $taxonomy );
1214
+ // validate URL and ensure it does not have a query string
1215
+ if ( filter_var( $term_archives_url, FILTER_VALIDATE_URL ) && ! filter_var( $term_archives_url, FILTER_VALIDATE_URL, FILTER_FLAG_QUERY_REQUIRED ) ) {
1216
+ // clear taxonomy archives page and its pagination page(s) cache
1217
+ self::clear_page_cache_by_url( $term_archives_url, 'pagination' );
1218
+ }
1219
+ }
1220
+ }
1221
  }
1222
  }
1223
 
1224
 
1225
  /**
1226
+ * clear author archives page cache by user ID
1227
  *
1228
+ * @since 1.5.0
1229
+ * @change 1.5.0
1230
+ *
1231
+ * @param integer $user_id user ID of the author
1232
+ */
1233
+
1234
+ public static function clear_author_archives_cache_by_user_id( $user_id ) {
1235
+
1236
+ // get author archives URL
1237
+ $author_username = get_the_author_meta( 'user_login', $user_id );
1238
+ $author_base = $GLOBALS['wp_rewrite']->author_base;
1239
+ $author_archives_url = home_url( '/' ) . $author_base . '/' . $author_username;
1240
+
1241
+ // clear author archives page and its pagination page(s) cache
1242
+ self::clear_page_cache_by_url( $author_archives_url, 'pagination' );
1243
+ }
1244
+
1245
+
1246
+ /**
1247
+ * clear date archives pages cache
1248
+ *
1249
+ * @since 1.5.0
1250
+ * @change 1.5.0
1251
  *
1252
  * @param integer $post_id post ID
1253
  */
1254
 
1255
+ public static function clear_date_archives_cache_by_post_id( $post_id ) {
1256
 
1257
+ // get post dates
1258
+ $post_date_day = get_the_date( 'd', $post_id );
1259
+ $post_date_month = get_the_date( 'm', $post_id );
1260
+ $post_date_year = get_the_date( 'Y', $post_id );
1261
 
1262
+ // get post dates archives URLs
1263
+ $date_archives_day_url = get_day_link( $post_date_year, $post_date_month, $post_date_day );
1264
+ $date_archives_month_url = get_month_link( $post_date_year, $post_date_month );
1265
+ $date_archives_year_url = get_year_link( $post_date_year );
1266
+
1267
+ // clear date archives pages and their pagination pages cache
1268
+ self::clear_page_cache_by_url( $date_archives_day_url, 'pagination' );
1269
+ self::clear_page_cache_by_url( $date_archives_month_url, 'pagination' );
1270
+ self::clear_page_cache_by_url( $date_archives_year_url, 'pagination' );
1271
  }
1272
 
1273
 
1275
  * clear page cache by post ID
1276
  *
1277
  * @since 1.0.0
1278
+ * @change 1.5.0
1279
  *
1280
+ * @param integer|string $post_id post ID
1281
+ * @param string $clear_type clear the `pagination` or the entire `dir` instead of only the cached `page`
1282
  */
1283
 
1284
+ public static function clear_page_cache_by_post_id( $post_id, $clear_type = 'page' ) {
1285
 
1286
  // validate integer
1287
  if ( ! is_int( $post_id ) ) {
1294
  }
1295
 
1296
  // clear page cache
1297
+ self::clear_page_cache_by_url( get_permalink( $post_id ), $clear_type );
1298
  }
1299
 
1300
 
1302
  * clear page cache by URL
1303
  *
1304
  * @since 1.0.0
1305
+ * @change 1.5.0
1306
  *
1307
+ * @param string $clear_url full URL to potentially cached page
1308
+ * @param string $clear_type clear the `pagination` or the entire `dir` instead of only the cached `page`
1309
  */
1310
 
1311
  public static function clear_page_cache_by_url( $clear_url, $clear_type = 'page' ) {
1312
 
 
 
 
 
 
1313
  // validate URL
1314
  if ( ! filter_var( $clear_url, FILTER_VALIDATE_URL ) ) {
1315
  return;
1316
  }
1317
 
1318
  // clear URL
1319
+ Cache_Enabler_Disk::clear_cache( $clear_url, $clear_type );
1320
 
1321
  // clear cache by URL post hook
1322
  do_action( 'ce_action_cache_by_url_cleared', $clear_url );
1324
 
1325
 
1326
  /**
1327
+ * clear site cache by blog ID
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1328
  *
1329
  * @since 1.4.0
1330
+ * @change 1.5.0
1331
  *
1332
+ * @param integer|string $blog_id blog ID
1333
+ * @param boolean $delete_cache_size_transient whether or not the cache size transient should be deleted
1334
  */
1335
 
1336
+ public static function clear_site_cache_by_blog_id( $blog_id, $delete_cache_size_transient = true ) {
1337
 
1338
  // check if network
1339
  if ( ! is_multisite() ) {
1351
  }
1352
 
1353
  // check if blog ID exists
1354
+ if ( ! in_array( $blog_id, self::get_blog_ids() ) ) {
1355
  return;
1356
  }
1357
 
1358
+ // get clear URL
1359
  $clear_url = get_home_url( $blog_id );
1360
 
1361
  // network with subdomain configuration
1369
 
1370
  // main site
1371
  if ( $blog_path === '/' ) {
1372
+ $blog_paths = self::get_blog_paths();
1373
+ $blog_domain = parse_url( $clear_url, PHP_URL_HOST );
1374
+ $glob_path = Cache_Enabler_Disk::$cache_dir . '/' . $blog_domain;
 
 
 
1375
 
1376
  // get cached page paths
1377
  $page_paths = glob( $glob_path . '/*', GLOB_MARK | GLOB_ONLYDIR );
1385
  }
1386
 
1387
  // clear home page cache
1388
+ self::clear_page_cache_by_url( $clear_url );
1389
  // subsite
1390
  } else {
1391
  // clear subsite cache
1392
  self::clear_page_cache_by_url( $clear_url, 'dir' );
1393
  }
1394
  }
1395
+
1396
+ // delete cache size transient
1397
+ if ( $delete_cache_size_transient ) {
1398
+ delete_transient( self::get_cache_size_transient_name( $blog_id ) );
1399
+ }
1400
  }
1401
 
1402
 
1403
  /**
1404
+ * clear cache when any post type is created or updated
1405
  *
1406
+ * @since 1.5.0
1407
+ * @change 1.5.0
1408
  *
1409
+ * @param integer $post_id post ID
1410
+ * @param boolean $trashed whether this is an existing post being trashed
1411
  */
1412
 
1413
+ public static function clear_cache_on_post_save( $post_id, $trashed = false ) {
 
 
 
1414
 
1415
+ // get post data
1416
+ $post = get_post( $post_id );
1417
 
1418
+ // if setting enabled clear complete cache
1419
+ if ( Cache_Enabler_Engine::$settings['clear_complete_cache_on_saved_post'] ) {
1420
+ self::clear_complete_cache();
1421
+ // clear page and/or associated cache otherwise
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1422
  } else {
1423
+ // if updated or trashed
1424
+ if ( strtotime( $post->post_modified_gmt ) > strtotime( $post->post_date_gmt ) || $trashed ) {
1425
+ // clear page cache
1426
+ self::clear_page_cache_by_post_id( $post_id );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1427
  }
1428
+ // clear associated cache
1429
+ self::clear_associated_cache( $post );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1430
  }
1431
  }
1432
 
1433
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1434
  /**
1435
  * check plugin requirements
1436
  *
1437
  * @since 1.1.0
1438
+ * @change 1.5.0
1439
  */
1440
 
1441
  public static function requirements_check() {
1442
 
1443
+ // check WordPress version
1444
  if ( version_compare( $GLOBALS['wp_version'], CE_MIN_WP . 'alpha', '<' ) ) {
1445
+ echo sprintf(
1446
+ '<div class="notice notice-error"><p>%s</p></div>',
1447
  sprintf(
1448
+ // translators: 1. Cache Enabler 2. WordPress version (e.g. 5.1)
1449
+ esc_html__( 'The %1$s plugin is optimized for WordPress %2$s. Please disable the plugin or upgrade your WordPress installation (recommended).', 'cache-enabler' ),
1450
+ '<strong>Cache Enabler</strong>',
1451
+ CE_MIN_WP
1452
+ )
1453
+ );
1454
+ }
1455
+
1456
+ // check permalink structure
1457
+ if ( Cache_Enabler_Engine::$settings['permalink_structure'] === 'plain' && current_user_can( 'manage_options' ) ) {
1458
+ echo sprintf(
1459
+ '<div class="notice notice-error"><p>%s</p></div>',
1460
+ sprintf(
1461
+ // translators: 1. Cache Enabler 2. Permalink Settings
1462
+ esc_html__( 'The %1$s plugin requires a custom permalink structure to start caching properly. Please enable a custom structure in the %2$s.', 'cache-enabler' ),
1463
+ '<strong>Cache Enabler</strong>',
1464
  sprintf(
1465
+ '<a href="%s">%s</a>',
1466
+ admin_url( 'options-permalink.php' ),
1467
+ esc_html__( 'Permalink Settings', 'cache-enabler' )
 
1468
  )
1469
  )
1470
  );
1471
  }
1472
 
1473
+ // check permissions
1474
+ if ( file_exists( Cache_Enabler_Disk::$cache_dir ) && ! is_writable( Cache_Enabler_Disk::$cache_dir ) ) {
1475
+ echo sprintf(
1476
+ '<div class="notice notice-error"><p>%s</p></div>',
1477
  sprintf(
1478
+ // translators: 1. Cache Enabler 2. 755 3. wp-content/cache 4. file permissions
1479
+ esc_html__( 'The %1$s plugin requires write permissions %2$s in %3$s. Please change the %4$s.', 'cache-enabler' ),
1480
+ '<strong>Cache Enabler</strong>',
1481
+ '<code>755</code>',
1482
+ '<code>wp-content/cache</code>',
1483
  sprintf(
1484
+ '<a href="%s" target="_blank" rel="nofollow noopener">%s</a>',
1485
+ 'https://wordpress.org/support/article/changing-file-permissions/',
1486
+ esc_html__( 'file permissions', 'cache-enabler' )
 
 
 
 
 
 
 
1487
  )
1488
  )
1489
  );
1490
  }
1491
 
1492
+ // check Autoptimize HTML optimization
1493
+ if ( defined( 'AUTOPTIMIZE_PLUGIN_DIR' ) && Cache_Enabler_Engine::$settings['minify_html'] && get_option( 'autoptimize_html', '' ) !== '' ) {
1494
+ echo sprintf(
1495
+ '<div class="notice notice-error"><p>%s</p></div>',
1496
  sprintf(
1497
+ // translators: 1. Autoptimize 2. Cache Enabler Settings
1498
+ esc_html__( 'The %1$s plugin HTML optimization is enabled. Please disable HTML minification in the %2$s.', 'cache-enabler' ),
1499
+ '<strong>Autoptimize</strong>',
1500
  sprintf(
1501
+ '<a href="%s">%s</a>',
1502
+ admin_url( 'options-general.php?page=cache-enabler' ),
1503
+ esc_html__( 'Cache Enabler Settings', 'cache-enabler' )
 
 
 
 
 
 
 
 
 
 
 
1504
  )
1505
  )
1506
  );
1518
  public static function register_textdomain() {
1519
 
1520
  // load translated strings
1521
+ load_plugin_textdomain( 'cache-enabler', false, 'cache-enabler/lang' );
 
 
 
 
1522
  }
1523
 
1524
 
1526
  * register settings
1527
  *
1528
  * @since 1.0.0
1529
+ * @change 1.5.0
1530
  */
1531
 
1532
  public static function register_settings() {
1533
 
1534
+ register_setting( 'cache_enabler', 'cache_enabler', array( __CLASS__, 'validate_settings' ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1535
  }
1536
 
1537
 
1539
  * validate regex
1540
  *
1541
  * @since 1.2.3
1542
+ * @change 1.5.0
1543
  *
1544
+ * @param string $regex string containing regex
1545
+ * @return string $validated_regex string containing regex or empty string if input is invalid
1546
  */
1547
 
1548
+ public static function validate_regex( $regex ) {
1549
 
1550
+ if ( ! empty( $regex ) ) {
1551
+ if ( ! preg_match( '/^\/.*\/$/', $regex ) ) {
1552
+ $regex = '/' . $regex . '/';
1553
  }
1554
 
1555
+ if ( @preg_match( $regex, null ) === false ) {
1556
  return '';
1557
  }
1558
 
1559
+ $validated_regex = sanitize_text_field( $regex );
1560
+
1561
+ return $validated_regex;
1562
  }
1563
 
1564
  return '';
1565
  }
1566
 
1567
+
1568
  /**
1569
  * validate settings
1570
  *
1571
  * @since 1.0.0
1572
+ * @change 1.5.0
1573
  *
1574
+ * @param array $settings user defined settings
1575
+ * @return array $validated_settings validated settings
1576
  */
1577
 
1578
+ public static function validate_settings( $settings ) {
1579
 
1580
+ // validate array
1581
+ if ( ! is_array( $settings ) ) {
1582
  return;
1583
  }
1584
 
1585
+ $validated_settings = array(
1586
+ 'cache_expires' => (int) ( ! empty( $settings['cache_expires'] ) ),
1587
+ 'cache_expiry_time' => (int) @$settings['cache_expiry_time'],
1588
+ 'clear_complete_cache_on_saved_post' => (int) ( ! empty( $settings['clear_complete_cache_on_saved_post'] ) ),
1589
+ 'clear_complete_cache_on_new_comment' => (int) ( ! empty( $settings['clear_complete_cache_on_new_comment'] ) ),
1590
+ 'clear_complete_cache_on_changed_plugin' => (int) ( ! empty( $settings['clear_complete_cache_on_changed_plugin'] ) ),
1591
+ 'compress_cache' => (int) ( ! empty( $settings['compress_cache'] ) ),
1592
+ 'convert_image_urls_to_webp' => (int) ( ! empty( $settings['convert_image_urls_to_webp'] ) ),
1593
+ 'excluded_post_ids' => (string) sanitize_text_field( @$settings['excluded_post_ids'] ),
1594
+ 'excluded_page_paths' => (string) self::validate_regex( @$settings['excluded_page_paths'] ),
1595
+ 'excluded_query_strings' => (string) self::validate_regex( @$settings['excluded_query_strings'] ),
1596
+ 'excluded_cookies' => (string) self::validate_regex( @$settings['excluded_cookies'] ),
1597
+ 'minify_html' => (int) ( ! empty( $settings['minify_html'] ) ),
1598
+ 'minify_inline_css_js' => (int) ( ! empty( $settings['minify_inline_css_js'] ) ),
1599
+ );
 
 
 
 
 
 
 
 
 
1600
 
1601
+ // add default system settings
1602
+ $validated_settings = wp_parse_args( $validated_settings, self::get_default_settings( 'system' ) );
 
 
 
 
 
 
1603
 
1604
+ // check if cache should be cleared
1605
+ if ( ! empty( $settings['clear_complete_cache_on_saved_settings'] ) ) {
1606
+ self::clear_complete_cache();
1607
+ set_transient( self::get_cache_cleared_transient_name(), 1 );
 
 
 
1608
  }
1609
 
1610
+ return $validated_settings;
 
 
 
 
 
 
 
 
 
 
 
 
 
1611
  }
1612
 
1613
 
1615
  * settings page
1616
  *
1617
  * @since 1.0.0
1618
+ * @change 1.5.0
1619
  */
1620
 
1621
  public static function settings_page() {
1622
 
1623
+ ?>
1624
+
1625
+ <div id="cache-enabler-settings" class="wrap">
1626
+ <h2>
1627
+ <?php esc_html_e( 'Cache Enabler Settings', 'cache-enabler' ); ?>
1628
+ </h2>
1629
+
1630
+ <?php
1631
+ if ( defined( 'WP_CACHE' ) && ! WP_CACHE ) {
1632
+ printf(
1633
  '<div class="notice notice-warning"><p>%s</p></div>',
1634
  sprintf(
1635
  // translators: 1. define( 'WP_CACHE', true ); 2. wp-config.php
1637
  "<code>define( 'WP_CACHE', true );</code>",
1638
  '<code>wp-config.php</code>'
1639
  )
1640
+ );
1641
+ }
1642
+ ?>
 
 
 
 
 
 
 
1643
 
1644
+ <div class="notice notice-info">
1645
  <p>
1646
  <?php
1647
  printf(
1648
+ // translators: %s: KeyCDN
1649
  esc_html__( 'Combine %s with Cache Enabler for even better WordPress performance and achieve the next level of caching with a CDN.', 'cache-enabler' ),
1650
  '<strong><a href="https://www.keycdn.com?utm_source=wp-admin&utm_medium=plugins&utm_campaign=cache-enabler">KeyCDN</a></strong>'
1651
  );
1654
  </div>
1655
 
1656
  <form method="post" action="options.php">
1657
+ <?php settings_fields( 'cache_enabler' ); ?>
 
1658
  <table class="form-table">
 
 
 
 
 
 
 
 
 
1659
  <tr valign="top">
1660
  <th scope="row">
1661
  <?php esc_html_e( 'Cache Behavior', 'cache-enabler' ); ?>
1662
  </th>
1663
  <td>
1664
  <fieldset>
1665
+ <p class="subheading"><?php esc_html_e( 'Expiration', 'cache-enabler' ); ?></p>
1666
+ <label for="cache_expires" class="checkbox--form-control">
1667
+ <input name="cache_enabler[cache_expires]" type="checkbox" id="cache_expires" value="1" <?php checked( '1', Cache_Enabler_Engine::$settings['cache_expires'] ); ?> />
1668
+ </label>
1669
+ <label for="cache_expiry_time">
1670
+ <?php
1671
+ printf(
1672
+ // translators: %s: Number of hours.
1673
+ esc_html__( 'Cached pages expire %s hours after being created.', 'cache-enabler' ),
1674
+ '<input name="cache_enabler[cache_expiry_time]" type="number" id="cache_expiry_time" value="' . Cache_Enabler_Engine::$settings['cache_expiry_time'] . '" class="small-text">'
1675
+ );
1676
+ ?>
1677
+ </label>
1678
+
1679
+ <br />
1680
+
1681
+ <p class="subheading"><?php esc_html_e( 'Clearing', 'cache-enabler' ); ?></p>
1682
+ <label for="clear_complete_cache_on_saved_post">
1683
+ <input name="cache_enabler[clear_complete_cache_on_saved_post]" type="checkbox" id="clear_complete_cache_on_saved_post" value="1" <?php checked( '1', Cache_Enabler_Engine::$settings['clear_complete_cache_on_saved_post'] ); ?> />
1684
+ <?php esc_html_e( 'Clear the complete cache if any post type has been published, updated, or trashed (instead of only the page and/or associated cache).', 'cache-enabler' ); ?>
1685
+ <span class="badge badge--success"><?php esc_html_e( 'Updated', 'cache-enabler' ); ?></span>
1686
  </label>
1687
 
1688
  <br />
1689
 
1690
+ <label for="clear_complete_cache_on_new_comment">
1691
+ <input name="cache_enabler[clear_complete_cache_on_new_comment]" type="checkbox" id="clear_complete_cache_on_new_comment" value="1" <?php checked( '1', Cache_Enabler_Engine::$settings['clear_complete_cache_on_new_comment'] ); ?> />
1692
+ <?php esc_html_e( 'Clear the complete cache if a new comment has been posted (instead of only the page cache).', 'cache-enabler' ); ?>
1693
  </label>
1694
 
1695
  <br />
1696
 
1697
+ <label for="clear_complete_cache_on_changed_plugin">
1698
+ <input name="cache_enabler[clear_complete_cache_on_changed_plugin]" type="checkbox" id="clear_complete_cache_on_changed_plugin" value="1" <?php checked( '1', Cache_Enabler_Engine::$settings['clear_complete_cache_on_changed_plugin'] ); ?> />
1699
+ <?php esc_html_e( 'Clear the complete cache if any plugin has been activated, updated, or deactivated.', 'cache-enabler' ); ?>
1700
  </label>
1701
 
1702
  <br />
1703
 
1704
+ <p class="subheading"><?php esc_html_e( 'Variants', 'cache-enabler' ); ?></p>
1705
+ <label for="convert_image_urls_to_webp">
1706
+ <input name="cache_enabler[convert_image_urls_to_webp]" type="checkbox" id="convert_image_urls_to_webp" value="1" <?php checked( '1', Cache_Enabler_Engine::$settings['convert_image_urls_to_webp'] ); ?> />
1707
+ <?php
1708
+ printf(
1709
+ // translators: %s: Optimus
1710
+ esc_html__( 'Create an additional cached version for WebP image support. Convert your images to WebP with %s.', 'cache-enabler' ),
1711
+ '<a href="https://optimus.io" target="_blank" rel="nofollow noopener">Optimus</a>'
1712
+ );
1713
+ ?>
1714
  </label>
1715
 
1716
  <br />
 
1717
 
1718
+ <label for="compress_cache">
1719
+ <input name="cache_enabler[compress_cache]" type="checkbox" id="compress_cache" value="1" <?php checked( '1', Cache_Enabler_Engine::$settings['compress_cache'] ); ?> />
1720
+ <?php esc_html_e( 'Pre-compress cached pages with Gzip.', 'cache-enabler' ); ?>
1721
  </label>
1722
 
1723
  <br />
1724
 
1725
+ <p class="subheading"><?php esc_html_e( 'Minification', 'cache-enabler' ); ?></p>
1726
+ <label for="minify_html" class="checkbox--form-control">
1727
+ <input name="cache_enabler[minify_html]" type="checkbox" id="minify_html" value="1" <?php checked( '1', Cache_Enabler_Engine::$settings['minify_html'] ); ?> />
1728
+ </label>
1729
+ <label for="minify_inline_css_js">
1730
+ <?php
1731
+ $minify_inline_css_js_options = array(
1732
+ esc_html__( 'excluding', 'cache-enabler' ) => 0,
1733
+ esc_html__( 'including', 'cache-enabler' ) => 1,
1734
+ );
1735
+ $minify_inline_css_js = '<select name="cache_enabler[minify_inline_css_js]" id="minify_inline_css_js">';
1736
+ foreach ( $minify_inline_css_js_options as $key => $value ) {
1737
+ $minify_inline_css_js .= '<option value="' . esc_attr( $value ) . '"' . selected( $value, Cache_Enabler_Engine::$settings['minify_inline_css_js'], false ) . '>' . $key . '</option>';
1738
+ }
1739
+ $minify_inline_css_js .= '</select>';
1740
+ printf(
1741
+ // translators: %s: Form field control for 'excluding' or 'including' inline CSS and JavaScript during HTML minification.
1742
+ esc_html__( 'Minify HTML in cached pages %s inline CSS and JavaScript.', 'cache-enabler' ),
1743
+ $minify_inline_css_js
1744
+ );
1745
+ ?>
1746
+ <span class="badge badge--success"><?php esc_html_e( 'Updated', 'cache-enabler' ); ?></span>
1747
  </label>
1748
  </fieldset>
1749
  </td>
1755
  </th>
1756
  <td>
1757
  <fieldset>
1758
+ <p class="subheading"><?php esc_html_e( 'Post IDs', 'cache-enabler' ); ?></p>
1759
+ <label for="excluded_post_ids">
1760
+ <input name="cache_enabler[excluded_post_ids]" type="text" id="excluded_post_ids" value="<?php echo esc_attr( Cache_Enabler_Engine::$settings['excluded_post_ids'] ) ?>" class="regular-text" />
1761
+ <p class="description">
1762
+ <?php
1763
+ // translators: %s: ,
1764
+ printf( esc_html__( 'Post IDs separated by a %s that should bypass the cache.', 'cache-enabler' ), '<code>,</code>' );
1765
+ ?>
1766
  </p>
1767
+ <p><?php esc_html_e( 'Example:', 'cache-enabler' ); ?> <code>2,43,65</code></p>
1768
  </label>
1769
 
1770
  <br />
1771
 
1772
+ <p class="subheading"><?php esc_html_e( 'Page Paths', 'cache-enabler' ); ?></p>
1773
+ <label for="excluded_page_paths">
1774
+ <input name="cache_enabler[excluded_page_paths]" type="text" id="excluded_page_paths" value="<?php echo esc_attr( Cache_Enabler_Engine::$settings['excluded_page_paths'] ) ?>" class="regular-text code" />
1775
+ <p class="description"><?php esc_html_e( 'A regex matching page paths that should bypass the cache.', 'cache-enabler' ); ?></p>
1776
+ <p><?php esc_html_e( 'Example:', 'cache-enabler' ); ?> <code>/^(\/|\/forums\/)$/</code></p>
1777
  </label>
1778
 
1779
  <br />
1780
 
1781
+ <p class="subheading"><?php esc_html_e( 'Query Strings', 'cache-enabler' ); ?><span class="badge badge--success"><?php esc_html_e( 'New', 'cache-enabler' ); ?></span></p>
1782
+ <label for="excluded_query_strings">
1783
+ <input name="cache_enabler[excluded_query_strings]" type="text" id="excluded_query_strings" value="<?php echo esc_attr( Cache_Enabler_Engine::$settings['excluded_query_strings'] ) ?>" class="regular-text code" />
1784
+ <p class="description"><?php esc_html_e( 'A regex matching query strings that should bypass the cache.', 'cache-enabler' ); ?></p>
1785
+ <p><?php esc_html_e( 'Example:', 'cache-enabler' ); ?> <code>/^nocache$/</code></p>
1786
  </label>
 
 
 
1787
 
1788
+ <br />
1789
+
1790
+ <p class="subheading"><?php esc_html_e( 'Cookies', 'cache-enabler' ); ?></p>
1791
+ <label for="excluded_cookies">
1792
+ <input name="cache_enabler[excluded_cookies]" type="text" id="excluded_cookies" value="<?php echo esc_attr( Cache_Enabler_Engine::$settings['excluded_cookies'] ) ?>" class="regular-text code" />
1793
+ <p class="description"><?php esc_html_e( 'A regex matching cookies that should bypass the cache.', 'cache-enabler' ); ?></p>
1794
+ <p><?php esc_html_e( 'Example:', 'cache-enabler' ); ?> <code>/^(comment_author|woocommerce_items_in_cart|wp_woocommerce_session)_?/</code></p>
1795
+ <p><?php esc_html_e( 'Default if unset:', 'cache-enabler' ); ?> <code>/^(wp-postpass|wordpress_logged_in|comment_author)_/</code></p>
 
 
 
1796
  </label>
1797
  </fieldset>
1798
  </td>
1799
  </tr>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1800
  </table>
1801
 
1802
  <p class="submit">
1803
  <input type="submit" class="button-secondary" value="<?php esc_html_e( 'Save Changes', 'cache-enabler' ); ?>" />
1804
+ <input name="cache_enabler[clear_complete_cache_on_saved_settings]" type="submit" class="button-primary" value="<?php esc_html_e( 'Save Changes and Clear Cache', 'cache-enabler' ); ?>" />
1805
  </p>
1806
  </form>
1807
  </div>
inc/cache_enabler_cli.class.php CHANGED
@@ -1,18 +1,15 @@
1
  <?php
2
-
3
-
4
- // exit
5
- defined( 'ABSPATH' ) || exit;
6
-
7
-
8
  /**
9
  * Interact with Cache Enabler.
10
  *
11
  * @since 1.3.5
12
  */
13
 
14
- class Cache_Enabler_CLI {
 
 
15
 
 
16
 
17
  /**
18
  * Clear the page cache.
@@ -62,7 +59,7 @@ class Cache_Enabler_CLI {
62
 
63
  // clear complete cache if no associative arguments are given
64
  if ( empty( $assoc_args['ids'] ) && empty( $assoc_args['urls'] ) && empty( $assoc_args['sites'] ) ) {
65
- Cache_Enabler::clear_total_cache();
66
 
67
  return WP_CLI::success( ( is_multisite() && is_plugin_active_for_network( CE_BASE ) ) ? esc_html__( 'Network cache cleared.', 'cache-enabler' ) : esc_html__( 'Cache cleared.', 'cache-enabler' ) );
68
  }
@@ -84,7 +81,7 @@ class Cache_Enabler_CLI {
84
 
85
  // clear pages cache by blog ID(s)
86
  if ( ! empty( $assoc_args['sites'] ) ) {
87
- array_map( 'Cache_Enabler::clear_blog_id_cache', explode( ',', $assoc_args['sites'] ) );
88
 
89
  // check if there is more than one site
90
  $separators = substr_count( $assoc_args['sites'], ',' );
@@ -98,5 +95,5 @@ class Cache_Enabler_CLI {
98
  }
99
  }
100
 
101
- // add WP-CLI command for Cache Enabler
102
  WP_CLI::add_command( 'cache-enabler', 'Cache_Enabler_CLI' );
1
  <?php
 
 
 
 
 
 
2
  /**
3
  * Interact with Cache Enabler.
4
  *
5
  * @since 1.3.5
6
  */
7
 
8
+ if ( ! defined( 'ABSPATH' ) ) {
9
+ exit;
10
+ }
11
 
12
+ class Cache_Enabler_CLI {
13
 
14
  /**
15
  * Clear the page cache.
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() && is_plugin_active_for_network( CE_BASE ) ) ? esc_html__( 'Network cache cleared.', 'cache-enabler' ) : esc_html__( 'Cache cleared.', 'cache-enabler' ) );
65
  }
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'], ',' );
95
  }
96
  }
97
 
98
+ // add WP-CLI command
99
  WP_CLI::add_command( 'cache-enabler', 'Cache_Enabler_CLI' );
inc/cache_enabler_disk.class.php CHANGED
@@ -1,211 +1,158 @@
1
  <?php
2
-
3
-
4
- // exit
5
- defined( 'ABSPATH' ) || exit;
6
-
7
-
8
  /**
9
- * Cache_Enabler_Disk
10
  *
11
  * @since 1.0.0
12
  */
13
 
14
- final class Cache_Enabler_Disk {
 
 
15
 
 
16
 
17
  /**
18
- * cached filename settings
19
  *
20
- * @since 1.0.7
21
- * @change 1.4.0
22
  *
23
- * @var string
24
  */
25
 
26
- const FILE_GLOB = '*index*';
27
- const FILE_HTML = 'index.html';
28
- const FILE_GZIP = 'index.html.gz';
29
- const FILE_WEBP_HTML = 'index-webp.html';
30
- const FILE_WEBP_GZIP = 'index-webp.html.gz';
31
 
32
 
33
  /**
34
- * permalink check
35
  *
36
- * @since 1.0.0
37
- * @change 1.0.0
38
  *
39
- * @return boolean true if installed
40
  */
41
 
42
- public static function is_permalink() {
43
-
44
- return get_option( 'permalink_structure' );
45
- }
46
 
47
 
48
  /**
49
- * store asset
50
  *
51
- * @since 1.0.0
52
- * @change 1.0.0
53
  *
54
- * @param string $data content of the asset
55
  */
56
 
57
- public static function store_asset( $data ) {
58
-
59
- // check if empty
60
- if ( empty( $data ) ) {
61
- wp_die( 'Asset is empty.' );
62
- }
63
-
64
- // save asset
65
- self::_create_files( $data );
66
- }
67
 
68
 
69
  /**
70
- * check asset
71
- *
72
- * @since 1.0.0
73
- * @change 1.0.0
74
  *
75
- * @return boolean true if asset exists
 
76
  */
77
 
78
- public static function check_asset() {
 
 
 
79
 
80
- return is_readable( self::_file_html() );
 
81
  }
82
 
83
 
84
  /**
85
- * check expiry
86
  *
87
- * @since 1.0.1
88
- * @change 1.0.1
89
- *
90
- * @return boolean true if asset expired
91
  */
92
 
93
- public static function check_expiry() {
94
-
95
- // get Cache Enabler options
96
- $options = Cache_Enabler::$options;
97
-
98
- // check if an expiry time is set
99
- if ( $options['expires'] === 0) {
100
- return false;
 
 
 
 
 
 
 
101
  }
102
-
103
- $now = time();
104
- $expires_seconds = 3600 * $options['expires'];
105
-
106
- // check if asset has expired
107
- if ( ( filemtime( self::_file_html() ) + $expires_seconds ) <= $now ) {
108
- return true;
109
- }
110
-
111
- return false;
112
  }
113
 
114
 
115
  /**
116
- * delete asset
117
  *
118
  * @since 1.0.0
119
- * @change 1.4.7
120
  *
121
- * @param string $clear_url full or relative URL of a page
122
- * @param string $clear_type if `dir` clear the entire directory
123
  */
124
 
125
- public static function delete_asset( $clear_url, $clear_type ) {
126
 
127
- // get directory
128
- $dir = self::_file_path( $clear_url );
129
-
130
- // delete all cached variants in directory
131
- array_map( 'unlink', glob( $dir . self::FILE_GLOB ) );
132
-
133
- // get directory data
134
- $objects = self::_get_dir( $dir );
135
-
136
- // check if directory is now empty or if it needs to be cleared anyways
137
- if ( empty( $objects ) || $clear_type === 'dir' ) {
138
- self::_clear_dir( $dir );
139
  }
 
 
 
140
  }
141
 
142
 
143
  /**
144
- * clear cache
145
  *
146
  * @since 1.0.0
147
  * @change 1.0.0
 
 
148
  */
149
 
150
- public static function clear_cache() {
151
 
152
- // clear complete cache
153
- self::_clear_dir( CE_CACHE_DIR );
154
  }
155
 
156
 
157
  /**
158
- * get asset
159
  *
160
- * @since 1.0.0
161
- * @change 1.0.9
 
 
162
  */
163
 
164
- public static function get_asset() {
165
-
166
- // set X-Cache-Handler response header
167
- header( 'X-Cache-Handler: php' );
168
 
169
- // get request headers
170
- if ( function_exists( 'apache_request_headers' ) ) {
171
- $headers = apache_request_headers();
172
- $http_if_modified_since = ( isset( $headers[ 'If-Modified-Since' ] ) ) ? $headers[ 'If-Modified-Since' ] : '';
173
- $http_accept = ( isset( $headers[ 'Accept' ] ) ) ? $headers[ 'Accept' ] : '';
174
- $http_accept_encoding = ( isset( $headers[ 'Accept-Encoding' ] ) ) ? $headers[ 'Accept-Encoding' ] : '';
175
- } else {
176
- $http_if_modified_since = ( isset( $_SERVER[ 'HTTP_IF_MODIFIED_SINCE' ] ) ) ? $_SERVER[ 'HTTP_IF_MODIFIED_SINCE' ] : '';
177
- $http_accept = ( isset( $_SERVER[ 'HTTP_ACCEPT' ] ) ) ? $_SERVER[ 'HTTP_ACCEPT' ] : '';
178
- $http_accept_encoding = ( isset( $_SERVER[ 'HTTP_ACCEPT_ENCODING' ] ) ) ? $_SERVER[ 'HTTP_ACCEPT_ENCODING' ] : '';
179
- }
180
-
181
- // check modified since with cached file and return 304 if no difference
182
- if ( $http_if_modified_since && ( strtotime( $http_if_modified_since ) >= filemtime( self::_file_html() ) ) ) {
183
- header( $_SERVER['SERVER_PROTOCOL'] . ' 304 Not Modified', true, 304 );
184
- exit;
185
  }
186
 
187
- // check webp and deliver gzip webp file if support
188
- if ( $http_accept && ( strpos( $http_accept, 'webp' ) !== false ) ) {
189
- if ( is_readable( self::_file_webp_gzip() ) ) {
190
- header( 'Content-Encoding: gzip' );
191
- readfile( self::_file_webp_gzip() );
192
- exit;
193
- } elseif ( is_readable( self::_file_webp_html() ) ) {
194
- readfile( self::_file_webp_html() );
195
- exit;
196
- }
197
- }
198
 
199
- // check encoding and deliver gzip file if support
200
- if ( $http_accept_encoding && ( strpos( $http_accept_encoding, 'gzip' ) !== false ) && is_readable( self::_file_gzip() ) ) {
201
- header( 'Content-Encoding: gzip' );
202
- readfile( self::_file_gzip() );
203
- exit;
204
  }
205
 
206
- // deliver cached file (default)
207
- readfile( self::_file_html() );
208
- exit;
209
  }
210
 
211
 
@@ -213,100 +160,110 @@ final class Cache_Enabler_Disk {
213
  * create signature
214
  *
215
  * @since 1.0.0
216
- * @change 1.0.0
217
  *
218
  * @return string signature
219
  */
220
 
221
- private static function _cache_signature() {
222
 
223
  return sprintf(
224
- "\n\n<!-- %s @ %s",
225
  'Cache Enabler by KeyCDN',
226
- date_i18n(
227
- 'd.m.Y H:i:s',
228
- current_time( 'timestamp' )
229
- )
230
  );
231
  }
232
 
233
 
234
  /**
235
- * create files
236
  *
237
  * @since 1.0.0
238
- * @change 1.4.8
239
  *
240
- * @param string $data HTML content
 
241
  */
242
 
243
- private static function _create_files( $data ) {
244
-
245
- // get Cache Enabler options
246
- $options = Cache_Enabler::$options;
247
 
248
- // get base signature
249
- $cache_signature = self::_cache_signature();
250
 
251
- // create folder
252
- if ( ! wp_mkdir_p( self::_file_path() ) ) {
253
- wp_die( 'Unable to create directory.' );
254
  }
255
 
256
- // create files
257
- self::_create_file( self::_file_html(), $data . $cache_signature . ' (' . self::_file_scheme() . ' html) -->' );
258
 
259
- // create pre-compressed file
260
- if ( $options['compress'] ) {
261
- self::_create_file( self::_file_gzip(), gzencode( $data . $cache_signature . ' (' . self::_file_scheme() . ' gzip) -->', 9) );
262
  }
263
 
264
- // create webp supported files
265
- if ( $options['webp'] ) {
266
- // magic regex rule
267
- $regex_rule = '#(?:(?:(src|srcset|data-[^=]+)\s*=|(url)\()\s*[\'\"]?\s*)\K(?:[^\?\"\'\s>]+)(?:\.jpe?g|\.png)(?:\s\d+w[^\"\'>]*)?(?=\/?[\"\'\s\)>])(?=[^<{]*(?:\)[^<{]*\}|>))#i';
268
-
269
- // call the webp converter callback
270
- $converted_data = apply_filters( 'cache_enabler_disk_webp_converted_data', preg_replace_callback( $regex_rule, 'self::_convert_webp', $data ) );
271
 
272
- self::_create_file( self::_file_webp_html(), $converted_data . $cache_signature . ' (' . self::_file_scheme() . ' webp html) -->' );
 
 
273
 
274
- // create pre-compressed file
275
- if ( $options['compress'] ) {
276
- self::_create_file( self::_file_webp_gzip(), gzencode( $converted_data . $cache_signature . ' (' . self::_file_scheme() . ' webp gzip) -->', 9) );
 
 
 
277
  }
278
  }
 
 
279
  }
280
 
281
 
282
  /**
283
- * create file
284
  *
285
  * @since 1.0.0
286
- * @change 1.0.0
287
  *
288
- * @param string $file file path
289
- * @param string $data content of the HTML
290
  */
291
 
292
- private static function _create_file( $file, $data ) {
293
 
294
- // open file handler
295
- if ( ! $handle = @fopen( $file, 'wb' ) ) {
296
- wp_die( 'Cannot write to file.' );
 
297
  }
298
 
299
- // write
300
- @fwrite( $handle, $data );
301
- fclose( $handle );
302
- clearstatcache();
303
 
304
- // set permissions
305
- $stat = @stat( dirname( $file ) );
306
- $perms = $stat['mode'] & 0007777;
307
- $perms = $perms & 0000666;
308
- @chmod( $file, $perms );
309
- clearstatcache();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
310
  }
311
 
312
 
@@ -314,122 +271,214 @@ final class Cache_Enabler_Disk {
314
  * clear directory
315
  *
316
  * @since 1.0.0
317
- * @change 1.4.7
318
  *
319
  * @param string $dir directory
320
  */
321
 
322
- private static function _clear_dir( $dir ) {
323
 
324
- // remove slashes
325
  $dir = untrailingslashit( $dir );
326
 
327
- // check if directory
328
  if ( ! is_dir( $dir ) ) {
329
  return;
330
  }
331
 
332
  // get directory data
333
- $objects = self::_get_dir( $dir );
334
 
335
  // check if directory is empty
336
- if ( empty( $objects ) ) {
337
  // delete empty directory
338
  @rmdir( $dir );
339
 
 
 
 
340
  // get parent directory
341
  $parent_dir = preg_replace( '/\/[^\/]+$/', '', $dir );
342
 
343
  // get parent directory data
344
- $parent_objects = self::_get_dir( $parent_dir );
345
 
346
  // check if parent directory is also empty
347
- if ( empty( $parent_objects ) ) {
348
- self::_clear_dir( $parent_dir );
349
  }
350
 
351
  return;
352
  }
353
 
354
- foreach ( $objects as $object ) {
355
- // full path
356
- $object = $dir . DIRECTORY_SEPARATOR . $object;
357
 
358
  // check if directory
359
- if ( is_dir( $object ) ) {
360
- self::_clear_dir( $object );
361
- } else {
362
- @unlink( $object );
 
363
  }
364
  }
365
 
366
  // delete directory
367
  @rmdir( $dir );
368
 
369
- // clears file status cache
370
  clearstatcache();
371
  }
372
 
373
 
374
  /**
375
- * get cache size
376
  *
377
  * @since 1.0.0
378
- * @change 1.0.0
379
  *
380
- * @param string $dir folder path
381
- * @return mixed $size size in bytes
382
  */
383
 
384
- public static function cache_size( $dir = '.' ) {
385
 
386
- // check if directory
387
- if ( ! is_dir( $dir ) ) {
388
- return;
 
 
 
389
  }
390
 
391
- // get directory data
392
- $objects = self::_get_dir( $dir );
393
 
394
- // check if empty
395
- if ( empty( $objects ) ) {
396
- return;
 
 
 
 
 
 
 
397
  }
398
 
399
- $size = 0;
 
 
 
400
 
401
- foreach ( $objects as $object ) {
402
- // full path
403
- $object = $dir . DIRECTORY_SEPARATOR . $object;
404
 
405
- // check if directory
406
- if ( is_dir( $object ) ) {
407
- $size += self::cache_size( $object );
408
- } else {
409
- $size += filesize( $object );
 
 
 
 
 
410
  }
411
  }
 
412
 
413
- return $size;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
414
  }
415
 
416
 
417
  /**
418
- * get cached file path
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
419
  *
420
  * @since 1.0.0
421
- * @change 1.4.8
422
  *
423
- * @param string $url full URL of a cached page
424
- * @return string path to cached file
425
  */
426
 
427
- private static function _file_path( $url = null ) {
428
 
429
- $file_path = sprintf(
430
- '%s%s%s%s',
431
- CE_CACHE_DIR,
432
- DIRECTORY_SEPARATOR,
433
  parse_url(
434
  ( $url ) ? $url : 'http://' . strtolower( $_SERVER['HTTP_HOST'] ),
435
  PHP_URL_HOST
@@ -440,256 +489,297 @@ final class Cache_Enabler_Disk {
440
  )
441
  );
442
 
443
- if ( is_file( $file_path ) ) {
444
  header( $_SERVER['SERVER_PROTOCOL'] . ' 404 Not Found', true, 404 );
445
  exit;
446
  }
447
 
448
- return trailingslashit( $file_path );
 
 
 
449
  }
450
 
451
 
452
  /**
453
- * get file scheme
454
  *
455
  * @since 1.4.0
456
- * @change 1.4.7
457
  *
458
  * @return string https or http
459
  */
460
 
461
- private static function _file_scheme() {
462
 
463
- return ( ( isset( $_SERVER['HTTPS'] ) && $_SERVER['HTTPS'] !== 'off' ) || $_SERVER['SERVER_PORT'] === '443' ) ? 'https' : 'http';
464
  }
465
 
466
 
467
  /**
468
- * get file path
469
  *
470
  * @since 1.0.0
471
  * @change 1.4.0
472
  *
473
- * @return string path to the HTML file
474
  */
475
 
476
- private static function _file_html() {
477
 
478
- return self::_file_path() . self::_file_scheme() . '-' . self::FILE_HTML;
479
  }
480
 
481
 
482
  /**
483
- * get gzip file path
484
  *
485
  * @since 1.0.1
486
  * @change 1.4.0
487
  *
488
- * @return string path to the gzipped HTML file
489
  */
490
 
491
- private static function _file_gzip() {
492
 
493
- return self::_file_path() . self::_file_scheme() . '-' . self::FILE_GZIP;
494
  }
495
 
496
 
497
  /**
498
- * get webp file path
499
  *
500
  * @since 1.0.7
501
  * @change 1.4.0
502
  *
503
- * @return string path to the webp HTML file
504
  */
505
 
506
- private static function _file_webp_html() {
507
 
508
- return self::_file_path() . self::_file_scheme() . '-' . self::FILE_WEBP_HTML;
509
  }
510
 
511
 
512
  /**
513
- * get gzip webp file path
514
  *
515
  * @since 1.0.1
516
  * @change 1.4.0
517
  *
518
- * @return string path to the webp gzipped HTML file
519
  */
520
 
521
- private static function _file_webp_gzip() {
522
 
523
- return self::_file_path() . self::_file_scheme() . '-' . self::FILE_WEBP_GZIP;
524
  }
525
 
526
 
527
  /**
528
- * get settings file
529
  *
530
- * @since 1.4.0
531
- * @change 1.4.8
532
- *
533
- * @return string settings file path
534
  */
535
 
536
- private static function _get_settings() {
537
 
538
- // network with subdirectory configuration
539
- if ( is_multisite() && ! is_subdomain_install() ) {
540
- // get blog path
541
- $blog_path = trim( get_blog_details()->path, '/' );
542
- // check if subsite
543
- if ( ! empty( $blog_path ) ) {
544
- $blog_path = '-' . $blog_path;
545
- }
546
- // single site, network subdirectory main site, or any network subdomain site
547
  } else {
548
- $blog_path = '';
 
 
549
  }
550
 
551
- // get settings file
552
- $settings_file = sprintf(
553
- '%s-%s%s.json',
554
- WP_CONTENT_DIR . '/plugins/cache-enabler/settings/cache-enabler-advcache',
555
- Cache_Enabler::get_blog_domain(),
556
- $blog_path
557
- );
558
 
559
- return $settings_file;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
560
  }
561
 
562
 
563
  /**
564
- * get directory data
565
  *
566
- * @since 1.4.7
567
- * @change 1.4.7
568
  *
569
- * @param string $dir directory path
570
- * @return array $objects directory objects
571
  */
572
 
573
- private static function _get_dir( $dir ) {
574
 
575
- // scan directory
576
- $data_dir = @scandir( $dir );
577
 
578
- if ( is_array( $data_dir ) ) {
579
- $objects = array_diff( $data_dir, array( '..', '.' ) );
580
- return $objects;
581
- }
582
- }
583
-
584
-
585
- /**
586
- * read settings file
587
- *
588
- * @since 1.2.3
589
- * @change 1.2.3
590
- *
591
- * @param string $settings_file settings file path
592
- * @return array settings or empty
593
- */
594
 
595
- private static function _read_settings( $settings_file ) {
 
 
596
 
597
- // check if settings file exists
598
- if ( ! file_exists( $settings_file ) ) {
599
- return array();
 
600
  }
601
 
602
- // check if any errors occur when reading the settings file
603
- if ( ! $settings = json_decode( file_get_contents( $settings_file ), true ) ) {
604
- return array();
605
- }
 
 
606
 
607
- return $settings;
608
  }
609
 
610
 
611
  /**
612
- * write settings file
613
  *
614
- * @since 1.2.3
615
- * @change 1.2.3
616
  *
617
- * @param string $settings_file settings file path
618
- * @param array $settings settings
619
  */
620
 
621
- private static function _write_settings( $settings_file, $settings ) {
622
 
623
- file_put_contents( $settings_file, wp_json_encode( $settings ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
624
  }
625
 
626
 
627
  /**
628
- * record settings for advanced-cache.php
629
  *
630
- * @since 1.2.3
631
- * @change 1.4.0
632
  *
633
- * @param array settings as array pairs
634
- * @return boolean true if successful
635
  */
636
 
637
- public static function record_advcache_settings( $settings ) {
638
 
639
- // get settings file
640
- $settings_file = self::_get_settings();
641
 
642
- // create folder if neccessary
643
- if ( ! wp_mkdir_p( dirname( $settings_file ) ) ) {
644
- wp_die( 'Unable to create directory.' );
645
  }
646
-
647
- // merge with old settings
648
- $settings = array_merge( self::_read_settings( $settings_file ), $settings );
649
-
650
- // update settings file
651
- self::_write_settings( $settings_file, $settings );
652
-
653
- return true;
654
  }
655
 
656
 
657
  /**
658
- * delete settings for advanced-cache.php
659
  *
660
- * @since 1.2.3
661
- * @change 1.4.0
662
  *
663
- * @param array settings keys as array or empty for delete all
664
- * @return boolean true if successful
665
  */
666
 
667
- public static function delete_advcache_settings( $settings_keys = array() ) {
668
 
669
- // get settings file
670
- $settings_file = self::_get_settings();
 
 
 
 
 
 
671
 
672
- // check if settings file exists
673
- if ( ! file_exists( $settings_file ) ) {
674
- return true;
675
  }
676
 
677
- $settings = self::_read_settings( $settings_file );
678
- foreach ( $settings_keys as $key ) {
679
- if ( array_key_exists( $key, $settings ) ) {
680
- unset( $settings[ $key ] );
681
- }
 
682
  }
683
 
684
- if ( empty( $settings ) || empty( $settings_keys ) ) {
685
- unlink( $settings_file );
686
- return true;
 
 
 
 
 
 
 
 
687
  }
688
 
689
- // update settings file
690
- self::_write_settings( $settings_file, $settings );
 
 
 
691
 
692
- return true;
 
693
  }
694
 
695
 
@@ -697,56 +787,63 @@ final class Cache_Enabler_Disk {
697
  * get image path
698
  *
699
  * @since 1.4.8
700
- * @change 1.4.8
701
  *
702
- * @param string $image_url full or relative URL with or without intrinsic width
703
  * @return string $image_path path to image
704
  */
705
 
706
- private static function _image_path( $image_url ) {
707
 
708
- // in case image has intrinsic width
709
  $image_parts = explode( ' ', $image_url );
710
  $image_url = $image_parts[0];
711
- $image_path = ABSPATH . ltrim( parse_url( $image_url, PHP_URL_PATH ), '/' );
 
 
 
 
712
 
713
  return $image_path;
714
  }
715
 
716
 
717
  /**
718
- * convert image URL to WebP
719
  *
720
  * @since 1.0.1
721
- * @change 1.4.9
722
  *
723
- * @param array $matches pattern matches from parsed HTML file
724
  * @return string $conversion converted image URL(s) to WebP if applicable, default URL(s) otherwise
725
  */
726
 
727
- private static function _convert_webp( $matches ) {
728
 
729
  $full_match = $matches[0];
730
- $image_count = preg_match_all( '/(\.jpe?g|\.png)/i', $full_match );
 
731
 
732
- if ( $image_count > 0 ) {
 
733
  $image_urls = explode( ',', $full_match );
 
734
  foreach ( $image_urls as &$image_url ) {
735
  // remove spaces if there are any
736
  $image_url = trim( $image_url, ' ' );
737
  // append .webp extension
738
- $image_url_webp = preg_replace( '/(\.jpe?g|\.png)/i', '$1.webp', $image_url );
739
  // get WebP image path
740
- $image_path_webp = self::_image_path( $image_url_webp );
741
 
742
  // check if WebP image exists
743
  if ( is_file( $image_path_webp ) ) {
744
  $image_url = $image_url_webp;
745
  } else {
746
  // remove default extension
747
- $image_url_webp = preg_replace( '/(\.jpe?g|\.png)/i', '', $image_url_webp );
748
  // get WebP image path
749
- $image_path_webp = self::_image_path( $image_url_webp );
750
 
751
  // check if WebP image exists
752
  if ( is_file( $image_path_webp ) ) {
@@ -760,4 +857,102 @@ final class Cache_Enabler_Disk {
760
  return $conversion;
761
  }
762
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
763
  }
1
  <?php
 
 
 
 
 
 
2
  /**
3
+ * Cache Enabler disk handling
4
  *
5
  * @since 1.0.0
6
  */
7
 
8
+ if ( ! defined( 'ABSPATH' ) ) {
9
+ exit;
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
  */
21
 
22
+ public static $cache_dir = WP_CONTENT_DIR . '/cache/cache-enabler';
 
 
 
 
23
 
24
 
25
  /**
26
+ * settings directory
27
  *
28
+ * @since 1.5.0
29
+ * @change 1.5.0
30
  *
 
31
  */
32
 
33
+ private static $settings_dir = WP_CONTENT_DIR . '/settings/cache-enabler';
 
 
 
34
 
35
 
36
  /**
37
+ * base cache file names
38
  *
39
+ * @since 1.0.7
40
+ * @change 1.5.0
41
  *
42
+ * @var string
43
  */
44
 
45
+ const CACHE_FILE_GLOB = '*index*';
46
+ const CACHE_FILE_HTML = 'index.html';
47
+ const CACHE_FILE_GZIP = 'index.html.gz';
48
+ const CACHE_FILE_WEBP_HTML = 'index-webp.html';
49
+ const CACHE_FILE_WEBP_GZIP = 'index-webp.html.gz';
 
 
 
 
 
50
 
51
 
52
  /**
53
+ * configure system files
 
 
 
54
  *
55
+ * @since 1.5.0
56
+ * @change 1.5.0
57
  */
58
 
59
+ public static function setup() {
60
+
61
+ // add advanced-cache.php drop-in
62
+ copy( CE_DIR . '/advanced-cache.php', WP_CONTENT_DIR . '/advanced-cache.php' );
63
 
64
+ // set WP_CACHE constant in config file if not already set
65
+ self::set_wp_cache_constant();
66
  }
67
 
68
 
69
  /**
70
+ * clean system files
71
  *
72
+ * @since 1.5.0
73
+ * @change 1.5.0
 
 
74
  */
75
 
76
+ public static function clean() {
77
+
78
+ // delete settings file
79
+ self::delete_settings_file();
80
+
81
+ // check if settings directory exists
82
+ if ( ! is_dir( self::$settings_dir ) ) {
83
+ // delete old advanced cache settings file(s) (1.4.0)
84
+ array_map( 'unlink', glob( WP_CONTENT_DIR . '/cache/cache-enabler-advcache-*.json' ) );
85
+ // delete incorrect advanced cache settings file(s) that may have been created in 1.4.0 (1.4.5)
86
+ array_map( 'unlink', glob( ABSPATH . 'CE_SETTINGS_PATH-*.json' ) );
87
+ // delete advanced-cache.php drop-in
88
+ @unlink( WP_CONTENT_DIR . '/advanced-cache.php' );
89
+ // unset WP_CACHE constant in config file if set by Cache Enabler
90
+ self::set_wp_cache_constant( false );
91
  }
 
 
 
 
 
 
 
 
 
 
92
  }
93
 
94
 
95
  /**
96
+ * store cached page(s)
97
  *
98
  * @since 1.0.0
99
+ * @change 1.5.0
100
  *
101
+ * @param string $page_contents content of a page from the output buffer
 
102
  */
103
 
104
+ public static function cache_page( $page_contents ) {
105
 
106
+ // check if page is empty
107
+ if ( empty( $page_contents ) ) {
108
+ return;
 
 
 
 
 
 
 
 
 
109
  }
110
+
111
+ // create cached page(s)
112
+ self::create_cache_files( $page_contents );
113
  }
114
 
115
 
116
  /**
117
+ * check if cached page exists
118
  *
119
  * @since 1.0.0
120
  * @change 1.0.0
121
+ *
122
+ * @return boolean true if cached page exists, false otherwise
123
  */
124
 
125
+ public static function cache_exists() {
126
 
127
+ return is_readable( self::cache_file_html() );
 
128
  }
129
 
130
 
131
  /**
132
+ * check if cached page expired
133
  *
134
+ * @since 1.0.1
135
+ * @change 1.5.0
136
+ *
137
+ * @return boolean true if cached page expired, false otherwise
138
  */
139
 
140
+ public static function cache_expired() {
 
 
 
141
 
142
+ // check if cached pages are set to expire
143
+ if ( ! Cache_Enabler_Engine::$settings['cache_expires'] || Cache_Enabler_Engine::$settings['cache_expiry_time'] === 0 ) {
144
+ return false;
 
 
 
 
 
 
 
 
 
 
 
 
 
145
  }
146
 
147
+ $now = time();
148
+ $expires_seconds = HOUR_IN_SECONDS * Cache_Enabler_Engine::$settings['cache_expiry_time'];
 
 
 
 
 
 
 
 
 
149
 
150
+ // check if cached page has expired
151
+ if ( ( filemtime( self::cache_file_html() ) + $expires_seconds ) <= $now ) {
152
+ return true;
 
 
153
  }
154
 
155
+ return false;
 
 
156
  }
157
 
158
 
160
  * create signature
161
  *
162
  * @since 1.0.0
163
+ * @change 1.5.0
164
  *
165
  * @return string signature
166
  */
167
 
168
+ private static function cache_signature() {
169
 
170
  return sprintf(
171
+ '<!-- %s @ %s',
172
  'Cache Enabler by KeyCDN',
173
+ date_i18n( 'd.m.Y H:i:s', current_time( 'timestamp' ) )
 
 
 
174
  );
175
  }
176
 
177
 
178
  /**
179
+ * get cache size
180
  *
181
  * @since 1.0.0
182
+ * @change 1.5.0
183
  *
184
+ * @param string $dir file system directory
185
+ * @return integer $size size in bytes
186
  */
187
 
188
+ public static function cache_size( $dir = null ) {
 
 
 
189
 
190
+ // set directory if provided, get directory otherwise
191
+ $dir = ( $dir ) ? $dir : self::cache_file_dir_path( home_url() );
192
 
193
+ // validate directory
194
+ if ( ! is_dir( $dir ) ) {
195
+ return;
196
  }
197
 
198
+ // get directory data
199
+ $dir_objects = self::get_dir_objects( $dir );
200
 
201
+ // check if empty
202
+ if ( empty( $dir_objects ) ) {
203
+ return;
204
  }
205
 
206
+ $size = 0;
 
 
 
 
 
 
207
 
208
+ foreach ( $dir_objects as $dir_object ) {
209
+ // get full path
210
+ $dir_object = $dir . '/' . $dir_object;
211
 
212
+ // check if directory
213
+ if ( is_dir( $dir_object ) ) {
214
+ $size += self::cache_size( $dir_object );
215
+ // check if file otherwise
216
+ } elseif ( is_file( $dir_object ) ) {
217
+ $size += filesize( $dir_object );
218
  }
219
  }
220
+
221
+ return $size;
222
  }
223
 
224
 
225
  /**
226
+ * clear cached page(s)
227
  *
228
  * @since 1.0.0
229
+ * @change 1.5.0
230
  *
231
+ * @param string $clear_url full URL to potentially cached page
232
+ * @param string $clear_type clear the `pagination` or the entire `dir` instead of only the cached `page`
233
  */
234
 
235
+ public static function clear_cache( $clear_url = null, $clear_type = null ) {
236
 
237
+ // check if complete cache should be cleared
238
+ if ( empty( $clear_url ) || empty( $clear_type ) ) {
239
+ self::clear_dir( self::$cache_dir );
240
+ return;
241
  }
242
 
243
+ // get directory
244
+ $dir = self::cache_file_dir_path( $clear_url );
 
 
245
 
246
+ // delete all cached variants in directory
247
+ array_map( 'unlink', glob( $dir . self::CACHE_FILE_GLOB ) );
248
+
249
+ // check if pagination needs to be cleared
250
+ if ( $clear_type === 'pagination' ) {
251
+ // get pagination base
252
+ $pagination_base = $GLOBALS['wp_rewrite']->pagination_base;
253
+ if ( strlen( $pagination_base ) > 0 ) {
254
+ $pagination_dir = $dir . $pagination_base;
255
+ // clear pagination page(s) cache
256
+ self::clear_dir( $pagination_dir );
257
+ }
258
+ }
259
+
260
+ // get directory data
261
+ $dir_objects = self::get_dir_objects( $dir );
262
+
263
+ // check if directory is now empty or if it needs to be cleared anyways
264
+ if ( empty( $dir_objects ) || $clear_type === 'dir' ) {
265
+ self::clear_dir( $dir );
266
+ }
267
  }
268
 
269
 
271
  * clear directory
272
  *
273
  * @since 1.0.0
274
+ * @change 1.5.0
275
  *
276
  * @param string $dir directory
277
  */
278
 
279
+ private static function clear_dir( $dir ) {
280
 
281
+ // remove trailing slash
282
  $dir = untrailingslashit( $dir );
283
 
284
+ // validate directory
285
  if ( ! is_dir( $dir ) ) {
286
  return;
287
  }
288
 
289
  // get directory data
290
+ $dir_objects = self::get_dir_objects( $dir );
291
 
292
  // check if directory is empty
293
+ if ( empty( $dir_objects ) ) {
294
  // delete empty directory
295
  @rmdir( $dir );
296
 
297
+ // clear file status cache
298
+ clearstatcache();
299
+
300
  // get parent directory
301
  $parent_dir = preg_replace( '/\/[^\/]+$/', '', $dir );
302
 
303
  // get parent directory data
304
+ $parent_dir_objects = self::get_dir_objects( $parent_dir );
305
 
306
  // check if parent directory is also empty
307
+ if ( empty( $parent_dir_objects ) ) {
308
+ self::clear_dir( $parent_dir );
309
  }
310
 
311
  return;
312
  }
313
 
314
+ foreach ( $dir_objects as $dir_object ) {
315
+ // get full path
316
+ $dir_object = $dir . '/' . $dir_object;
317
 
318
  // check if directory
319
+ if ( is_dir( $dir_object ) ) {
320
+ self::clear_dir( $dir_object );
321
+ // check if file otherwise
322
+ } elseif ( is_file( $dir_object ) ) {
323
+ unlink( $dir_object );
324
  }
325
  }
326
 
327
  // delete directory
328
  @rmdir( $dir );
329
 
330
+ // clear file status cache
331
  clearstatcache();
332
  }
333
 
334
 
335
  /**
336
+ * create files for cache
337
  *
338
  * @since 1.0.0
339
+ * @change 1.5.0
340
  *
341
+ * @param string $page_contents content of a page from the output buffer
 
342
  */
343
 
344
+ private static function create_cache_files( $page_contents ) {
345
 
346
+ // get base signature
347
+ $cache_signature = self::cache_signature();
348
+
349
+ // make directory if necessary
350
+ if ( ! wp_mkdir_p( self::cache_file_dir_path() ) ) {
351
+ wp_die( 'Unable to create directory.' );
352
  }
353
 
354
+ // minify HTML
355
+ $page_contents = self::minify_html( $page_contents );
356
 
357
+ // create default file
358
+ self::create_cache_file( self::cache_file_html(), $page_contents . $cache_signature . ' (' . self::cache_file_scheme() . ' html) -->' );
359
+
360
+ // create pre-compressed file
361
+ if ( Cache_Enabler_Engine::$settings['compress_cache'] ) {
362
+ $compressed_page_contents = gzencode( $page_contents . $cache_signature . ' (' . self::cache_file_scheme() . ' gzip) -->', 9 );
363
+ // validate compression
364
+ if ( is_string( $compressed_page_contents ) ) {
365
+ self::create_cache_file( self::cache_file_gzip(), $compressed_page_contents );
366
+ }
367
  }
368
 
369
+ // create WebP supported files
370
+ if ( Cache_Enabler_Engine::$settings['convert_image_urls_to_webp'] ) {
371
+ // magic regex rule
372
+ $image_urls_regex = '#(?:(?:(src|srcset|data-[^=]+)\s*=|(url)\()\s*[\'\"]?\s*)\K(?:[^\?\"\'\s>]+)(?:\.jpe?g|\.png)(?:\s\d+[wx][^\"\'>]*)?(?=\/?[\"\'\s\)>])(?=[^<{]*(?:\)[^<{]*\}|>))#i';
373
 
374
+ // call the WebP converter callback
375
+ $converted_page_contents = apply_filters( 'cache_enabler_disk_webp_converted_data', preg_replace_callback( $image_urls_regex, 'self::convert_webp', $page_contents ) );
 
376
 
377
+ // create default WebP file
378
+ self::create_cache_file( self::cache_file_webp_html(), $converted_page_contents . $cache_signature . ' (' . self::cache_file_scheme() . ' webp html) -->' );
379
+
380
+ // create pre-compressed file
381
+ if ( Cache_Enabler_Engine::$settings['compress_cache'] ) {
382
+ $compressed_converted_page_contents = gzencode( $converted_page_contents . $cache_signature . ' (' . self::cache_file_scheme() . ' webp gzip) -->', 9 );
383
+ // validate compression
384
+ if ( is_string( $compressed_converted_page_contents ) ) {
385
+ self::create_cache_file( self::cache_file_webp_gzip(), $compressed_converted_page_contents );
386
+ }
387
  }
388
  }
389
+ }
390
 
391
+
392
+ /**
393
+ * create file for cache
394
+ *
395
+ * @since 1.0.0
396
+ * @change 1.5.0
397
+ *
398
+ * @param string $file_path file path
399
+ * @param string $page_contents content of a page from the output buffer
400
+ */
401
+
402
+ private static function create_cache_file( $file_path, $page_contents ) {
403
+
404
+ // write page contents from output buffer to file
405
+ file_put_contents( $file_path, $page_contents );
406
+
407
+ // clear file status cache
408
+ clearstatcache();
409
+
410
+ // set permissions
411
+ $file_stats = @stat( dirname( $file_path ) );
412
+ $permissions = $file_stats['mode'] & 0007777;
413
+ $permissions = $permissions & 0000666;
414
+ @chmod( $file_path, $permissions );
415
+
416
+ // clear file status cache
417
+ clearstatcache();
418
  }
419
 
420
 
421
  /**
422
+ * create settings file
423
+ *
424
+ * @since 1.2.3
425
+ * @change 1.5.0
426
+ *
427
+ * @param array $settings settings from database
428
+ */
429
+
430
+ public static function create_settings_file( $settings ) {
431
+
432
+ // validate array
433
+ if ( ! is_array( $settings ) ) {
434
+ return;
435
+ }
436
+
437
+ // check settings file requirements
438
+ if ( ! function_exists( 'home_url' ) ) {
439
+ return;
440
+ }
441
+
442
+ // get settings file
443
+ $settings_file = self::get_settings_file();
444
+
445
+ // make directory if necessary
446
+ if ( ! wp_mkdir_p( dirname( $settings_file ) ) ) {
447
+ wp_die( 'Unable to create directory.' );
448
+ }
449
+
450
+ // create new settings file
451
+ $new_settings_file_contents = '<?php' . PHP_EOL;
452
+ $new_settings_file_contents .= '/**' . PHP_EOL;
453
+ $new_settings_file_contents .= ' * Cache Enabler settings for ' . home_url() . PHP_EOL;
454
+ $new_settings_file_contents .= ' *' . PHP_EOL;
455
+ $new_settings_file_contents .= ' * @since 1.5.0' . PHP_EOL;
456
+ $new_settings_file_contents .= ' * @change 1.5.0' . PHP_EOL;
457
+ $new_settings_file_contents .= ' *' . PHP_EOL;
458
+ $new_settings_file_contents .= ' * @generated ' . date_i18n( 'd.m.Y H:i:s', current_time( 'timestamp' ) ) . PHP_EOL;
459
+ $new_settings_file_contents .= ' */' . PHP_EOL;
460
+ $new_settings_file_contents .= PHP_EOL;
461
+ $new_settings_file_contents .= 'return ' . var_export( $settings, true ) . ';';
462
+
463
+ file_put_contents( $settings_file, $new_settings_file_contents );
464
+ }
465
+
466
+
467
+ /**
468
+ * get cache file directory path
469
  *
470
  * @since 1.0.0
471
+ * @change 1.5.0
472
  *
473
+ * @param string $url full URL to potentially cached page
474
+ * @return string $file_dir_path file directory path to new or potentially cached page
475
  */
476
 
477
+ private static function cache_file_dir_path( $url = null ) {
478
 
479
+ $file_dir_path = sprintf(
480
+ '%s/%s%s',
481
+ self::$cache_dir,
 
482
  parse_url(
483
  ( $url ) ? $url : 'http://' . strtolower( $_SERVER['HTTP_HOST'] ),
484
  PHP_URL_HOST
489
  )
490
  );
491
 
492
+ if ( is_file( $file_dir_path ) ) {
493
  header( $_SERVER['SERVER_PROTOCOL'] . ' 404 Not Found', true, 404 );
494
  exit;
495
  }
496
 
497
+ // add trailing slash
498
+ $file_dir_path = rtrim( $file_dir_path, '/\\' ) . '/';
499
+
500
+ return $file_dir_path;
501
  }
502
 
503
 
504
  /**
505
+ * get cache file scheme
506
  *
507
  * @since 1.4.0
508
+ * @change 1.5.0
509
  *
510
  * @return string https or http
511
  */
512
 
513
+ private static function cache_file_scheme() {
514
 
515
+ return ( ( isset( $_SERVER['HTTPS'] ) && $_SERVER['HTTPS'] !== 'off' ) || $_SERVER['SERVER_PORT'] == '443' ) ? 'https' : 'http';
516
  }
517
 
518
 
519
  /**
520
+ * get complete cache file path (HTML)
521
  *
522
  * @since 1.0.0
523
  * @change 1.4.0
524
  *
525
+ * @return string file path to new or potentially cached page
526
  */
527
 
528
+ private static function cache_file_html() {
529
 
530
+ return self::cache_file_dir_path() . self::cache_file_scheme() . '-' . self::CACHE_FILE_HTML;
531
  }
532
 
533
 
534
  /**
535
+ * get complete cache file path (Gzip)
536
  *
537
  * @since 1.0.1
538
  * @change 1.4.0
539
  *
540
+ * @return string file path to new or potentially cached page
541
  */
542
 
543
+ private static function cache_file_gzip() {
544
 
545
+ return self::cache_file_dir_path() . self::cache_file_scheme() . '-' . self::CACHE_FILE_GZIP;
546
  }
547
 
548
 
549
  /**
550
+ * get complete cache file path (WebP HTML)
551
  *
552
  * @since 1.0.7
553
  * @change 1.4.0
554
  *
555
+ * @return string file path to new or potentially cached page
556
  */
557
 
558
+ private static function cache_file_webp_html() {
559
 
560
+ return self::cache_file_dir_path() . self::cache_file_scheme() . '-' . self::CACHE_FILE_WEBP_HTML;
561
  }
562
 
563
 
564
  /**
565
+ * get complete cache file path (WebP Gzip)
566
  *
567
  * @since 1.0.1
568
  * @change 1.4.0
569
  *
570
+ * @return string file path to new or potentially cached page
571
  */
572
 
573
+ private static function cache_file_webp_gzip() {
574
 
575
+ return self::cache_file_dir_path() . self::cache_file_scheme() . '-' . self::CACHE_FILE_WEBP_GZIP;
576
  }
577
 
578
 
579
  /**
580
+ * get cached page
581
  *
582
+ * @since 1.0.0
583
+ * @change 1.5.0
 
 
584
  */
585
 
586
+ public static function get_cache() {
587
 
588
+ // set X-Cache-Handler response header
589
+ header( 'X-Cache-Handler: cache-enabler-engine' );
590
+
591
+ // get request headers
592
+ if ( function_exists( 'apache_request_headers' ) ) {
593
+ $headers = apache_request_headers();
594
+ $http_if_modified_since = ( isset( $headers[ 'If-Modified-Since' ] ) ) ? $headers[ 'If-Modified-Since' ] : '';
595
+ $http_accept = ( isset( $headers[ 'Accept' ] ) ) ? $headers[ 'Accept' ] : '';
596
+ $http_accept_encoding = ( isset( $headers[ 'Accept-Encoding' ] ) ) ? $headers[ 'Accept-Encoding' ] : '';
597
  } else {
598
+ $http_if_modified_since = ( isset( $_SERVER[ 'HTTP_IF_MODIFIED_SINCE' ] ) ) ? $_SERVER[ 'HTTP_IF_MODIFIED_SINCE' ] : '';
599
+ $http_accept = ( isset( $_SERVER[ 'HTTP_ACCEPT' ] ) ) ? $_SERVER[ 'HTTP_ACCEPT' ] : '';
600
+ $http_accept_encoding = ( isset( $_SERVER[ 'HTTP_ACCEPT_ENCODING' ] ) ) ? $_SERVER[ 'HTTP_ACCEPT_ENCODING' ] : '';
601
  }
602
 
603
+ // check modified since with cached file and return 304 if no difference
604
+ if ( $http_if_modified_since && ( strtotime( $http_if_modified_since ) >= filemtime( self::cache_file_html() ) ) ) {
605
+ header( $_SERVER['SERVER_PROTOCOL'] . ' 304 Not Modified', true, 304 );
606
+ exit;
607
+ }
 
 
608
 
609
+ // check webp and deliver gzip webp file if support
610
+ if ( $http_accept && ( strpos( $http_accept, 'webp' ) !== false ) ) {
611
+ if ( is_readable( self::cache_file_webp_gzip() ) ) {
612
+ header( 'Content-Encoding: gzip' );
613
+ return self::cache_file_webp_gzip();
614
+ } elseif ( is_readable( self::cache_file_webp_html() ) ) {
615
+ return self::cache_file_webp_html();
616
+ }
617
+ }
618
+
619
+ // check encoding and deliver gzip file if support
620
+ if ( $http_accept_encoding && ( strpos( $http_accept_encoding, 'gzip' ) !== false ) && is_readable( self::cache_file_gzip() ) ) {
621
+ header( 'Content-Encoding: gzip' );
622
+ return self::cache_file_gzip();
623
+ }
624
+
625
+ // get default cached file
626
+ return self::cache_file_html();
627
  }
628
 
629
 
630
  /**
631
+ * get settings file
632
  *
633
+ * @since 1.4.0
634
+ * @change 1.5.0
635
  *
636
+ * @param boolean $fallback whether or not to provide fallback settings file path
637
+ * @return string $settings_file settings file path
638
  */
639
 
640
+ private static function get_settings_file( $fallback = false ) {
641
 
642
+ // single site not in subdirectory, any site of subdomain network, or main site of subdirectory network (fallback)
643
+ $blog_path = '';
644
 
645
+ // get URL path from home or request URL
646
+ if ( ! $fallback ) {
647
+ if ( function_exists( 'home_url' ) ) {
648
+ $url_path = parse_url( home_url( '/' ), PHP_URL_PATH ); // trailing slash required
649
+ } else {
650
+ $url_path = $_SERVER['REQUEST_URI'];
651
+ }
 
 
 
 
 
 
 
 
 
652
 
653
+ // get subdirectory network blog path or subdirectory installation path
654
+ $url_path_pieces = explode( '/', $url_path, 3 );
655
+ $blog_path = $url_path_pieces[1];
656
 
657
+ // subdirectory network or installation
658
+ if ( ! empty( $blog_path ) ) {
659
+ $blog_path = '.' . $blog_path;
660
+ }
661
  }
662
 
663
+ // get settings file
664
+ $settings_file = sprintf(
665
+ '%s/%s.php',
666
+ self::$settings_dir,
667
+ parse_url( ( function_exists( 'home_url' ) ) ? home_url() : 'http://' . strtolower( $_SERVER['HTTP_HOST'] ), PHP_URL_HOST ) . $blog_path
668
+ );
669
 
670
+ return $settings_file;
671
  }
672
 
673
 
674
  /**
675
+ * get settings from settings file
676
  *
677
+ * @since 1.5.0
678
+ * @change 1.5.0
679
  *
680
+ * @return array $settings current settings from settings file
 
681
  */
682
 
683
+ public static function get_settings() {
684
 
685
+ // get settings file
686
+ $settings_file = self::get_settings_file();
687
+
688
+ // include existing settings file
689
+ if ( file_exists( $settings_file ) ) {
690
+ $settings = include_once $settings_file;
691
+ // if settings file does not exist try to get fallback settings file when network with subdirectory configuration
692
+ } elseif ( is_multisite() && defined( 'SUBDOMAIN_INSTALL' ) && ! SUBDOMAIN_INSTALL ) {
693
+ $fallback = true;
694
+ $fallback_settings_file = self::get_settings_file( $fallback );
695
+ // include existing fallback settings file
696
+ if ( file_exists( $fallback_settings_file ) ) {
697
+ $settings = include_once $fallback_settings_file;
698
+ }
699
+ } else {
700
+ $settings = array();
701
+ }
702
+
703
+ return $settings;
704
  }
705
 
706
 
707
  /**
708
+ * get directory file system objects
709
  *
710
+ * @since 1.4.7
711
+ * @change 1.5.0
712
  *
713
+ * @param string $dir directory path
714
+ * @return array $dir_objects directory objects
715
  */
716
 
717
+ private static function get_dir_objects( $dir ) {
718
 
719
+ // scan directory
720
+ $dir_data = @scandir( $dir );
721
 
722
+ if ( is_array( $dir_data ) ) {
723
+ $dir_objects = array_diff( $dir_data, array( '..', '.' ) );
724
+ return $dir_objects;
725
  }
 
 
 
 
 
 
 
 
726
  }
727
 
728
 
729
  /**
730
+ * set or unset WP_CACHE constant in wp-config.php
731
  *
732
+ * @since 1.1.1
733
+ * @change 1.5.0
734
  *
735
+ * @param boolean $set true to set WP_CACHE constant, false to unset
 
736
  */
737
 
738
+ private static function set_wp_cache_constant( $set = true ) {
739
 
740
+ // get config file
741
+ if ( file_exists( ABSPATH . 'wp-config.php' ) ) {
742
+ // config file resides in ABSPATH
743
+ $wp_config_file = ABSPATH . 'wp-config.php';
744
+ } elseif ( @file_exists( dirname( ABSPATH ) . '/wp-config.php' ) && ! @file_exists( dirname( ABSPATH ) . '/wp-settings.php' ) ) {
745
+ // config file resides one level above ABSPATH but is not part of another installation
746
+ $wp_config_file = dirname( ABSPATH ) . '/wp-config.php';
747
+ }
748
 
749
+ // check if config file can be written to
750
+ if ( ! is_writable( $wp_config_file ) ) {
751
+ return;
752
  }
753
 
754
+ // get config file contents
755
+ $wp_config_file_contents = file_get_contents( $wp_config_file );
756
+
757
+ // validate config file
758
+ if ( ! is_string( $wp_config_file_contents ) ) {
759
+ return;
760
  }
761
 
762
+ // search for WP_CACHE constant
763
+ $found_wp_cache_constant = preg_match( '/define\s*\(\s*[\'\"]WP_CACHE[\'\"]\s*,.+\);/', $wp_config_file_contents );
764
+
765
+ // if not found set WP_CACHE constant when config file is default (must be before WordPress sets up)
766
+ if ( $set && ! $found_wp_cache_constant ) {
767
+ $ce_wp_config_lines = '/** Enables page caching for Cache Enabler. */' . PHP_EOL;
768
+ $ce_wp_config_lines .= "if ( ! defined( 'WP_CACHE' ) ) {" . PHP_EOL;
769
+ $ce_wp_config_lines .= "\tdefine( 'WP_CACHE', true );" . PHP_EOL;
770
+ $ce_wp_config_lines .= '}' . PHP_EOL;
771
+ $ce_wp_config_lines .= PHP_EOL;
772
+ $wp_config_file_contents = preg_replace( '/(\/\*\* Sets up WordPress vars and included files\. \*\/)/', $ce_wp_config_lines . '$1', $wp_config_file_contents );
773
  }
774
 
775
+ // unset WP_CACHE constant if set by Cache Enabler
776
+ if ( ! $set ) {
777
+ $wp_config_file_contents = preg_replace( '/.+Added by Cache Enabler\r\n/', '', $wp_config_file_contents ); // < 1.5.0
778
+ $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 );
779
+ }
780
 
781
+ // update config file
782
+ file_put_contents( $wp_config_file, $wp_config_file_contents );
783
  }
784
 
785
 
787
  * get image path
788
  *
789
  * @since 1.4.8
790
+ * @change 1.5.0
791
  *
792
+ * @param string $image_url full or relative URL with or without intrinsic width or density descriptor
793
  * @return string $image_path path to image
794
  */
795
 
796
+ private static function image_path( $image_url ) {
797
 
798
+ // in case image has intrinsic width or density descriptor
799
  $image_parts = explode( ' ', $image_url );
800
  $image_url = $image_parts[0];
801
+
802
+ // in case installation is in a subdirectory
803
+ $image_url_path = ltrim( parse_url( $image_url, PHP_URL_PATH ), '/' );
804
+ $installation_dir = preg_replace( '/^[^\/]+\/\K.+/', '', $image_url_path );
805
+ $image_path = str_replace( $installation_dir, '', ABSPATH ) . $image_url_path;
806
 
807
  return $image_path;
808
  }
809
 
810
 
811
  /**
812
+ * convert image URL(s) to WebP
813
  *
814
  * @since 1.0.1
815
+ * @change 1.5.0
816
  *
817
+ * @param array $matches pattern matches from parsed page contents
818
  * @return string $conversion converted image URL(s) to WebP if applicable, default URL(s) otherwise
819
  */
820
 
821
+ private static function convert_webp( $matches ) {
822
 
823
  $full_match = $matches[0];
824
+ $image_extension_regex = '/(\.jpe?g|\.png)/i';
825
+ $image_found = preg_match( $image_extension_regex, $full_match );
826
 
827
+ if ( $image_found ) {
828
+ // set image URL(s)
829
  $image_urls = explode( ',', $full_match );
830
+
831
  foreach ( $image_urls as &$image_url ) {
832
  // remove spaces if there are any
833
  $image_url = trim( $image_url, ' ' );
834
  // append .webp extension
835
+ $image_url_webp = preg_replace( $image_extension_regex, '$1.webp', $image_url );
836
  // get WebP image path
837
+ $image_path_webp = self::image_path( $image_url_webp );
838
 
839
  // check if WebP image exists
840
  if ( is_file( $image_path_webp ) ) {
841
  $image_url = $image_url_webp;
842
  } else {
843
  // remove default extension
844
+ $image_url_webp = preg_replace( $image_extension_regex, '', $image_url_webp );
845
  // get WebP image path
846
+ $image_path_webp = self::image_path( $image_url_webp );
847
 
848
  // check if WebP image exists
849
  if ( is_file( $image_path_webp ) ) {
857
  return $conversion;
858
  }
859
  }
860
+
861
+
862
+ /**
863
+ * minify HTML
864
+ *
865
+ * @since 1.0.0
866
+ * @change 1.5.0
867
+ *
868
+ * @param string $page_contents content of a page from the output buffer
869
+ * @return string $minified_html|$page_contents minified page contents if applicable, unchanged otherwise
870
+ *
871
+ * @hook array cache_minify_ignore_tags
872
+ */
873
+
874
+ private static function minify_html( $page_contents ) {
875
+
876
+ // check if disabled
877
+ if ( ! Cache_Enabler_Engine::$settings['minify_html'] ) {
878
+ return $page_contents;
879
+ }
880
+
881
+ // HTML character limit
882
+ if ( strlen( $page_contents ) > 700000) {
883
+ return $page_contents;
884
+ }
885
+
886
+ // HTML tags to ignore
887
+ $ignore_tags = (array) apply_filters( 'cache_minify_ignore_tags', array( 'textarea', 'pre', 'code' ) );
888
+
889
+ // if selected exclude inline CSS and JavaScript
890
+ if ( ! Cache_Enabler_Engine::$settings['minify_inline_css_js'] ) {
891
+ array_push( $ignore_tags, 'style', 'script' );
892
+ }
893
+
894
+ // check if there are ignore tags
895
+ if ( ! $ignore_tags ) {
896
+ return $page_contents;
897
+ }
898
+
899
+ // stringify
900
+ $ignore_regex = implode( '|', $ignore_tags );
901
+
902
+ // regex minification
903
+ $minified_html = preg_replace(
904
+ array(
905
+ '/<!--[^\[><](.*?)-->/s',
906
+ '#(?ix)(?>[^\S ]\s*|\s{2,})(?=(?:(?:[^<]++|<(?!/?(?:' . $ignore_regex . ')\b))*+)(?:<(?>' . $ignore_regex . ')\b|\z))#',
907
+ ),
908
+ array(
909
+ '',
910
+ ' ',
911
+ ),
912
+ $page_contents
913
+ );
914
+
915
+ // something went wrong
916
+ if ( strlen( $minified_html ) <= 1 ) {
917
+ return $page_contents;
918
+ }
919
+
920
+ return $minified_html;
921
+ }
922
+
923
+
924
+ /**
925
+ * delete settings file
926
+ *
927
+ * @since 1.5.0
928
+ * @change 1.5.0
929
+ */
930
+
931
+ private static function delete_settings_file() {
932
+
933
+ // get settings file
934
+ $settings_file = self::get_settings_file();
935
+
936
+ // delete settings file
937
+ @unlink( $settings_file );
938
+
939
+ // delete settings directory if empty
940
+ @rmdir( self::$settings_dir );
941
+ }
942
+
943
+
944
+ /**
945
+ * delete asset (deprecated)
946
+ *
947
+ * @deprecated 1.5.0
948
+ */
949
+
950
+ public static function delete_asset( $url ) {
951
+
952
+ if ( empty( $url ) ) {
953
+ wp_die( 'URL is empty.' );
954
+ }
955
+
956
+ self::clear_dir( self::cache_file_dir_path( $url ) );
957
+ }
958
  }
inc/cache_enabler_engine.class.php ADDED
@@ -0,0 +1,358 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Cache Enabler engine
4
+ *
5
+ * @since 1.5.0
6
+ */
7
+
8
+ if ( ! defined( 'ABSPATH' ) ) {
9
+ exit;
10
+ }
11
+
12
+ final class Cache_Enabler_Engine {
13
+
14
+ /**
15
+ * engine status
16
+ *
17
+ * @since 1.5.0
18
+ * @change 1.5.0
19
+ *
20
+ * @var boolean
21
+ */
22
+
23
+ public static $started = false;
24
+
25
+
26
+ /**
27
+ * engine settings from disk or database
28
+ *
29
+ * @since 1.5.0
30
+ * @change 1.5.0
31
+ *
32
+ * @var array
33
+ */
34
+
35
+ public static $settings;
36
+
37
+
38
+ /**
39
+ * constructor
40
+ *
41
+ * @since 1.5.0
42
+ * @change 1.5.0
43
+ */
44
+
45
+ public function __construct() {
46
+
47
+ // get settings from disk if cache exists
48
+ if ( Cache_Enabler_Disk::cache_exists() ) {
49
+ self::$settings = Cache_Enabler_Disk::get_settings();
50
+ // get settings from database otherwise
51
+ } elseif ( class_exists( 'Cache_Enabler' ) ) {
52
+ self::$settings = Cache_Enabler::get_settings();
53
+ }
54
+
55
+ // check engine requirements
56
+ if ( ! empty( self::$settings ) ) {
57
+ self::$started = true;
58
+ }
59
+ }
60
+
61
+
62
+ /**
63
+ * check if output buffering should start
64
+ *
65
+ * @since 1.5.0
66
+ * @change 1.5.0
67
+ *
68
+ * @return boolean true if output buffering should start, false otherwise
69
+ */
70
+
71
+ public static function should_buffer() {
72
+
73
+ if ( self::$started && self::is_index() ) {
74
+ return true;
75
+ }
76
+
77
+ return false;
78
+ }
79
+
80
+
81
+ /**
82
+ * start output buffering
83
+ *
84
+ * @since 1.5.0
85
+ * @change 1.5.0
86
+ */
87
+
88
+ public static function start_buffering() {
89
+
90
+ if ( self::should_buffer() ) {
91
+ ob_start( 'self::end_buffering' );
92
+ }
93
+ }
94
+
95
+
96
+ /**
97
+ * end output buffering and cache page if applicable
98
+ *
99
+ * @since 1.0.0
100
+ * @change 1.5.0
101
+ *
102
+ * @param string $page_contents content of a page from the output buffer
103
+ * @param integer $phase bitmask of PHP_OUTPUT_HANDLER_* constants
104
+ * @return string $page_contents content of a page from the output buffer
105
+ *
106
+ * @hook string cache_enabler_before_store
107
+ */
108
+
109
+ private static function end_buffering( $page_contents, $phase ) {
110
+
111
+ if ( $phase & PHP_OUTPUT_HANDLER_FINAL || $phase & PHP_OUTPUT_HANDLER_END ) {
112
+ if ( ! self::is_cacheable( $page_contents ) || self::bypass_cache() ) {
113
+ return $page_contents;
114
+ }
115
+
116
+ $page_contents = apply_filters( 'cache_enabler_before_store', $page_contents );
117
+
118
+ Cache_Enabler_Disk::cache_page( $page_contents );
119
+
120
+ return $page_contents;
121
+ }
122
+ }
123
+
124
+
125
+ /**
126
+ * check if installation directory index
127
+ *
128
+ * @since 1.0.0
129
+ * @change 1.5.0
130
+ *
131
+ * @return boolean true if installation directory index, false otherwise
132
+ */
133
+
134
+ private static function is_index() {
135
+
136
+ if ( strtolower( basename( $_SERVER['SCRIPT_NAME'] ) ) === 'index.php' ) {
137
+ return true;
138
+ }
139
+
140
+ return false;
141
+ }
142
+
143
+
144
+ /**
145
+ * check if page can be cached
146
+ *
147
+ * @since 1.5.0
148
+ * @change 1.5.0
149
+ *
150
+ * @param string $page_contents content of a page from the output buffer
151
+ * @return boolean true if page contents are cacheable, false otherwise
152
+ */
153
+
154
+ private static function is_cacheable( $page_contents ) {
155
+
156
+ $has_html_tag = ( stripos( $page_contents, '<html' ) !== false );
157
+ $has_html5_doctype = preg_match( '/^<!DOCTYPE.+html>/i', ltrim( $page_contents ) );
158
+ $has_xsl_stylesheet = ( stripos( $page_contents, '<xsl:stylesheet' ) !== false || stripos( $page_contents, '<?xml-stylesheet' ) !== false );
159
+
160
+ if ( $has_html_tag && $has_html5_doctype && ! $has_xsl_stylesheet ) {
161
+ return true;
162
+ }
163
+
164
+ return false;
165
+ }
166
+
167
+
168
+ /**
169
+ * check permalink structure
170
+ *
171
+ * @since 1.5.0
172
+ * @change 1.5.0
173
+ *
174
+ * @return boolean true if request URI does not match permalink structure or if plain, false otherwise
175
+ */
176
+
177
+ private static function is_wrong_permalink_structure() {
178
+
179
+ // check if trailing slash is set and missing (ignoring root index and file extensions)
180
+ if ( self::$settings['permalink_structure'] === 'has_trailing_slash' ) {
181
+ if ( preg_match( '/\/[^\.\/\?]+(\?.*)?$/', $_SERVER['REQUEST_URI'] ) ) {
182
+ return true;
183
+ }
184
+ }
185
+
186
+ // check if trailing slash is not set and appended (ignoring root index and file extensions)
187
+ if ( self::$settings['permalink_structure'] === 'no_trailing_slash' ) {
188
+ if ( preg_match( '/\/[^\.\/\?]+\/(\?.*)?$/', $_SERVER['REQUEST_URI'] ) ) {
189
+ return true;
190
+ }
191
+ }
192
+
193
+ // check if custom permalink structure is not set
194
+ if ( self::$settings['permalink_structure'] === 'plain' ) {
195
+ return true;
196
+ }
197
+
198
+ return false;
199
+ }
200
+
201
+
202
+ /**
203
+ * check if page is excluded from cache
204
+ *
205
+ * @since 1.5.0
206
+ * @change 1.5.0
207
+ *
208
+ * @return boolean true if page is excluded from the cache, false otherwise
209
+ */
210
+
211
+ private static function is_excluded() {
212
+
213
+ // if post ID excluded
214
+ if ( ! empty( self::$settings['excluded_post_ids'] ) && function_exists( 'is_singular' ) && is_singular() ) {
215
+ if ( in_array( get_queried_object_id(), (array) explode( ',', self::$settings['excluded_post_ids'] ) ) ) {
216
+ return true;
217
+ }
218
+ }
219
+
220
+ // if page path excluded
221
+ if ( ! empty( self::$settings['excluded_page_paths'] ) ) {
222
+ $page_path = parse_url( $_SERVER['REQUEST_URI'], PHP_URL_PATH );
223
+
224
+ if ( preg_match( self::$settings['excluded_page_paths'], $page_path ) ) {
225
+ return true;
226
+ }
227
+ }
228
+
229
+ // if query string excluded
230
+ if ( ! empty( self::$settings['excluded_query_strings'] ) ) {
231
+ $query_string = parse_url( $_SERVER['REQUEST_URI'], PHP_URL_QUERY );
232
+
233
+ if ( preg_match( self::$settings['excluded_query_strings'], $query_string ) ) {
234
+ return true;
235
+ }
236
+ }
237
+
238
+ // if cookie excluded
239
+ if ( ! empty( $_COOKIE ) ) {
240
+ // set regex matching cookies that should bypass the cache
241
+ if ( ! empty( self::$settings['excluded_cookies'] ) ) {
242
+ $cookies_regex = self::$settings['excluded_cookies'];
243
+ } else {
244
+ $cookies_regex = '/^(wp-postpass|wordpress_logged_in|comment_author)_/';
245
+ }
246
+ // bypass cache if an excluded cookie is found
247
+ foreach ( $_COOKIE as $key => $value) {
248
+ if ( preg_match( $cookies_regex, $key ) ) {
249
+ return true;
250
+ }
251
+ }
252
+ }
253
+
254
+ return false;
255
+ }
256
+
257
+
258
+ /**
259
+ * check if mobile template
260
+ *
261
+ * @since 1.0.0
262
+ * @change 1.0.0
263
+ *
264
+ * @return boolean true if mobile template, false otherwise
265
+ */
266
+
267
+ private static function is_mobile() {
268
+
269
+ return ( strpos( TEMPLATEPATH, 'wptouch' ) || strpos( TEMPLATEPATH, 'carrington' ) || strpos( TEMPLATEPATH, 'jetpack' ) || strpos( TEMPLATEPATH, 'handheld' ) );
270
+ }
271
+
272
+
273
+ /**
274
+ * check if cache should be bypassed
275
+ *
276
+ * @since 1.0.0
277
+ * @change 1.5.0
278
+ *
279
+ * @return boolean true if cache should be bypassed, false otherwise
280
+ *
281
+ * @hook boolean bypass_cache
282
+ */
283
+
284
+ private static function bypass_cache() {
285
+
286
+ // bypass cache hook
287
+ if ( apply_filters( 'bypass_cache', false ) ) {
288
+ return true;
289
+ }
290
+
291
+ // check request method
292
+ if ( ! isset( $_SERVER['REQUEST_METHOD'] ) || $_SERVER['REQUEST_METHOD'] !== 'GET' ) {
293
+ return true;
294
+ }
295
+
296
+ // check HTTP status code
297
+ if ( http_response_code() !== 200 ) {
298
+ return true;
299
+ }
300
+
301
+ // check WP_CACHE constant
302
+ if ( defined( 'WP_CACHE' ) && ! WP_CACHE ) {
303
+ return true;
304
+ }
305
+
306
+ // check DONOTCACHEPAGE constant
307
+ if ( defined( 'DONOTCACHEPAGE' ) && DONOTCACHEPAGE ) {
308
+ return true;
309
+ }
310
+
311
+ // check if Ajax request
312
+ if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
313
+ return true;
314
+ }
315
+
316
+ // check if REST API request
317
+ if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) {
318
+ return true;
319
+ }
320
+
321
+ // check if XMLRPC request
322
+ if ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST ) {
323
+ return true;
324
+ }
325
+
326
+ // check conditional tags
327
+ if ( self::is_wrong_permalink_structure() || self::is_excluded() ) {
328
+ return true;
329
+ }
330
+
331
+ // check conditional tags when output buffer has ended
332
+ if ( class_exists( 'WP' ) ) {
333
+ if ( is_admin() || is_search() || is_feed() || is_trackback() || is_robots() || is_preview() || post_password_required() || self::is_mobile() ) {
334
+ return true;
335
+ }
336
+ }
337
+
338
+ return false;
339
+ }
340
+
341
+
342
+ /**
343
+ * deliver cache
344
+ *
345
+ * @since 1.5.0
346
+ * @change 1.5.0
347
+ */
348
+
349
+ public function deliver_cache() {
350
+
351
+ if ( ! self::$started || Cache_Enabler_Disk::cache_expired() || self::bypass_cache() ) {
352
+ return;
353
+ }
354
+
355
+ readfile( Cache_Enabler_Disk::get_cache() );
356
+ exit;
357
+ }
358
+ }
readme.txt CHANGED
@@ -78,29 +78,55 @@ When combined with Optimus, the WordPress Cache Enabler allows you to easily del
78
  * [KeyCDN](https://www.keycdn.com "KeyCDN")
79
 
80
 
81
- = Credits =
82
- * Inspired by [Cachify](https://wordpress.org/plugins/cachify/).
83
-
84
-
85
  == Changelog ==
86
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87
  = 1.4.9 =
88
- * Fix WebP URL conversion
89
 
90
  = 1.4.8 =
91
- * Update WebP URL conversion (#116)
92
- * Update WP-CLI clear subcommand messages (#111)
93
- * Update WP-CLI clear subcommand for multisite networks (#111)
 
94
  * Fix cache clearing for installations in a subdirectory
95
  * Fix advanced cache settings recognition for installations in a subdirectory
96
  * Fix file permissions requirement notice
97
 
98
  = 1.4.7 =
99
- * Update getting wp-config.php if one level above installation (#106)
100
  * Add clear types for strict cache clearing (#110)
101
  * Fix advanced cache settings recognition for subdirectory multisite networks
102
- * Fix WP-CLI clear subcommand for post IDs (#110)
103
- * Fix scheme-based caching for NGINX/PHP-FPM (#109)
104
  * Fix trailing slash handling
105
 
106
  = 1.4.6 =
@@ -108,8 +134,8 @@ When combined with Optimus, the WordPress Cache Enabler allows you to easily del
108
  * Fix cache clearing for subdirectory multisite networks (#103)
109
 
110
  = 1.4.5 =
111
- * Update WP_CACHE constant handling (#102)
112
- * Add cache bypass method for WP_CACHE constant (#102)
113
  * Add translation descriptions (#102)
114
  * Fix cache handling for default redirects (#102)
115
 
@@ -121,7 +147,7 @@ When combined with Optimus, the WordPress Cache Enabler allows you to easily del
121
  * Fix advanced cache settings updating unnecessarily (#99)
122
 
123
  = 1.4.2 =
124
- * Update cache clearing for the Clear URL Cache admin bar button (#98)
125
  * Update scheme-based caching (#98)
126
  * Fix advanced cache path variants (#98)
127
 
@@ -130,14 +156,14 @@ When combined with Optimus, the WordPress Cache Enabler allows you to easily del
130
 
131
  = 1.4.0 =
132
  * Update default cache behavior for WooCommerce stock update (#88)
133
- * Update Cache Behavior setting for plugin actions (#91)
134
  * Update admin bar clear cache buttons (#96)
135
  * Update cache behavior for logged in users (#95)
136
  * Update default clear cache publishing action (#88)
137
  * Update advanced cache settings (#91 and #92)
138
  * Update trailing slash handling (#91)
139
  * Update settings page (#84 and #92)
140
- * Add Cache Behavior setting for WooCommerce stock update (#88)
141
  * Add fbclid as default URL query parameter to bypass cache (#84)
142
  * Add scheme-based caching (#94)
143
  * Fix advanced cache settings recognition for multisite networks (#92)
78
  * [KeyCDN](https://www.keycdn.com "KeyCDN")
79
 
80
 
 
 
 
 
81
  == Changelog ==
82
 
83
+ = 1.5.0 =
84
+ * Update settings file type to PHP instead of JSON (#147)
85
+ * Update settings file(s) storage location (#147)
86
+ * Update plugin activation, deactivation, and uninstall handling (#147)
87
+ * Update HTML minification to also include or exclude inline CSS (#147)
88
+ * Update cache size handling for multisite networks (#147)
89
+ * Update `WP_CACHE` constant handling (#140)
90
+ * Update cache cleared admin notice (#139)
91
+ * Update admin bar clear cache buttons (#139)
92
+ * Update output buffer timing to start earlier on the `init` hook instead of `template_redirect` (#137)
93
+ * Update default cache behavior to not bypass the cache for query strings (#129)
94
+ * Update cache clearing setting for when any post type is published to include all post actions (#142)
95
+ * Update cache clearing setting for post actions to clear the page and/or associated cache by default (#142)
96
+ * Update settings page layout (#129 and #142)
97
+ * Update WebP URL conversion for images with density descriptors (#125)
98
+ * Add cache engine to improve handling and performance (#147)
99
+ * Add cache bypass method for Ajax, REST API, and XMLRPC requests (#147)
100
+ * Add new cache clearing structure for post publish, update, and trash actions (#129)
101
+ * Add post type, taxonomies, author, and date archives to the new associated cache (#129)
102
+ * Add new cache exclusions setting for query strings (#129)
103
+ * Fix cache size file status edge case (#147)
104
+ * Fix `WP_CACHE` constant not being set edge case (#140)
105
+ * Fix settings file from using unvalidated data (#129)
106
+ * Fix clear URL admin bar button for installations in a subdirectory (#127)
107
+ * Fix WebP URL conversion for installations in a subdirectory (#125)
108
+ * Remove cache clearing publishing action from post sidebar in favor of the new cache clearing structure for post actions (#129)
109
+ * Remove cache clearing setting for WooCommerce stock updates in favor of the new cache clearing structure for post actions (#129)
110
+ * Remove cache inclusions setting for URL query parameters because of the updated default cache behavior for query strings (#129)
111
+
112
  = 1.4.9 =
113
+ * Fix WebP URL conversion changing all image paths to lowercase
114
 
115
  = 1.4.8 =
116
+ * Update WebP URL conversion for inline CSS (#116)
117
+ * Update WP-CLI `clear` subcommand messages (#111)
118
+ * Update WP-CLI `clear` subcommand for multisite networks (#111)
119
+ * Fix WebP URL conversion image matching edge cases (#116)
120
  * Fix cache clearing for installations in a subdirectory
121
  * Fix advanced cache settings recognition for installations in a subdirectory
122
  * Fix file permissions requirement notice
123
 
124
  = 1.4.7 =
125
+ * Update getting `wp-config.php` if one level above installation (#106)
126
  * Add clear types for strict cache clearing (#110)
127
  * Fix advanced cache settings recognition for subdirectory multisite networks
128
+ * Fix WP-CLI `clear` subcommand for post IDs (#110)
129
+ * Fix scheme-based caching for NGINX/PHP-FPM (#109 @centminmod)
130
  * Fix trailing slash handling
131
 
132
  = 1.4.6 =
134
  * Fix cache clearing for subdirectory multisite networks (#103)
135
 
136
  = 1.4.5 =
137
+ * Update `WP_CACHE` constant handling (#102)
138
+ * Add cache bypass method for `WP_CACHE` constant (#102)
139
  * Add translation descriptions (#102)
140
  * Fix cache handling for default redirects (#102)
141
 
147
  * Fix advanced cache settings updating unnecessarily (#99)
148
 
149
  = 1.4.2 =
150
+ * Update cache clearing for the clear URL admin bar button (#98)
151
  * Update scheme-based caching (#98)
152
  * Fix advanced cache path variants (#98)
153
 
156
 
157
  = 1.4.0 =
158
  * Update default cache behavior for WooCommerce stock update (#88)
159
+ * Update cache clearing setting for plugin actions (#91)
160
  * Update admin bar clear cache buttons (#96)
161
  * Update cache behavior for logged in users (#95)
162
  * Update default clear cache publishing action (#88)
163
  * Update advanced cache settings (#91 and #92)
164
  * Update trailing slash handling (#91)
165
  * Update settings page (#84 and #92)
166
+ * Add cache clearing setting for WooCommerce stock updates (#88)
167
  * Add fbclid as default URL query parameter to bypass cache (#84)
168
  * Add scheme-based caching (#94)
169
  * Fix advanced cache settings recognition for multisite networks (#92)