diff --git a/.gitchange b/.gitchange index db3c9ac..073f132 100644 --- a/.gitchange +++ b/.gitchange @@ -1,4 +1,4 @@ 1460135613 49dd4154d8f5d3b70691c8e3f52a063a49137fb7:57be33556df067.43016776 -853d9120b42cf4f2eabe2f35ea18a6ace7170ea6:585cfba51d7115.78255177 -d0e05c36184f81b133761b71ca6ee0136be67409:5862af7eeb5ac2.32178454 +853d9120b42cf4f2eabe2f35ea18a6ace7170ea6:588bb5682f4739.12912408 +d0e05c36184f81b133761b71ca6ee0136be67409:58926dbb57e4a4.36452623 diff --git a/CHANGELOG.md b/CHANGELOG.md index 39ba3b4..c575fe6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,16 @@ += v170201-RC = + +- **New Feature:** Comet Cache can now be configured to automatically clear the cache for date-based archive views whenever any single post is cleared due to changes in content, title, etc. See: **Dashboard → Comet Cache → Plugin Options → Automatic Cache Clearing → Auto-Clear "Date-Based Archives" Too?**. See also: [Issue #724](https://github.com/websharks/comet-cache/issues/724). +- **New Pro Feature:** Apache Optimizations now include a new option that allows site owners to enforce an exact host name for all requests. See: **Dashboard → Comet Cache Pro → Plugin Options → Apache Optimizations → Enforce an Exact Host Name?**. See also: [Issue #101](https://github.com/websharks/comet-cache/issues/101). +- **Bug Fix:** Apache detection sometimes inaccurate. So instead of using default WP core globals for server detection, Comet Cache now uses it's own set of Apache/Nginx/IIS detection functions. And, this release enhances our Apache and Nginx detection routines; making them smart enough to catch additional edge cases; i.e., to further reduce the likelihood of there being a false-positive. See [Issue #748](https://github.com/websharks/comet-cache/issues/748). +- **Bug Fix:** Some XML-RPC and REST API requests were being cached inadvertently. See [Issue #855](https://github.com/websharks/comet-cache/issues/855). +- **Bug Fix:** Broken textarea field due to `white-space:nowrap` in Firefox. See [Issue #866](https://github.com/websharks/comet-cache/issues/866). +- **Bug Fix:** This release resolves empty directories being left in the cache folder, in some scenarios. See [Thread #866](https://forums.wpsharks.com/t/cache-folders-not-removed-during-clean-up-process/866). +- **Bug Fix** (Pro): Some REST requests were being redirected incorrectly whenever Apache Optimizations were enabled. See [Issue #855](https://github.com/websharks/comet-cache/issues/855). +- **Compatibility Bug Fix:** Some Jetpack API calls were being cached inadvertently. See [Issue #855](https://github.com/websharks/comet-cache/issues/855). +- **Enhancement:** Notes in HTML source now indicate fully functional on first load for improved clarity. See [Issue #860](https://github.com/websharks/comet-cache/issues/860). +- **Code Cleanup:** Enhancing security by removing `basename(__FILE__)` from direct access notices. + = v161227 = _**Note:** This is a Comet Cache Pro maintenance release._ diff --git a/comet-cache.php b/comet-cache.php index 6ada9fc..004b578 100644 --- a/comet-cache.php +++ b/comet-cache.php @@ -1,6 +1,6 @@ plugin->cap)) { return; // Nothing to do. } elseif (empty($_REQUEST['_wpnonce']) || !wp_verify_nonce($_REQUEST['_wpnonce'])) { @@ -222,10 +225,10 @@ protected function saveOptions($args) if (!($add_wp_cache_to_wp_config = $this->plugin->addWpCacheToWpConfig())) { $query_args[GLOBAL_NS.'_wp_config_wp_cache_add_failure'] = '1'; } - if ($is_apache && !($add_wp_htaccess = $this->plugin->addWpHtaccess())) { + if ($this->plugin->isApache() && !($add_wp_htaccess = $this->plugin->addWpHtaccess())) { $query_args[GLOBAL_NS.'_wp_htaccess_add_failure'] = '1'; } - if ($is_nginx && $this->plugin->applyWpFilters(GLOBAL_NS.'_wp_htaccess_nginx_notice', true) + if ($this->plugin->isNginx() && $this->plugin->applyWpFilters(GLOBAL_NS.'_wp_htaccess_nginx_notice', true) && (!isset($_SERVER['WP_NGINX_CONFIG']) || $_SERVER['WP_NGINX_CONFIG'] !== 'done')) { $query_args[GLOBAL_NS.'_wp_htaccess_nginx_notice'] = '1'; } @@ -246,7 +249,7 @@ protected function saveOptions($args) if (!($remove_wp_cache_from_wp_config = $this->plugin->removeWpCacheFromWpConfig())) { $query_args[GLOBAL_NS.'_wp_config_wp_cache_remove_failure'] = '1'; } - if ($is_apache && !($remove_wp_htaccess = $this->plugin->removeWpHtaccess())) { + if ($this->plugin->isApache() && !($remove_wp_htaccess = $this->plugin->removeWpHtaccess())) { $query_args[GLOBAL_NS.'_wp_htaccess_remove_failure'] = '1'; } if (!($remove_advanced_cache = $this->plugin->removeAdvancedCache())) { @@ -269,11 +272,10 @@ protected function saveOptions($args) * @since 150422 Rewrite. * * @param mixed Input action argument(s). + * @param mixed $args */ protected function restoreDefaultOptions($args) { - global $is_apache, $is_nginx; - if (!current_user_can($this->plugin->cap)) { return; // Nothing to do. } elseif (is_multisite() && !current_user_can($this->plugin->network_cap)) { @@ -292,10 +294,10 @@ protected function restoreDefaultOptions($args) if (!($add_wp_cache_to_wp_config = $this->plugin->addWpCacheToWpConfig())) { $query_args[GLOBAL_NS.'_wp_config_wp_cache_add_failure'] = '1'; } - if ($is_apache && !($add_wp_htaccess = $this->plugin->addWpHtaccess())) { + if ($this->plugin->isApache() && !($add_wp_htaccess = $this->plugin->addWpHtaccess())) { $query_args[GLOBAL_NS.'_wp_htaccess_add_failure'] = '1'; } - if ($is_nginx && $this->plugin->applyWpFilters(GLOBAL_NS.'_wp_htaccess_nginx_notice', true) + if ($this->plugin->isNginx() && $this->plugin->applyWpFilters(GLOBAL_NS.'_wp_htaccess_nginx_notice', true) && (!isset($_SERVER['WP_NGINX_CONFIG']) || $_SERVER['WP_NGINX_CONFIG'] !== 'done')) { $query_args[GLOBAL_NS.'_wp_htaccess_nginx_notice'] = '1'; } @@ -316,7 +318,7 @@ protected function restoreDefaultOptions($args) if (!($remove_wp_cache_from_wp_config = $this->plugin->removeWpCacheFromWpConfig())) { $query_args[GLOBAL_NS.'_wp_config_wp_cache_remove_failure'] = '1'; } - if ($is_apache && !($remove_wp_htaccess = $this->plugin->removeWpHtaccess())) { + if ($this->plugin->isApache() && !($remove_wp_htaccess = $this->plugin->removeWpHtaccess())) { $query_args[GLOBAL_NS.'_wp_htaccess_remove_failure'] = '1'; } if (!($remove_advanced_cache = $this->plugin->removeAdvancedCache())) { @@ -341,6 +343,7 @@ protected function restoreDefaultOptions($args) * @since 150422 Rewrite. * * @param mixed Input action argument(s). + * @param mixed $args */ protected function dismissNotice($args) { diff --git a/src/includes/classes/AdvancedCache.php b/src/includes/classes/AdvancedCache.php index c9f8d4f..4372649 100644 --- a/src/includes/classes/AdvancedCache.php +++ b/src/includes/classes/AdvancedCache.php @@ -52,11 +52,28 @@ public function __construct() return; // Missing; wait for update. } elseif (COMET_CACHE_AC_FILE_VERSION !== VERSION) { return; // Version mismatch; wait for update. + // } elseif (!defined('WP_CACHE') || !WP_CACHE || !COMET_CACHE_ENABLE) { return; // Not enabled in `wp-config.php` or otherwise. } elseif (defined('WP_INSTALLING') || defined('RELOCATE')) { - return; // N/A; installing|relocating. + return; // Not applicable; installing and/or relocating. + // + } elseif (defined('XMLRPC_REQUEST') && XMLRPC_REQUEST) { + return; // Not applicable; bypass API requests. + } elseif (defined('REST_REQUEST') && REST_REQUEST) { + return; // Not applicable; bypass API requests. } + // Note: `REST_REQUEST` is only here as a way of future-proofing the software. + // Ideally, we could catch all API requests here to avoid any overhead in processing. + // I suspect this will be the case in a future release of WordPress. + + // For now, `REST_REQUEST` is not defined by WP until later in the `parse_request` phase. + // Therefore, this check by itself is not enough to avoid all REST requests at this time. + // See: `traits/Ac/ObUtils.php` for additional checks for `REST_REQUEST` API calls. + + // `XMLRPC_REQUEST` on the other hand, is set very early via `xmlrpc.php`. So no issue. + // ------------------------------------------------------------------------------------------------------------- + $this->is_running = true; $this->timer = microtime(true); diff --git a/src/includes/classes/MenuPageOptions.php b/src/includes/classes/MenuPageOptions.php index b00000c..8208307 100644 --- a/src/includes/classes/MenuPageOptions.php +++ b/src/includes/classes/MenuPageOptions.php @@ -17,9 +17,6 @@ public function __construct() { parent::__construct(); // Parent constructor. - global $is_nginx; // WP global for web server checks below. - global $is_apache; // WP global for web server checks below. - echo '
'."\n"; @@ -425,6 +422,15 @@ public function __construct() echo ' '."\n"; echo '

'."\n"; + echo '

'.__('Auto-Clear "Date-Based Archives" Too?', 'comet-cache').'

'."\n"; + echo '

'.sprintf(__('Date-Based Archives allow visitors to browse Posts by the year, month, or day they were originally published. If a single Post (of any type) is changed in some way; and %1$s clears/resets the cache for that Post, would you like %1$s to also clear any existing cache files for Dated-Based Archives that match the publication time?', 'comet-cache'), esc_html(NAME)).'

'."\n"; + echo '

'."\n"; + echo '

'.__('Auto-Clear "Custom Term Archives" Too?', 'comet-cache').'

'."\n"; echo '

'.sprintf(__('Most sites do not use any custom Terms so it should be safe to leave this disabled. However, if your site uses custom Terms and they have their own Term archive views, you may want to clear those when the associated post is cleared. Therefore, if a single Post/Page is changed in some way; and %1$s clears/resets the cache for a single Post/Page, would you like %1$s to also clear any existing cache files for the associated Tag archive views?', 'comet-cache'), esc_html(NAME)).'

'."\n"; echo '

/'."\n"; + echo '
'.esc_html(WP_CONTENT_DIR).'//
'."\n"; echo ' '."\n"; echo ''."\n"; @@ -866,7 +872,7 @@ public function __construct() echo '

'."\n"; echo '

'.__('XML Sitemap URL (or an XML Sitemap Index)', 'comet-cache').'

'."\n"; - echo '
'.esc_html(home_url('/')).'
'."\n"; + echo '
'.esc_html(home_url('/')).'
'."\n"; if (is_multisite()) { echo '

; '.esc_html(GLOBAL_NS.' '.VERSION).''."\n"; + echo '
; '.esc_html(GLOBAL_NS.' '.VERSION).'
'."\n"; echo '

'.__('This is how the Auto-Cache Engine identifies itself when connecting to URLs. See User Agent in the Wikipedia.', 'comet-cache').'

'."\n"; echo '
'."\n"; echo ' '."\n"; @@ -991,9 +997,9 @@ public function __construct() echo '
'."\n"; echo ' '."\n"; echo '

'.__('Enable Static CDN Filters (e.g., MaxCDN/CloudFront)?', 'comet-cache').'

'."\n"; - echo '

'.sprintf(__('This feature allows you to serve some and/or ALL static files on your site from a CDN of your choosing. This is made possible through content/URL filters exposed by WordPress and implemented by %1$s. All it requires is that you setup a CDN host name sourced by your WordPress installation domain. You enter that CDN host name below and %1$s will do the rest! Super easy, and it doesn\'t require any DNS changes either. :-) Please click here for a general set of instructions.', 'comet-cache'), esc_html(NAME)).'

'."\n"; + echo '

'.sprintf(__('This feature allows you to serve some and/or ALL static files on your site from a CDN of your choosing. This is made possible through content/URL filters exposed by WordPress and implemented by %1$s. All it requires is that you setup a CDN hostname sourced by your WordPress installation domain. You enter that CDN hostname below and %1$s will do the rest! Super easy, and it doesn\'t require any DNS changes either. :-) Please click here for a general set of instructions.', 'comet-cache'), esc_html(NAME)).'

'."\n"; echo '

'.__('What\'s a CDN? It\'s a Content Delivery Network (i.e., a network of optimized servers) designed to cache static resources served from your site (e.g., JS/CSS/images and other static files) onto it\'s own servers, which are located strategically in various geographic areas around the world. Integrating a CDN for static files can dramatically improve the speed and performance of your site, lower the burden on your own server, and reduce latency associated with visitors attempting to access your site from geographic areas of the world that might be very far away from the primary location of your own web servers.', 'comet-cache').'

'."\n"; - if ($is_nginx && $this->plugin->applyWpFilters(GLOBAL_NS.'_wp_htaccess_nginx_notice', true) && (!isset($_SERVER['WP_NGINX_CONFIG']) || $_SERVER['WP_NGINX_CONFIG'] !== 'done')) { + if ($this->plugin->isNginx() && $this->plugin->applyWpFilters(GLOBAL_NS.'_wp_htaccess_nginx_notice', true) && (!isset($_SERVER['WP_NGINX_CONFIG']) || $_SERVER['WP_NGINX_CONFIG'] !== 'done')) { echo '
'."\n"; echo ' '.__('It appears that your server is running NGINX and does not support .htaccess rules. Please update your server configuration manually. Note that updating your NGINX server configuration before enabling Static CDN Filters is recommended to prevent any CORS errors with your CDN. If you\'ve already updated your NGINX configuration, you can safely ignore this message.', 'comet-cache')."\n"; echo '
'."\n"; @@ -1002,29 +1008,29 @@ public function __construct() echo ' '."\n"; echo ' '."\n"; echo '

'."\n"; - if ($is_apache && $this->plugin->options['cdn_enable'] && !$this->plugin->options['htaccess_access_control_allow_origin']) { + if ($this->plugin->isApache() && $this->plugin->options['cdn_enable'] && !$this->plugin->options['htaccess_access_control_allow_origin']) { echo '

'.__('Warning: Static CDN Filters are enabled above but the Comet Cache → Plugin Options → Apache Optimizations → Send Access-Control-Allow-Origin Header option has been disabled. We recommend sending the Access-Control-Allow-Origin header to avoid CORS errors when a CDN is configured.', 'comet-cache').'

'."\n"; } echo '
'."\n"; echo '
'."\n"; - echo '

'.__('CDN Host Name (Required)', 'comet-cache').'

'."\n"; + echo '

'.__('CDN Hostname (Required)', 'comet-cache').'

'."\n"; echo '

'.// This note includes three graphics. One for MaxCDN; another for CloudFront, and another for KeyCDN. ' '. ' '. ' '. - ' '.__('This field is really all that\'s necessary to get Static CDN Filters working! However, it does requires a little bit of work on your part. You need to setup and configure a CDN before you can fill in this field. Once you configure a CDN, you\'ll receive a host name (provided by your CDN), which you\'ll enter here; e.g., js9dgjsl4llqpp.cloudfront.net. We recommend MaxCDN, Amazon CloudFront, KeyCDN, and/or CDN77 but this should work with many of the most popular CDNs. Please read this article for a general set of instructions. We also have a MaxCDN tutorial, CloudFront tutorial, KeyCDN tutorial, and a CDN77 tutorial to walk you through the process.', 'comet-cache').'

'."\n"; + ' '.__('This field is really all that\'s necessary to get Static CDN Filters working! However, it does requires a little bit of work on your part. You need to setup and configure a CDN before you can fill in this field. Once you configure a CDN, you\'ll receive a hostname (provided by your CDN), which you\'ll enter here; e.g., js9dgjsl4llqpp.cloudfront.net. We recommend MaxCDN, Amazon CloudFront, KeyCDN, and/or CDN77 but this should work with many of the most popular CDNs. Please read this article for a general set of instructions. We also have a MaxCDN tutorial, CloudFront tutorial, KeyCDN tutorial, and a CDN77 tutorial to walk you through the process.', 'comet-cache').'

'."\n"; echo '

plugin->options['cdn_hosts'] ? ' disabled="disabled"' : '').' />

'."\n"; echo '
'."\n"; - echo '

'.__('Multiple CDN Host Names for Domain Sharding and Multisite Networks (Optional)', 'comet-cache').'

'."\n"; - echo '

'.sprintf(__('%1$s also supports multiple CDN Host Names for any given domain. Using multiple CDN Host Names (instead of just one, as seen above) is referred to as Domain Sharding (click here to learn more). If you configure multiple CDN Host Names (i.e., if you implement Domain Sharding), %1$s will use the first one that you list for static resources loaded in the HTML <head> section, the last one for static resources loaded in the footer, and it will choose one at random for all other static resource locations. Configuring multiple CDN Host Names can improve speed! This is a way for advanced site owners to work around concurrency limits in popular browsers; i.e., making it possible for browsers to download many more resources simultaneously, resulting in a faster overall completion time. In short, this tells the browser that your website will not be overloaded by concurrent requests, because static resources are in fact being served by a content-delivery network (i.e., multiple CDN host names). If you use this functionality for Domain Sharding, we suggest that you setup one CDN Distribution (aka: Pull Zone), and then create multiple CNAME records pointing to that distribution. You can enter each of your CNAMES in the field below, as instructed.', 'comet-cache'), esc_html(NAME)).'

'."\n"; - echo '

'.sprintf(__('On WordPress Multisite Network installations, this field also allows you to configure different CDN Host Names for each domain (or sub-domain) that you run from a single installation of WordPress. For more information about configuring Static CDN Filters on a WordPress Multisite Network, see this tutorial: Static CDN Filters for WordPress Multisite Networks.', 'comet-cache'), esc_html(NAME)).'

'."\n"; - echo '

'."\n"; - echo '

'.sprintf(__('↑ Syntax: This is a line-delimited list of domain mappings. Each line should start with your WordPress domain name (e.g., %1$s), followed by an = sign, followed by a comma-delimited list of CDN Host Names associated with the domain in that line. If you\'re running a Multisite Network installation of WordPress, you might have multiple configuration lines. Otherwise, you should only need one line to configure multiple CDN Host Names for a standard WordPress installation.', 'comet-cache'), esc_html($this->plugin->hostToken(false, true))).'

'."\n"; + echo '

'.__('Multiple CDN Hostnames for Domain Sharding and Multisite Networks (Optional)', 'comet-cache').'

'."\n"; + echo '

'.sprintf(__('%1$s also supports multiple CDN Hostnames for any given domain. Using multiple CDN Hostnames (instead of just one, as seen above) is referred to as Domain Sharding (click here to learn more). If you configure multiple CDN Hostnames (i.e., if you implement Domain Sharding), %1$s will use the first one that you list for static resources loaded in the HTML <head> section, the last one for static resources loaded in the footer, and it will choose one at random for all other static resource locations. Configuring multiple CDN Hostnames can improve speed! This is a way for advanced site owners to work around concurrency limits in popular browsers; i.e., making it possible for browsers to download many more resources simultaneously, resulting in a faster overall completion time. In short, this tells the browser that your website will not be overloaded by concurrent requests, because static resources are in fact being served by a content-delivery network (i.e., multiple CDN hostnames). If you use this functionality for Domain Sharding, we suggest that you setup one CDN Distribution (aka: Pull Zone), and then create multiple CNAME records pointing to that distribution. You can enter each of your CNAMES in the field below, as instructed.', 'comet-cache'), esc_html(NAME)).'

'."\n"; + echo '

'.sprintf(__('On WordPress Multisite Network installations, this field also allows you to configure different CDN Hostnames for each domain (or sub-domain) that you run from a single installation of WordPress. For more information about configuring Static CDN Filters on a WordPress Multisite Network, see this tutorial: Static CDN Filters for WordPress Multisite Networks.', 'comet-cache'), esc_html(NAME)).'

'."\n"; + echo '

'."\n"; + echo '

'.sprintf(__('↑ Syntax: This is a line-delimited list of domain mappings. Each line should start with your WordPress domain name (e.g., %1$s), followed by an = sign, followed by a comma-delimited list of CDN Hostnames associated with the domain in that line. If you\'re running a Multisite Network installation of WordPress, you might have multiple configuration lines. Otherwise, you should only need one line to configure multiple CDN Hostnames for a standard WordPress installation.', 'comet-cache'), esc_html($this->plugin->hostToken(false, true))).'

'."\n"; echo '
'."\n"; @@ -1060,8 +1066,8 @@ public function __construct() echo '

'.__('Whitelisted URI Inclusion Patterns (Optional; One Per Line)', 'comet-cache').'

'."\n"; echo '

'."\n"; echo '

'.__('Note: please remember that your entries here should be formatted as a line-delimited list; e.g., one inclusion pattern per line.', 'comet-cache').'

'."\n"; - echo '

'.__('If provided, only local URIs matching one of the patterns you list here will be served from your CDN Host Name. URI patterns are caSe-insensitive. A wildcard * will match zero or more characters in any of your patterns. A caret ^ symbol will match zero or more characters that are NOT the / character. For instance, */wp-content/* here would indicate that you only want to filter URLs that lead to files located inside the wp-content directory. Adding an additional line with */wp-includes/* would filter URLs in the wp-includes directory also. If you leave this empty, ALL files matching a static file extension will be served from your CDN; i.e., the default behavior.', 'comet-cache').'

'."\n"; - echo '

'.__('Please note that URI patterns are tested against a file\'s path (i.e., a file\'s URI, and NOT its full URL). A URI always starts with a leading /. To clarify, a URI is the portion of the URL which comes after the host name. For instance, given the following URL: http://example.com/path/to/style.css?ver=3, the URI you are matching against would be: /path/to/style.css?ver=3. To whitelist this URI, you could use a line that contains something like this: /path/to/*.css*', 'comet-cache').'

'."\n"; + echo '

'.__('If provided, only local URIs matching one of the patterns you list here will be served from your CDN Hostname. URI patterns are caSe-insensitive. A wildcard * will match zero or more characters in any of your patterns. A caret ^ symbol will match zero or more characters that are NOT the / character. For instance, */wp-content/* here would indicate that you only want to filter URLs that lead to files located inside the wp-content directory. Adding an additional line with */wp-includes/* would filter URLs in the wp-includes directory also. If you leave this empty, ALL files matching a static file extension will be served from your CDN; i.e., the default behavior.', 'comet-cache').'

'."\n"; + echo '

'.__('Please note that URI patterns are tested against a file\'s path (i.e., a file\'s URI, and NOT its full URL). A URI always starts with a leading /. To clarify, a URI is the portion of the URL which comes after the hostname. For instance, given the following URL: http://example.com/path/to/style.css?ver=3, the URI you are matching against would be: /path/to/style.css?ver=3. To whitelist this URI, you could use a line that contains something like this: /path/to/*.css*', 'comet-cache').'

'."\n"; echo '

'.__('Blacklisted URI Exclusion Patterns (Optional; One Per Line)', 'comet-cache').'

'."\n"; echo '

'."\n"; @@ -1083,7 +1089,7 @@ public function __construct() } /* ----------------------------------------------------------------------------------------- */ - if ($is_apache || $this->plugin->isProPreview()) { + if (!$this->plugin->isApache() || $this->plugin->isProPreview()) { echo ''."\n"; - if ((!IS_PRO && $is_apache) && !$this->plugin->isProPreview()) { + if ((!IS_PRO && $this->plugin->isApache()) && !$this->plugin->isProPreview()) { echo '
'."\n"; echo '

'.sprintf(__('Enable the Pro Preview to see Leverage Browser Caching, Enforce Canonical URLs, and more!', 'comet-cache'), esc_attr(add_query_arg(urlencode_deep(['page' => GLOBAL_NS, GLOBAL_NS.'_pro_preview' => '1']), self_admin_url('/admin.php')))).'

'."\n"; } @@ -1131,6 +1137,20 @@ public function __construct() echo '
'.esc_html($this->plugin->fillReplacementCodes(file_get_contents(dirname(__DIR__).'/templates/htaccess/browser-caching-enable.txt'))).'
'."\n"; echo '
'."\n"; } + if (IS_PRO || $this->plugin->isProPreview()) { + echo '
'."\n"; + echo '

'.__('Enforce an Exact Hostname?', 'comet-cache').'

'."\n"; + echo '

'.sprintf(__('By enforcing an exact hostname you avoid duplicate cache files, which saves disk space and improves cache performance. For example, if a bot or crawler accesses your site using your server\'s IP address instead of using your domain name (e.g., http://123.456.789/path), this results in duplicate cache files, because the host was an IP address. The \'host\' being an important factor in any cache storage system. The same would be true if a visitor attempted to access your site using a made-up sub-domain; e.g., http://foo.bar.%1$s/path. This sort of thing can be avoided by explicitly enforcing an exact hostname in the request. One that matches exactly what you\'ve configured in WordPress Settings → General.', 'comet-cache'), esc_html(parse_url(network_home_url(), PHP_URL_HOST))).'

'."\n"; + echo '

'."\n"; + echo '

'.__('Or, you can update your configuration manually: [ .htaccess configuration ]', 'comet-cache').'

'."\n"; + echo ' '."\n"; + } if ((IS_PRO && !empty($GLOBALS['wp_rewrite']->permalink_structure)) || $this->plugin->isProPreview()) { echo '
'."\n"; echo '

'.__('Enforce Canonical URLs?', 'comet-cache').'

'."\n"; @@ -1233,7 +1253,7 @@ public function __construct() echo '

'.__('For more documentation, please see Dynamic Version Salts.', 'comet-cache').'

'."\n"; echo '
'."\n"; echo '

'.sprintf(__('Create a Dynamic Version Salt For %1$s?       150%% OPTIONAL', 'comet-cache'), esc_html(NAME)).'

'."\n"; - echo '
PROTOCOL.HOST.URI.v.
'."\n"; + echo '
PROTOCOL.HOST.URI.v.
'."\n"; echo '

'.__('Super Globals work here; $GLOBALS[\'table_prefix\'] is a popular one.
Or, perhaps a PHP Constant defined in /wp-config.php; such as WPLANG or DB_HOST.', 'comet-cache').'

'."\n"; echo '

'.__('Important: your Version Salt is scanned for PHP syntax errors via phpCodeChecker.com. If errors are found, you\'ll receive a notice in the Dashboard.', 'comet-cache').'

'."\n"; echo '

'.__('If you\'ve enabled a separate cache for each user (optional) that\'s perfectly OK. A Version Salt works with user caching too.', 'comet-cache').'

'."\n"; diff --git a/src/includes/classes/Plugin.php b/src/includes/classes/Plugin.php index 9933c6d..acfaf55 100644 --- a/src/includes/classes/Plugin.php +++ b/src/includes/classes/Plugin.php @@ -30,6 +30,7 @@ class Plugin extends AbsBaseAp use Traits\Plugin\UserUtils; use Traits\Plugin\WcpAuthorUtils; use Traits\Plugin\WcpCommentUtils; + use Traits\Plugin\WcpDateArchiveUtils; use Traits\Plugin\WcpFeedUtils; use Traits\Plugin\WcpHomeBlogUtils; use Traits\Plugin\WcpJetpackUtils; @@ -226,6 +227,7 @@ public function setup() 'auto_cache_user_agent', 'htaccess_browser_caching_enable', + 'htaccess_enforce_exact_host_name', 'htaccess_enforce_canonical_urls', 'htaccess_access_control_allow_origin', @@ -321,6 +323,12 @@ public function setup() 'cache_clear_term_post_tag_enable' => '1', // `0|1`. 'cache_clear_term_other_enable' => '1', // `0|1`. + 'cache_clear_date_archives_enable' => '1', // `0|1|2|3`. + // 0 = No, don't clear any associated Date archive views. + // 1 = Yes, if any single Post is cleared/reset, also clear the associated Date archive views. + // 2 = Yes, but only clear the associated Day and Month Date archive views. + // 3 = Yes, but only clear the associated Day Date archive view. + /* Misc. cache behaviors. */ 'allow_client_side_cache' => '0', // `0|1`. @@ -387,6 +395,7 @@ public function setup() 'htaccess_browser_caching_enable' => '0', // `0|1`; enable browser caching? 'htaccess_gzip_enable' => '0', // `0|1`; enable GZIP compression? + 'htaccess_enforce_exact_host_name' => '0', // `0|1`; enforce exact hostname? 'htaccess_enforce_canonical_urls' => '0', // `0|1`; enforce canonical URLs? 'htaccess_access_control_allow_origin' => '0', // `0|1`; send Access-Control-Allow-Origin header? diff --git a/src/includes/classes/VsUpgrades.php b/src/includes/classes/VsUpgrades.php index 9deeeaa..276a866 100644 --- a/src/includes/classes/VsUpgrades.php +++ b/src/includes/classes/VsUpgrades.php @@ -9,7 +9,7 @@ class VsUpgrades extends AbsBase { /** - * @var string Version they are upgrading from. + * @type string Version they are upgrading from. * * @since 150422 Rewrite. */ @@ -132,9 +132,7 @@ protected function fromLte151107() protected function fromLte151114() { if (version_compare($this->prev_version, '151114', '<=')) { - global $is_apache; - - if (!$is_apache) { + if (!$this->plugin->isApache()) { return; // Not running the Apache web server. } if (!($htaccess_file = $this->plugin->findHtaccessFile())) { @@ -187,8 +185,8 @@ protected function fromZenCache() } $this->plugin->deleteBaseDir(); // Let's be extra sure that the old base directory is gone. - global $is_apache; // Remove htaccess rules added by ZenCache so that they can be re-added by Comet Cache - if ($is_apache && $this->plugin->findHtaccessMarker('WmVuQ2FjaGU') && ($htaccess = $this->plugin->readHtaccessFile())) { + // Remove htaccess rules added by ZenCache so that they can be re-added by Comet Cache + if ($this->plugin->isApache() && $this->plugin->findHtaccessMarker('WmVuQ2FjaGU') && ($htaccess = $this->plugin->readHtaccessFile())) { $regex = '/#\s*BEGIN\s+ZenCache\s+WmVuQ2FjaGU.*?#\s*END\s+ZenCache\s+WmVuQ2FjaGU\s*/uis'; $htaccess['file_contents'] = preg_replace($regex, '', $htaccess['file_contents']); @@ -238,8 +236,6 @@ protected function fromLte160227() protected function fromLte160521() { if (version_compare($this->prev_version, '160521', '<=')) { - global $is_apache; // WP global for web server checks below. - $this->plugin->dismissMainNotice('allow_url_fopen_disabled'); $this->plugin->removeAdvancedCache(); @@ -255,8 +251,7 @@ protected function fromLte160521() $this->plugin->activate(); // Reactivate plugin w/ new options. } } - - if ($is_apache) { + if ($this->plugin->isApache()) { $this->plugin->enqueueMainNotice(sprintf(__('New %1$s Feature! This release of %1$s includes a whole new panel for Apache Performance Tuning. Visit the settings and see the new options in Comet Cache → Plugin Options → Apache Optimizations.', 'comet-cache'), esc_html(NAME), esc_attr(add_query_arg(urlencode_deep(['page' => GLOBAL_NS]), self_admin_url('/admin.php'))))); } } diff --git a/src/includes/interfaces/Shared/NcDebugConsts.php b/src/includes/interfaces/Shared/NcDebugConsts.php index 288f3b5..6b6d014 100644 --- a/src/includes/interfaces/Shared/NcDebugConsts.php +++ b/src/includes/interfaces/Shared/NcDebugConsts.php @@ -66,6 +66,24 @@ interface NcDebugConsts */ const NC_DEBUG_DONOTCACHEPAGE_SERVER_VAR = 'nc_debug_donotcachepage_server_var'; + /** + * No-cache because it's an `XMLRPC_REQUEST`. + * + * @since 17xxxx Enhancing compatibility with API requests. + * + * @type string A unique string identifier in the set of `NC_DEBUG_` constants. + */ + const NC_DEBUG_XMLRPC_REQUEST_CONSTANT = 'nc_debug_xmlrpc_request_constant'; + + /** + * No-cache because it's a `REST_REQUEST`. + * + * @since 17xxxx Enhancing compatibility with API requests. + * + * @type string A unique string identifier in the set of `NC_DEBUG_` constants. + */ + const NC_DEBUG_REST_REQUEST_CONSTANT = 'nc_debug_rest_request_constant'; + /** * No-cache because the current request includes the `?[SHORT_NAME]AC=0` parameter. * @@ -193,12 +211,12 @@ interface NcDebugConsts const NC_DEBUG_PREVIEW = 'nc_debug_preview'; /** - * No-cache because the current request is excluded by its host name. - * - * @since 160706 Host exclusions. - * - * @type string A unique string identifier in the set of `NC_DEBUG_` constants. - */ + * No-cache because the current request is excluded by its hostname. + * + * @since 160706 Host exclusions. + * + * @type string A unique string identifier in the set of `NC_DEBUG_` constants. + */ const NC_DEBUG_EXCLUDED_HOSTS = 'nc_debug_excluded_hosts'; /** diff --git a/src/includes/plugin.php b/src/includes/plugin.php index 7828864..b21acfe 100644 --- a/src/includes/plugin.php +++ b/src/includes/plugin.php @@ -9,7 +9,7 @@ use WebSharks\CometCache\Classes; if (!defined('WPINC')) { - exit('Do NOT access this file directly: '.basename(__FILE__)); + exit('Do NOT access this file directly.'); } require_once __DIR__.'/stub.php'; diff --git a/src/includes/stub.php b/src/includes/stub.php index 596fc43..eda6400 100644 --- a/src/includes/stub.php +++ b/src/includes/stub.php @@ -8,12 +8,12 @@ namespace WebSharks\CometCache; if (!defined('WPINC')) { - exit('Do NOT access this file directly: '.basename(__FILE__)); + exit('Do NOT access this file directly.'); } require_once dirname(__DIR__).'/vendor/autoload.php'; require_once __DIR__.'/functions/i18n-utils.php'; -${__FILE__}['version'] = '161227'; //version// +${__FILE__}['version'] = '170201-RC'; //version// ${__FILE__}['plugin'] = dirname(dirname(__DIR__)); ${__FILE__}['plugin'] .= '/'.basename(${__FILE__}['plugin']).'.php'; ${__FILE__}['ns_path'] = str_replace('\\', '/', __NAMESPACE__); // To dir/path. diff --git a/src/includes/templates/ac-plugin.txt b/src/includes/templates/ac-plugin.txt index 9eaa1b4..082304a 100644 --- a/src/includes/templates/ac-plugin.txt +++ b/src/includes/templates/ac-plugin.txt @@ -6,7 +6,7 @@ * `/wp-content/ac-plugins/my-ac-plugin.php` */ if (!defined('WPINC')) { - exit('Do NOT access this file directly: '.basename(__FILE__)); + exit('Do NOT access this file directly.'); } function my_ac_plugin() // Example plugin. { diff --git a/src/includes/templates/advanced-cache.x-php b/src/includes/templates/advanced-cache.x-php index de868a3..e1d6b02 100644 --- a/src/includes/templates/advanced-cache.x-php +++ b/src/includes/templates/advanced-cache.x-php @@ -10,7 +10,7 @@ namespace WebSharks\CometCache; use WebSharks\CometCache\Classes; if (!defined('WPINC')) { - exit('Do NOT access this file directly: '.basename(__FILE__)); + exit('Do NOT access this file directly.'); } if (!defined('COMET_CACHE_PLUGIN_FILE')) { /* diff --git a/src/includes/templates/htaccess/canonical-urls-no-ts-enable.txt b/src/includes/templates/htaccess/canonical-urls-no-ts-enable.txt index fd73028..6e946f9 100644 --- a/src/includes/templates/htaccess/canonical-urls-no-ts-enable.txt +++ b/src/includes/templates/htaccess/canonical-urls-no-ts-enable.txt @@ -10,6 +10,9 @@ # Not a part of the WP admin area. RewriteCond %{REQUEST_URI} !(?:^|/)wp\-admin(?:/|$) + # Not a REST request, which never redirects. + RewriteCond %{REQUEST_URI} !(?:^|/)%%REST_REQUEST_PREFIX_AS_REGEX_FRAG%%(?:/|$) + # If there is a trailing slash. RewriteCond %{REQUEST_URI} /$ diff --git a/src/includes/templates/htaccess/canonical-urls-ts-enable.txt b/src/includes/templates/htaccess/canonical-urls-ts-enable.txt index 9be96ab..d1fb864 100644 --- a/src/includes/templates/htaccess/canonical-urls-ts-enable.txt +++ b/src/includes/templates/htaccess/canonical-urls-ts-enable.txt @@ -10,6 +10,9 @@ # Not a part of the WP admin area. RewriteCond %{REQUEST_URI} !(?:^|/)wp\-admin(?:/|$) + # Not a REST request, which never redirects. + RewriteCond %{REQUEST_URI} !(?:^|/)%%REST_REQUEST_PREFIX_AS_REGEX_FRAG%%(?:/|$) + # If there is no trailing slash. RewriteCond %{REQUEST_URI} !/$ diff --git a/src/includes/templates/htaccess/enforce-exact-host-name.txt b/src/includes/templates/htaccess/enforce-exact-host-name.txt new file mode 100644 index 0000000..82901d9 --- /dev/null +++ b/src/includes/templates/htaccess/enforce-exact-host-name.txt @@ -0,0 +1,15 @@ +# Enforce exact host name. + + RewriteEngine on + RewriteBase %%REWRITE_BASE%% + + RewriteCond %{HTTP_HOST} !^%%HOST_NAME_AS_REGEX_FRAG%%$ + RewriteCond %{HTTPS} !^on$ [NC] + RewriteCond %{HTTP:X-Forwarded-Proto} !^https$ [NC] + RewriteRule .* http://%%HOST_NAME_AS_REGEX_FRAG%%%{REQUEST_URI} [R=301,L] + + RewriteCond %{HTTP_HOST} !^%%HOST_NAME_AS_REGEX_FRAG%%$ + RewriteCond %{HTTPS} ^on$ [NC,OR] + RewriteCond %{HTTP:X-Forwarded-Proto} ^https$ [NC] + RewriteRule .* https://%%HOST_NAME_AS_REGEX_FRAG%%%{REQUEST_URI} [R=301,L] + diff --git a/src/includes/traits/Ac/NcDebugUtils.php b/src/includes/traits/Ac/NcDebugUtils.php index 077b314..8e2eb2d 100644 --- a/src/includes/traits/Ac/NcDebugUtils.php +++ b/src/includes/traits/Ac/NcDebugUtils.php @@ -10,7 +10,7 @@ trait NcDebugUtils * * @since 150422 Rewrite. * - * @var array An array of debug info; i.e. `reason_code` and `reason` (optional). + * @type array An array of debug info; i.e. `reason_code` and `reason` (optional). */ public $debug_info = ['reason_code' => '', 'reason' => '']; @@ -38,6 +38,7 @@ public function maybeSetDebugInfo($reason_code, $reason = '') * Echoes `NC_DEBUG_` info in the WordPress `shutdown` phase (if applicable). * * @since 150422 Rewrite. + * @since 17xxxx Do not display for API requests. * * @attaches-to `shutdown` hook in WordPress w/ a late priority. */ @@ -52,6 +53,12 @@ public function maybeEchoNcDebugInfo() if (strcasecmp(PHP_SAPI, 'cli') === 0) { return; // Let's not run the risk here. } + if (defined('XMLRPC_REQUEST') && XMLRPC_REQUEST) { + return; // Let's not run the risk here. + } + if (defined('REST_REQUEST') && REST_REQUEST) { + return; // Let's not run the risk here. + } if ($this->debug_info && $this->hasACacheableContentType() && $this->is_a_wp_content_type) { echo (string) $this->maybeGetNcDebugInfo($this->debug_info['reason_code'], $this->debug_info['reason']); } @@ -61,6 +68,7 @@ public function maybeEchoNcDebugInfo() * Gets `NC_DEBUG_` info (if applicable). * * @since 150422 Rewrite. + * @since 17xxxx Adding API request constants. * * @param string $reason_code One of the `NC_DEBUG_` constants. * @param string $reason Optional; to override the default description with a custom message. @@ -110,6 +118,14 @@ public function maybeGetNcDebugInfo($reason_code = '', $reason = '') $reason = __('because the environment variable `$_SERVER[\'DONOTCACHEPAGE\']` has been set at runtime. Perhaps by WordPress itself, or by one of your themes/plugins. This usually means that you have a theme/plugin intentionally disabling the cache on this page; and it\'s usually for a very good reason.', 'comet-cache'); break; // Break switch handler. + case $this::NC_DEBUG_XMLRPC_REQUEST_CONSTANT: + $reason = __('because the PHP constant `XMLRPC_REQUEST` has been set to a boolean-ish `TRUE` value at runtime. XML-RPC requests are never cached, as they are often very dynamic in nature.', 'comet-cache'); + break; // Break switch handler. + + case $this::NC_DEBUG_REST_REQUEST_CONSTANT: + $reason = __('because the PHP constant `REST_REQUEST` has been set to a boolean-ish `TRUE` value at runtime. REST requests are never cached, as they are often very dynamic in nature.', 'comet-cache'); + break; // Break switch handler. + case $this::NC_DEBUG_AC_GET_VAR: $reason = sprintf(__('because `$_GET[\'%1$sAC\']` is set to a boolean-ish FALSE value.', 'comet-cache'), mb_strtolower(SHORT_NAME)); break; // Break switch handler. diff --git a/src/includes/traits/Ac/ObUtils.php b/src/includes/traits/Ac/ObUtils.php index 2089277..a3e69e3 100644 --- a/src/includes/traits/Ac/ObUtils.php +++ b/src/includes/traits/Ac/ObUtils.php @@ -118,9 +118,12 @@ trait ObUtils /** * Start output buffering or serve cache. * - * @since 150422 Rewrite. This is a vital part of Comet Cache. - * This method serves existing (fresh) cache files. It is also responsible - * for beginning the process of collecting the output buffer. + * @since 150422 Rewrite. + * @since 17xxxx Adding API request constants. + * + * @note This is a vital part of Comet Cache. + * This method serves existing (fresh) cache files. It is also responsible + * for beginning the process of collecting the output buffer. */ public function maybeStartOutputBuffering() { @@ -145,6 +148,12 @@ public function maybeStartOutputBuffering() if (isset($_SERVER['DONOTCACHEPAGE'])) { return $this->maybeSetDebugInfo($this::NC_DEBUG_DONOTCACHEPAGE_SERVER_VAR); } + if (defined('XMLRPC_REQUEST') && XMLRPC_REQUEST) { + return $this->maybeSetDebugInfo($this::NC_DEBUG_XMLRPC_REQUEST_CONSTANT); + } + if (defined('REST_REQUEST') && REST_REQUEST) { + return $this->maybeSetDebugInfo($this::NC_DEBUG_REST_REQUEST_CONSTANT); + } if (isset($_GET[mb_strtolower(SHORT_NAME).'AC']) && !filter_var($_GET[mb_strtolower(SHORT_NAME).'AC'], FILTER_VALIDATE_BOOLEAN)) { return $this->maybeSetDebugInfo($this::NC_DEBUG_AC_GET_VAR); } @@ -240,9 +249,6 @@ public function maybeStartOutputBuffering() $DebugNotes = new Classes\Notes(); - $DebugNotes->addAsciiArt(sprintf(__('%1$s is Fully Functional', 'comet-cache'), NAME)); - $DebugNotes->addLineBreak(); - $DebugNotes->add(__('Loaded via Cache On', 'comet-cache'), date('M jS, Y @ g:i a T')); $DebugNotes->add(__('Loaded via Cache In', 'comet-cache'), sprintf(__('%1$s seconds', 'comet-cache'), $total_time)); @@ -258,10 +264,8 @@ public function maybeStartOutputBuffering() /** * Output buffer handler; i.e. the cache file generator. * - * @note We CANNOT depend on any WP functionality here; it will cause problems. - * Anything we need from WP should be saved in the postload phase as a scalar value. - * * @since 150422 Rewrite. + * @since 17xxxx Adding API request constants. * * @param string $buffer The buffer from {@link \ob_start()}. * @param int $phase A set of bitmask flags. @@ -270,6 +274,9 @@ public function maybeStartOutputBuffering() * * @return string|bool The output buffer, or `FALSE` to indicate no change. * + * @note We CANNOT depend on any WP functionality here; it will cause problems. + * Anything we need from WP should be saved in the postload phase as a scalar value. + * * @attaches-to {@link \ob_start()} */ public function outputBufferCallbackHandler($buffer, $phase) @@ -299,6 +306,12 @@ public function outputBufferCallbackHandler($buffer, $phase) if (isset($_SERVER['DONOTCACHEPAGE'])) { return (bool) $this->maybeSetDebugInfo($this::NC_DEBUG_DONOTCACHEPAGE_SERVER_VAR); } + if (defined('XMLRPC_REQUEST') && XMLRPC_REQUEST) { + return (bool) $this->maybeSetDebugInfo($this::NC_DEBUG_XMLRPC_REQUEST_CONSTANT); + } + if (defined('REST_REQUEST') && REST_REQUEST) { + return (bool) $this->maybeSetDebugInfo($this::NC_DEBUG_REST_REQUEST_CONSTANT); + } if ((!IS_PRO || !COMET_CACHE_WHEN_LOGGED_IN) && $this->is_user_logged_in) { return (bool) $this->maybeSetDebugInfo($this::NC_DEBUG_IS_LOGGED_IN_USER); } @@ -371,7 +384,7 @@ public function outputBufferCallbackHandler($buffer, $phase) $time = time(); // Needed below for expiration calculation. $DebugNotes = new Classes\Notes(); - $DebugNotes->addAsciiArt(sprintf(__('%1$s Notes', 'comet-cache'), NAME)); + $DebugNotes->addAsciiArt(sprintf(__('%1$s is Fully Functional', 'comet-cache'), NAME)); $DebugNotes->addLineBreak(); if (IS_PRO && COMET_CACHE_WHEN_LOGGED_IN && $this->user_token) { diff --git a/src/includes/traits/Plugin/HtaccessUtils.php b/src/includes/traits/Plugin/HtaccessUtils.php index ce58765..0d6deeb 100644 --- a/src/includes/traits/Plugin/HtaccessUtils.php +++ b/src/includes/traits/Plugin/HtaccessUtils.php @@ -15,15 +15,20 @@ trait HtaccessUtils public $htaccess_marker = 'WmVuQ2FjaGU'; /** - * Plugin options that have associated htaccess rules. + * Plugin options that have htaccess rules. * * @since 160103 Improving `.htaccess` tweaks. * - * @return array Plugin options that have associated htaccess rules + * @return array Plugin options that have htaccess rules. * - * @note We keep track of this to avoid the issue described here: http://git.io/vEFIH + * @note This avoids: */ - public $options_with_htaccess_rules = ['cdn_enable', 'htaccess_browser_caching_enable', 'htaccess_gzip_enable', 'htaccess_enforce_canonical_urls',]; + public $options_with_htaccess_rules = [ + 'cdn_enable', + 'htaccess_browser_caching_enable', + 'htaccess_gzip_enable', + 'htaccess_enforce_canonical_urls', + ]; /** * Add template blocks to `/.htaccess` file. @@ -32,14 +37,13 @@ trait HtaccessUtils * * @return bool True if added successfully. * - * @TODO Improve error reporting detail to better catch unexpected failures; see http://git.io/vEFLT + * @TODO Improve error reporting detail to better + * catch unexpected failures. See: */ public function addWpHtaccess() { - global $is_apache; // WP global for web server checks below. - - if (!$is_apache) { - return false; // Not running the Apache web server. + if (!$this->isApache()) { + return false; // Not Apache. } if (!$this->options['enable']) { return true; // Nothing to do. @@ -54,16 +58,23 @@ public function addWpHtaccess() return false; // Unable to remove. } if (!($htaccess = $this->readHtaccessFile())) { - return false; // Failure; could not read file or invalid UTF8 encountered, file may be corrupt. + return false; // Failure; could not read file. } - $template_blocks = ''; // Initialize. - foreach (array('gzip-enable.txt', 'access-control-allow-origin-enable.txt', 'browser-caching-enable.txt', 'canonical-urls-ts-enable.txt', 'canonical-urls-no-ts-enable.txt') as $_template) { + foreach ([ + 'gzip-enable.txt', + 'access-control-allow-origin-enable.txt', + 'browser-caching-enable.txt', + 'enforce-exact-host-name.txt', + 'canonical-urls-ts-enable.txt', + 'canonical-urls-no-ts-enable.txt', + ] as $_template) { + // if (!is_file($_template_file = dirname(dirname(dirname(__FILE__))).'/templates/htaccess/'.$_template)) { continue; // Template file missing; bypass. - } // ↑ Some files might be missing in the lite version. - elseif (!($_template_file_contents = trim(file_get_contents($_template_file)))) { + // ↑ Some files might be missing in the lite version. + } elseif (!($_template_file_contents = trim(file_get_contents($_template_file)))) { continue; // Template file empty; bypass. } // ↑ Some files might be empty in the lite version. @@ -73,16 +84,15 @@ public function addWpHtaccess() $template_blocks .= $_template_file_contents."\n\n"; } // ↑ Only if GZIP is enabled at this time. break; + } - } - unset($_template_file); // Housekeeping + } // unset($_template_file); // Housekeeping. if (empty($template_blocks)) { // Do we need to add anything to htaccess? $this->closeHtaccessFile($htaccess); // No need to write to htaccess file in this case. return true; // Nothing to do, but no failures either. } - $template_blocks = $this->fillReplacementCodes($template_blocks); $template_header = '# BEGIN '.NAME.' '.$this->htaccess_marker.' (the '.$this->htaccess_marker.' marker is required for '.NAME.'; do not remove)'; $template_footer = '# END '.NAME.' '.$this->htaccess_marker; @@ -91,7 +101,6 @@ public function addWpHtaccess() if (!$this->writeHtaccessFile($htaccess, true)) { return false; // Failure; could not write changes. } - return true; // Added successfully. } @@ -102,32 +111,29 @@ public function addWpHtaccess() * * @return bool True if removed successfully. * - * @TODO Improve error reporting detail to better catch unexpected failures; see http://git.io/vEFLT + * @TODO Improve error reporting detail to better + * catch unexpected failures. See: */ public function removeWpHtaccess() { - global $is_apache; - - if (!$is_apache) { - return false; // Not running the Apache web server. + if (!$this->isApache()) { + return false; // Not running Apache. } if (!($htaccess_file = $this->findHtaccessFile())) { return true; // File does not exist. } if (!$this->findHtaccessMarker()) { - return true; // Template blocks are already gone. + return true; // Template blocks are gone. } if (!($htaccess = $this->readHtaccessFile())) { - return false; // Failure; could not read file, create file, or invalid UTF8 encountered, file may be corrupt. + return false; // Failure; could not read file. } - $regex = '/#\s*BEGIN\s+'.preg_quote(NAME, '/').'\s+'.$this->htaccess_marker.'.*?#\s*END\s+'.preg_quote(NAME, '/').'\s+'.$this->htaccess_marker.'\s*/uis'; $htaccess['file_contents'] = preg_replace($regex, '', $htaccess['file_contents']); if (!$this->writeHtaccessFile($htaccess, false)) { - return false; // Failure; could not write changes. + return false; // Failure; could not write. } - return true; // Removed successfully. } @@ -136,8 +142,7 @@ public function removeWpHtaccess() * * @since 151114 Adding `.htaccess` tweaks. * - * @return string Absolute server path to `/.htaccess` file; - * else an empty string if unable to locate the file. + * @return string Absolute server path to `/.htaccess` file. */ public function findHtaccessFile() { @@ -155,29 +160,30 @@ public function findHtaccessFile() * * @since 160103 Improving `.htaccess` tweaks. * - * @return bool True when an option is enabled that requires htaccess rules, false otherwise. + * @return bool True when an option is enabled that requires htaccess rules. */ public function needHtaccessRules() { if (!is_array($this->options_with_htaccess_rules)) { - return false; // Nothing to do. + return false; // Not even possible. } - foreach ($this->options_with_htaccess_rules as $option) { - if ($this->options[$option]) { - return true; // Yes, there are options enabled that require htaccess rules. + foreach ($this->options_with_htaccess_rules as $_option) { + if ($this->options[$_option]) { + return true; // Yes. } - } - return false; // No, there are no options enabled that require htaccess rules. + } // unset($_option); // Housekeeping. + + return false; } /** - * Utility method used to check if htaccess file contains $htaccess_marker. + * Utility method used to check if htaccess file contains `$htaccess_marker`. * * @since 151114 Adding `.htaccess` tweaks. * * @param string $htaccess_marker Unique comment marker used to identify rules added by this plugin. * - * @return bool False on failure or when marker does not exist in htaccess, true otherwise. + * @return bool False on failure or when marker does not exist in htaccess. */ public function findHtaccessMarker($htaccess_marker = '') { @@ -194,14 +200,13 @@ public function findHtaccessMarker($htaccess_marker = '') $htaccess_marker = $this->htaccess_marker; } if (mb_stripos($htaccess_file_contents, $htaccess_marker) === false) { - return false; // Htaccess marker is missing + return false; // Htaccess marker is missing. } - - return true; // Htaccess has the marker + return true; // Htaccess has the marker. } /** - * Utility method used to update replacement codes in .htaccess templates + * Utility method used to update replacement codes in .htaccess templates. * * @since 160706 Adding Apache Optimizations * @@ -212,16 +217,16 @@ public function findHtaccessMarker($htaccess_marker = '') public function fillReplacementCodes($template_blocks) { if (mb_stripos($template_blocks, '%%') === false) { - return $template_blocks; // No replacement codes to fill + return $template_blocks; // No replacement codes to fill. } - - $home_url = is_multisite() ? network_home_url() : home_url(); - $replacement_codes = ['%%REWRITE_BASE%%' => trailingslashit(parse_url($home_url, PHP_URL_PATH))]; - + $replacement_codes = [ + '%%REWRITE_BASE%%' => trailingslashit(parse_url(network_home_url(), PHP_URL_PATH)), + '%%HOST_NAME_AS_REGEX_FRAG%%' => mb_strtolower(parse_url(network_home_url(), PHP_URL_HOST)), + '%%REST_REQUEST_PREFIX_AS_REGEX_FRAG%%' => rest_get_url_prefix(), + ]; foreach ($replacement_codes as $_code => $_replacement) { $template_blocks = preg_replace('/'.preg_quote($_code, '/').'/ui', $_replacement, $template_blocks); - } - unset($_code, $_replacement); + } // unset($_code, $_replacement); return $template_blocks; } @@ -257,7 +262,7 @@ public function readHtaccessFile($htaccess_file = '') if (($file_contents = fread($fp, filesize($htaccess_file))) && ($file_contents === wp_check_invalid_utf8($file_contents))) { rewind($fp); // Rewind pointer to beginning of file. return compact('fp', 'file_contents'); - } else { // Failure; could not read file or invalid UTF8 encountered, file may be corrupt. + } else { // Failure; could not read file. flock($fp, LOCK_UN); fclose($fp); return false; @@ -273,7 +278,7 @@ public function readHtaccessFile($htaccess_file = '') * @param bool $require_marker Whether or not to require the marker be present in contents before writing. * @param string $htaccess_marker Unique comment marker used to identify rules added by this plugin. * - * @return bool True on success, false on failure. + * @return bool True on success. */ public function writeHtaccessFile(array $htaccess, $require_marker = true, $htaccess_marker = '') { @@ -291,7 +296,7 @@ public function writeHtaccessFile(array $htaccess, $require_marker = true, $htac if (($require_marker && $_have_marker === false) || !rewind($htaccess['fp']) || !ftruncate($htaccess['fp'], 0) || !fwrite($htaccess['fp'], $htaccess['file_contents'])) { flock($htaccess['fp'], LOCK_UN); fclose($htaccess['fp']); - return false; // Failure; could not write changes. + return false; // Failure. } fflush($htaccess['fp']); flock($htaccess['fp'], LOCK_UN); @@ -305,14 +310,14 @@ public function writeHtaccessFile(array $htaccess, $require_marker = true, $htac * * @since 151114 Adding `.htaccess` tweaks. * - * @param array $htaccess Array containing at least an `fp` file resource pointing to htaccess file. + * @param array $htaccess Array containing at least an `fp` file resource. * - * @return bool False on failure, true otherwise. + * @return bool False on failure. */ public function closeHtaccessFile(array $htaccess) { if (!is_resource($htaccess['fp'])) { - return false; // Failure; requires a valid file resource. + return false; // Failure. } flock($htaccess['fp'], LOCK_UN); fclose($htaccess['fp']); diff --git a/src/includes/traits/Plugin/WcpDateArchiveUtils.php b/src/includes/traits/Plugin/WcpDateArchiveUtils.php new file mode 100644 index 0000000..6de4626 --- /dev/null +++ b/src/includes/traits/Plugin/WcpDateArchiveUtils.php @@ -0,0 +1,94 @@ +cacheKey('autoClearDateArchiveCache', [$post_id, $force]))) { + return $counter; // Already did this. + } + $done = true; // Flag as having been done. + + if (!$this->options['enable']) { + return $counter; // Nothing to do. + } + if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) { + return $counter; // Nothing to do. + } + if (!$this->options['cache_clear_date_archives_enable']) { + return $counter; // Nothing to do. + } + if (!is_dir($cache_dir = $this->cacheDir())) { + return $counter; // Nothing to do. + } + $post_status = get_post_status($post_id); // Cache this. + + if ($post_status === 'draft' && isset($GLOBALS['pagenow'], $_POST['publish']) + && is_admin() && $GLOBALS['pagenow'] === 'post.php' && current_user_can('publish_posts') + && mb_strpos(wp_get_referer(), '/post-new.php') !== false + ) { + $post_status = 'publish'; // A new post being published now. + } + if (in_array($post_status, ['inherit', 'auto-draft'], true)) { + return $counter; // Nothing to do. Note: `inherit` = revision. + } + if (in_array($post_status, ['draft', 'pending', 'future'], true) && !$force) { + return $counter; // Nothing to do; i.e., NOT forcing in this case. + } + $date_archive_urls = []; // Initialize archive urls. + $publish_time = get_post_time('U', true, $post_id); + + $Y = strtotime('Y', $publish_time); + $m = strtotime('m', $publish_time); + $j = strtotime('j', $publish_time); + + if ($this->options['cache_clear_date_archives_enable'] === '1') { + $date_archive_links[sprintf(__('%1$s Date Archive', 'comet-cache'), $Y)] = get_year_link($Y); + $date_archive_links[sprintf(__('%1$s/%2$s Date Archive', 'comet-cache'), $Y, $m)] = get_month_link($Y, $m); + $date_archive_links[sprintf(__('%1$s/%2$s/%3$s Date Archive', 'comet-cache'), $Y, $m, $j)] = get_day_link($Y, $m, $j); + } elseif ($this->options['cache_clear_date_archives_enable'] === '2') { + $date_archive_links[sprintf(__('%1$s/%2$s Date Archive', 'comet-cache'), $Y, $m)] = get_month_link($Y, $m); + $date_archive_links[sprintf(__('%1$s/%2$s/%3$s Date Archive', 'comet-cache'), $Y, $m, $j)] = get_day_link($Y, $m, $j); + } else { // Assume $this->options['cache_clear_date_archives_enable'] === '3' + $date_archive_links[sprintf(__('%1$s/%2$s/%3$s Date Archive', 'comet-cache'), $Y, $m, $j)] = get_day_link($Y, $m, $j); + } + foreach ($date_archive_urls as $_label => $_url) { + $_url_regex = $this->buildHostCachePathRegex($_url); + $_url_counter = $this->clearFilesFromHostCacheDir($_url_regex); + $counter += $_url_counter; // Add to overall counter. + + if ($_url_counter && $enqueued_notices < 100 && is_admin() && (!IS_PRO || $this->options['change_notifications_enable'])) { + $this->enqueueNotice(sprintf(__('Found %1$s in the cache for %2$s; auto-clearing.', 'comet-cache'), esc_html($this->i18nFiles($_url_counter)), esc_html($_label)), ['combinable' => true]); + ++$enqueued_notices; // Increment enqueued notices counter. + } + } // unset($_label, $_url, $_url_regex, $_url_counter); // Housekeeping. + + return $counter; + } +} diff --git a/src/includes/traits/Plugin/WcpPostUtils.php b/src/includes/traits/Plugin/WcpPostUtils.php index b889546..cda6092 100644 --- a/src/includes/traits/Plugin/WcpPostUtils.php +++ b/src/includes/traits/Plugin/WcpPostUtils.php @@ -100,6 +100,7 @@ public function autoClearPostCache($post_id, $force = false) $counter += $this->autoClearHomePageCache(); $counter += $this->autoClearPostsPageCache(); $counter += $this->autoClearPostTermsCache($post_id, $force); + $counter += $this->autoClearDateArchiveCache($post_id, $force); $counter += $this->autoClearCustomPostTypeArchiveCache($post_id); diff --git a/src/includes/traits/Shared/CacheDirUtils.php b/src/includes/traits/Shared/CacheDirUtils.php index dc22135..7227564 100644 --- a/src/includes/traits/Shared/CacheDirUtils.php +++ b/src/includes/traits/Shared/CacheDirUtils.php @@ -272,14 +272,19 @@ public function deleteFilesFromHostCacheDir( // On a standard installation delete from all hosts. // See: if (!is_multisite() && !$___considering_domain_mapping) { - $regex = ltrim($regex, '^\\/'); - - if (mb_strpos($regex, '(?:\/') === 0 || mb_strpos($regex, '(\/') === 0) { - $regex = '/^https?\/[^\/]+'.$regex; - } else { - $regex = '/^https?\/[^\/]+\/'.$regex; + if (in_array(rtrim(str_replace(['^', '$'], '', $regex), 'ui'), ['/.*/', '/.+/'], true)) { + return $this->deleteFilesFromCacheDir($regex, $check_max_age); + // + } else { // Clearing specifics. + $regex = ltrim($regex, '^\\/'); + + if (mb_strpos($regex, '(?:\/') === 0 || mb_strpos($regex, '(\/') === 0) { + $regex = '/^https?\/[^\/]+'.$regex; + } else { + $regex = '/^https?\/[^\/]+\/'.$regex; + } + return $this->deleteFilesFromCacheDir($regex, $check_max_age); } - return $this->deleteFilesFromCacheDir($regex, $check_max_age); } $cache_dir = $this->nDirSeps($cache_dir); // Normalize. $host_token = $current_host_token = $this->hostToken(); diff --git a/src/includes/traits/Shared/ServerUtils.php b/src/includes/traits/Shared/ServerUtils.php index b01a762..61e6e17 100644 --- a/src/includes/traits/Shared/ServerUtils.php +++ b/src/includes/traits/Shared/ServerUtils.php @@ -14,16 +14,19 @@ trait ServerUtils */ public function isApache() { - if (!is_null($is = &$this->staticKey('isApache'))) { + if (!is_null($is = &$this->staticKey(__FUNCTION__))) { return $is; // Already cached this. } if (!empty($_SERVER['SERVER_SOFTWARE']) && is_string($_SERVER['SERVER_SOFTWARE'])) { if (mb_stripos($_SERVER['SERVER_SOFTWARE'], 'apache') !== false) { return $is = true; - } - if (mb_stripos($_SERVER['SERVER_SOFTWARE'], 'litespeed') !== false) { + } elseif (mb_stripos($_SERVER['SERVER_SOFTWARE'], 'litespeed') !== false) { return $is = true; } + } // Checking `SERVER_SOFTWARE` is faster. + + if ($this->functionIsPossible('apache_get_version')) { + return $is = true; } return $is = false; } @@ -37,13 +40,17 @@ public function isApache() */ public function isNginx() { - if (!is_null($is = &$this->staticKey('isNginx'))) { + if (!is_null($is = &$this->staticKey(__FUNCTION__))) { return $is; // Already cached this. } if (!empty($_SERVER['SERVER_SOFTWARE']) && is_string($_SERVER['SERVER_SOFTWARE'])) { if (mb_stripos($_SERVER['SERVER_SOFTWARE'], 'nginx') !== false) { return $is = true; } + } // Checking `SERVER_SOFTWARE` is faster. + + if (!empty($_SERVER['WP_NGINX_CONFIG'])) { + return $is = true; // See: } return $is = false; } @@ -57,17 +64,17 @@ public function isNginx() */ public function isIis() { - if (!is_null($is = &$this->staticKey('isIis'))) { + if (!is_null($is = &$this->staticKey(__FUNCTION__))) { return $is; // Already cached this. } if (!empty($_SERVER['SERVER_SOFTWARE']) && is_string($_SERVER['SERVER_SOFTWARE'])) { if (mb_stripos($_SERVER['SERVER_SOFTWARE'], 'microsoft-iis') !== false) { return $is = true; - } - if (mb_stripos($_SERVER['SERVER_SOFTWARE'], 'expressiondevserver') !== false) { + } elseif (mb_stripos($_SERVER['SERVER_SOFTWARE'], 'expressiondevserver') !== false) { return $is = true; } - } + } // Checking `SERVER_SOFTWARE` is faster. + return $is = false; } } diff --git a/src/includes/uninstall.php b/src/includes/uninstall.php index 0ad4941..be915af 100644 --- a/src/includes/uninstall.php +++ b/src/includes/uninstall.php @@ -9,7 +9,7 @@ use WebSharks\CometCache\Classes; if (!defined('WPINC')) { - exit('Do NOT access this file directly: '.basename(__FILE__)); + exit('Do NOT access this file directly.'); } require_once __DIR__.'/stub.php'; diff --git a/uninstall.php b/uninstall.php index 1e4ab23..1993382 100644 --- a/uninstall.php +++ b/uninstall.php @@ -1,6 +1,6 @@