diff --git a/quick-cache-pro/includes/menu-pages.php b/quick-cache-pro/includes/menu-pages.php
index a2014945..fb77dde9 100644
--- a/quick-cache-pro/includes/menu-pages.php
+++ b/quick-cache-pro/includes/menu-pages.php
@@ -223,6 +223,13 @@ public function options()
echo ' plugin->options['cache_purge_term_other_enable'], '0', FALSE).'>'.__('No, my site doesn\'t use any custom Terms and/or I don\'t have any custom Term archive views.', $this->plugin->text_domain).' '."\n";
echo '
'."\n";
echo ' '."\n";
+ echo ' '.__('Auto-Purge "RSS/RDF/ATOM Feeds" Too?', $this->plugin->text_domain).' '."\n";
+ echo ' '.__('If you enable Feed Caching (below), this can be quite handy. If enabled, when you update a Post/Page, approve a Comment, or make other changes where Quick Cache can detect that certain types of Feeds should be purged to keep your site up-to-date, then Quick Cache will do this for you automatically. For instance, the blog\'s master feed, the blog\'s master comments feed, feeds associated with comments on a Post/Page, term-related feeds (including mixed term-related feeds), author-related feeds, etc. Under various circumstances (i.e. as you work in the Dashboard) these can be purged automatically to keep your site up-to-date.', $this->plugin->text_domain).'
'."\n";
+ echo ' '."\n";
+ echo ' plugin->options['cache_purge_xml_feeds_enable'], '1', FALSE).'>'.__('Yes, automatically purge RSS/RDF/ATOM Feeds from the cache when certain changes occur.', $this->plugin->text_domain).' '."\n";
+ echo ' plugin->options['cache_purge_xml_feeds_enable'], '0', FALSE).'>'.__('No, I don\'t have Feed Caching enabled, or I prefer not to automatically purge Feeds.', $this->plugin->text_domain).' '."\n";
+ echo '
'."\n";
+ echo ' '."\n";
echo ' '.__('Auto-Purge "XML Sitemaps" Too?', $this->plugin->text_domain).' '."\n";
echo ' '.__('If you\'re generating XML Sitemaps with a plugin like Google XML Sitemaps , you can tell Quick Cache to automatically purge the cache of any XML Sitemaps whenever it purges a Post/Page. Note; this does NOT purge the XML Sitemap itself of course, only the cache. The point being, to clear the cache and allow changes to a Post/Page to be reflected by a fresh copy of your XML Sitemap; sooner rather than later.', $this->plugin->text_domain).'
'."\n";
echo ' '."\n";
@@ -354,7 +361,7 @@ public function options()
echo ' '."\n";
echo '
'."\n";
echo '
'.__('Caching Enabled for RSS, RDF, Atom Feeds?', $this->plugin->text_domain).' '."\n";
- echo '
'.__('This should almost ALWAYS be set to No
. UNLESS, you\'re sure that you want to cache your feeds. If you use a web feed management provider like Google® Feedburner and you set this option to Yes
, you may experience delays in the detection of new posts.', $this->plugin->text_domain).'
'."\n";
+ echo '
'.__('This should almost ALWAYS be set to No
. UNLESS, you\'re sure that you want to cache your feeds. If you use a web feed management provider like Google® Feedburner and you set this option to Yes
, you may experience delays in the detection of new posts. NOTE: If you do enable this, it is highly recommended that you also enable automatic Feed Purging too. Please see the section above: "Clearing the Cache". Find the sub-section titled: "Auto-Purge RSS/RDF/ATOM Feeds".', $this->plugin->text_domain).'
'."\n";
echo '
'."\n";
echo ' plugin->options['feeds_enable'], '0', FALSE).'>'.__('No, do NOT cache (or serve a cache file) when displaying a feed.', $this->plugin->text_domain).' '."\n";
echo ' plugin->options['feeds_enable'], '1', FALSE).'>'.__('Yes, I would like to cache feed URLs.', $this->plugin->text_domain).' '."\n";
diff --git a/quick-cache-pro/includes/share.php b/quick-cache-pro/includes/share.php
index c6d794ee..07b546a0 100644
--- a/quick-cache-pro/includes/share.php
+++ b/quick-cache-pro/includes/share.php
@@ -243,6 +243,9 @@ public function build_cache_path($url, $with_user_token = '', $with_version_salt
if($url && strpos($url, '://') === FALSE)
$url = '//'.ltrim($url, '/');
+ if($url && strpos($url, '&') !== FALSE)
+ $url = str_replace('&', '&', $url);
+
if(!$url || !($url = parse_url($url)))
return ''; // Invalid URL.
@@ -902,6 +905,42 @@ public function esc_sq($string, $times = 1)
return str_replace("'", str_repeat('\\', abs($times))."'", (string)$string);
}
+ /**
+ * Normalizes directory/file separators.
+ *
+ * @since 14xxxx Implementing XML/RSS feed purging.
+ *
+ * @param string $dir_file Directory/file path.
+ *
+ * @param boolean $allow_trailing_slash Defaults to FALSE.
+ * If TRUE; and `$dir_file` contains a trailing slash; we'll leave it there.
+ *
+ * @return string Normalized directory/file path.
+ */
+ public function n_dir_seps($dir_file, $allow_trailing_slash = FALSE)
+ {
+ $dir_file = (string)$dir_file; // Force string value.
+ if(!isset($dir_file[0])) return ''; // Catch empty string.
+
+ if(strpos($dir_file, '://' !== FALSE)) // A possible stream wrapper?
+ {
+ if(preg_match('/^(?P[a-zA-Z0-9]+)\:\/\//', $dir_file, $stream_wrapper))
+ $dir_file = preg_replace('/^(?P[a-zA-Z0-9]+)\:\/\//', '', $dir_file);
+ }
+ if(strpos($dir_file, ':' !== FALSE)) // Might have a Windows® drive letter?
+ {
+ if(preg_match('/^(?P[a-zA-Z])\:[\/\\\\]/', $dir_file)) // It has a Windows® drive letter?
+ $dir_file = preg_replace_callback('/^(?P[a-zA-Z])\:[\/\\\\]/', create_function('$m', 'return strtoupper($m[0]);'), $dir_file);
+ }
+ $dir_file = preg_replace('/\/+/', '/', str_replace(array(DIRECTORY_SEPARATOR, '\\', '/'), '/', $dir_file));
+ $dir_file = ($allow_trailing_slash) ? $dir_file : rtrim($dir_file, '/'); // Strip trailing slashes.
+
+ if(!empty($stream_wrapper[0])) // Stream wrapper (force lowercase).
+ $dir_file = strtolower($stream_wrapper[0]).$dir_file;
+
+ return $dir_file; // Normalized now.
+ }
+
/**
* Recursive directory iterator based on a regex pattern.
*
@@ -922,6 +961,66 @@ public function dir_regex_iteration($dir, $regex)
return $regex_iterator;
}
+ /**
+ * Deletes files from the cache directory (for the current host) that match a regex pattern.
+ *
+ * @since 14xxxx Implementing XML/RSS feed purging.
+ *
+ * @param string $regex A regex pattern; compares to each full file path.
+ *
+ * @return integer Total files deleted by this routine (if any).
+ *
+ * @throws \exception If unable to delete a file for any reason.
+ *
+ * @TODO @raamdev I think we could take the same concept introduced by this routine and use it to build others
+ * that deal with purging/clearing/wiping; thereby centralizing this sort of job, making QC DRYer.
+ * Also, this type of routine works to improve speed when running on a large MS network.
+ *
+ * Suggested class members to come in a future release of QC.
+ * - `purge_files_from_host_cache_dir()`
+ * - `clear_files_from_host_cache_dir()`
+ *
+ * Also, this class member (i.e. `delete_files_from_host_cache_dir()`) could be
+ * used by many of the existing auto-purge routines. Thereby requiring less code
+ * and speeding QC up overall; i.e. making it faster on large MS networks.
+ */
+ public function delete_files_from_host_cache_dir($regex)
+ {
+ $counter = 0; // Initialize.
+
+ if(!($regex = (string)$regex))
+ return $counter; // Nothing to do.
+
+ if(!is_dir($cache_dir = $this->cache_dir()))
+ return $counter; // Nothing to do.
+
+ $host = $_SERVER['HTTP_HOST'];
+ $host_base_dir_tokens = $this->host_base_dir_tokens();
+ $cache_dir = $this->n_dir_seps($cache_dir);
+
+ foreach(array('http', 'https') as $_host_scheme) // Consider all possible schemes.
+ {
+ $_host_url = $_host_scheme.'://'.$host.$host_base_dir_tokens; // Base URL for this host (w/ MS support).
+ $_host_cache_path = $this->build_cache_path($_host_url, '', '', $this::CACHE_PATH_NO_PATH_INDEX | $this::CACHE_PATH_NO_QUV | $this::CACHE_PATH_NO_EXT);
+ $_host_cache_dir = $this->n_dir_seps($cache_dir.'/'.$_host_cache_path);
+
+ /** @var $_dir_file \RecursiveDirectoryIterator For IDEs. */
+ if($_host_cache_dir && is_dir($_host_cache_dir)) foreach($this->dir_regex_iteration($_host_cache_dir, $regex) as $_dir_file)
+ {
+ if(($_dir_file->isFile() || $_dir_file->isLink()) && ($_host_cache_dir !== $cache_dir || strpos($_dir_file->getSubpathname(), '/') !== FALSE))
+ // Don't delete files in the immediate directory; e.g. `qc-advanced-cache` or `.htaccess`, etc.
+ // Actual `http|https/...` cache files are nested. Files in the immediate directory are for other purposes.
+ if(!unlink($_dir_file->getPathname())) // Throw exception if unable to delete.
+ throw new \exception(sprintf(__('Unable to delete file: `%1$s`.', $this->text_domain), $_dir_file->getPathname()));
+ else $counter++; // Increment counter for each file we purge.
+ }
+ unset($_dir_file); // Housekeeping.
+ }
+ unset($_host_scheme, $_host_url, $_host_cache_path, $_host_cache_dir); // Housekeeping.
+
+ return $counter; // Total files deleted by this routine.
+ }
+
/* --------------------------------------------------------------------------------------
* Hook/filter API for Quick Cache.
-------------------------------------------------------------------------------------- */
diff --git a/quick-cache-pro/quick-cache-pro.inc.php b/quick-cache-pro/quick-cache-pro.inc.php
index 1993e2a4..5a5dc780 100644
--- a/quick-cache-pro/quick-cache-pro.inc.php
+++ b/quick-cache-pro/quick-cache-pro.inc.php
@@ -182,6 +182,7 @@ public function setup()
'admin_bar_enable' => '1', // `0|1`.
'cache_clear_s2clean_enable' => '0', // `0|1`.
'cache_clear_eval_code' => '', // PHP code.
+ 'cache_purge_xml_feeds_enable' => '1', // `0|1`.
'cache_purge_xml_sitemaps_enable' => '1', // `0|1`.
'cache_purge_xml_sitemap_patterns' => '/sitemap*.xml', // Empty string or line-delimited patterns.
'cache_purge_home_page_enable' => '1', // `0|1`.
@@ -1395,7 +1396,6 @@ public function auto_purge_post_cache($id, $force = FALSE)
static::$static['___allow_auto_purge_post_cache'] = TRUE; // Reset state.
return $counter; // Nothing to do.
}
-
if(!$this->options['enable'])
return $counter; // Nothing to do.
@@ -1455,10 +1455,14 @@ public function auto_purge_post_cache($id, $force = FALSE)
}
unset($_file); // Just a little housekeeping.
- $counter += $this->auto_purge_xml_sitemaps_cache(); // If enabled and necessary.
- $counter += $this->auto_purge_home_page_cache(); // If enabled and necessary.
- $counter += $this->auto_purge_posts_page_cache(); // If enabled and necessary.
- $counter += $this->auto_purge_post_terms_cache($id, $force); // If enabled and necessary.
+ $counter += $this->auto_purge_xml_feeds_cache('blog');
+ $counter += $this->auto_purge_xml_feeds_cache('post-terms', $id);
+ $counter += $this->auto_purge_xml_feeds_cache('post-authors', $id);
+
+ $counter += $this->auto_purge_xml_sitemaps_cache();
+ $counter += $this->auto_purge_home_page_cache();
+ $counter += $this->auto_purge_posts_page_cache();
+ $counter += $this->auto_purge_post_terms_cache($id, $force);
return apply_filters(__METHOD__, $counter, get_defined_vars());
}
@@ -1507,6 +1511,266 @@ public function auto_purge_post_cache_transition($new_status, $old_status, \WP_P
return apply_filters(__METHOD__, $counter, get_defined_vars());
}
+ /**
+ * Automatically purges cache files related to XML feeds.
+ *
+ * @since 14xxxx Working to improve compatibility with feeds.
+ *
+ * @param string $type Type of feed(s) to auto-purge.
+ * @param integer $post_id A Post ID (when applicable).
+ *
+ * @return integer Total files purged by this routine (if any).
+ *
+ * @throws \exception If a purge failure occurs.
+ *
+ * @note Unlike many of the other `auto_` methods, this one is NOT currently
+ * attached to any hooks. However, it is called upon by other routines attached to hooks.
+ */
+ public function auto_purge_xml_feeds_cache($type, $post_id = 0)
+ {
+ $counter = 0; // Initialize.
+
+ if(!($type = (string)$type))
+ return $counter; // Nothing we can do.
+ $post_id = (integer)$post_id; // Force integer.
+
+ if(isset($this->cache[__FUNCTION__][$type][$post_id]))
+ return $counter; // Already did this.
+ $this->cache[__FUNCTION__][$type][$post_id] = -1;
+
+ if(!$this->options['enable'])
+ return $counter; // Nothing to do.
+
+ if(!$this->options['feeds_enable'])
+ return $counter; // Nothing to do.
+
+ if(!$this->options['cache_purge_xml_feeds_enable'])
+ return $counter; // Nothing to do.
+
+ if(!is_dir($cache_dir = $this->cache_dir()))
+ return $counter; // Nothing to do.
+
+ $home_url = home_url('/'); // Need this below.
+ $default_feed = get_default_feed(); // Need this below.
+ $seo_friendly_permalinks = (boolean)get_option('permalink_structure');
+ $_this = $this; // Reference needed by the closure below.
+ $feed_cache_path_regexs = array(); // Initialize array of feed cache paths.
+ $build_cache_path_regex = function ($feed_link, $wildcard_regex = NULL) use ($_this)
+ {
+ if(!is_string($feed_link) || !$feed_link)
+ return ''; // Nothing to do here.
+
+ $cache_path_flags = $_this::CACHE_PATH_NO_SCHEME | $_this::CACHE_PATH_NO_EXT
+ | $_this::CACHE_PATH_NO_USER | $_this::CACHE_PATH_NO_VSALT;
+ if($wildcard_regex) $cache_path_flags |= $_this::CACHE_PATH_ALLOW_WILDCARDS;
+
+ $cache_path_regex = preg_quote($_this->build_cache_path($feed_link, '', '', $cache_path_flags), '/');
+
+ if($wildcard_regex) // Replace wildcards with regex.
+ $cache_path_regex = preg_replace('/\\\\\*/', $wildcard_regex, $cache_path_regex);
+
+ return $cache_path_regex;
+ };
+ switch($type) // Handle purging based on the `$type`.
+ {
+ case 'blog': // The blog feed; i.e. `/feed/` on most WP installs.
+
+ $feed_cache_path_regexs[] = $build_cache_path_regex(get_feed_link($default_feed));
+ $feed_cache_path_regexs[] = $build_cache_path_regex(get_feed_link('rdf'));
+ $feed_cache_path_regexs[] = $build_cache_path_regex(get_feed_link('rss'));
+ $feed_cache_path_regexs[] = $build_cache_path_regex(get_feed_link('rss2'));
+ $feed_cache_path_regexs[] = $build_cache_path_regex(get_feed_link('atom'));
+
+ // It is not necessary to cover query string variations for these when `$seo_friendly_permalinks = TRUE`,
+ // because `redirect_canonical()` will force SEO-friendly links in the end anyway.
+
+ break; // Break switch handler.
+
+ case 'blog-comments': // The blog comments feed; i.e. `/comments/feed/` on most WP installs.
+
+ $feed_cache_path_regexs[] = $build_cache_path_regex(get_feed_link('comments_'.$default_feed));
+ $feed_cache_path_regexs[] = $build_cache_path_regex(get_feed_link('comments_rdf'));
+ $feed_cache_path_regexs[] = $build_cache_path_regex(get_feed_link('comments_rss'));
+ $feed_cache_path_regexs[] = $build_cache_path_regex(get_feed_link('comments_rss2'));
+ $feed_cache_path_regexs[] = $build_cache_path_regex(get_feed_link('comments_atom'));
+
+ // It is not necessary to cover query string variations for these when `$seo_friendly_permalinks = TRUE`,
+ // because `redirect_canonical()` will force SEO-friendly links in the end anyway.
+
+ break; // Break switch handler.
+
+ // @TODO Possibly consider search-related feeds in the future.
+ // See:
+ // e.g. case 'blog-searches':
+
+ case 'post-comments': // Feeds related to comments that a post has.
+
+ if(!$post_id) break; // Nothing to do here.
+ if(!($post = get_post($post_id))) break;
+
+ $feed_cache_path_regexs[] = $build_cache_path_regex(get_post_comments_feed_link($post->ID, $default_feed));
+ $feed_cache_path_regexs[] = $build_cache_path_regex(get_post_comments_feed_link($post->ID, 'rdf'));
+ $feed_cache_path_regexs[] = $build_cache_path_regex(get_post_comments_feed_link($post->ID, 'rss'));
+ $feed_cache_path_regexs[] = $build_cache_path_regex(get_post_comments_feed_link($post->ID, 'rss2'));
+ $feed_cache_path_regexs[] = $build_cache_path_regex(get_post_comments_feed_link($post->ID, 'atom'));
+
+ // It is not necessary to cover query string variations for these when `$seo_friendly_permalinks = TRUE`,
+ // because `redirect_canonical()` will force SEO-friendly links in the end anyway.
+
+ break; // Break switch handler.
+
+ case 'post-authors': // Feeds related to authors that a post has.
+
+ if(!$post_id) break; // nothing to do here.
+ if(!($post = get_post($post_id))) break;
+
+ $feed_cache_path_regexs[] = $build_cache_path_regex(get_author_feed_link($post->post_author, $default_feed));
+ $feed_cache_path_regexs[] = $build_cache_path_regex(get_author_feed_link($post->post_author, 'rdf'));
+ $feed_cache_path_regexs[] = $build_cache_path_regex(get_author_feed_link($post->post_author, 'rss'));
+ $feed_cache_path_regexs[] = $build_cache_path_regex(get_author_feed_link($post->post_author, 'rss2'));
+ $feed_cache_path_regexs[] = $build_cache_path_regex(get_author_feed_link($post->post_author, 'atom'));
+
+ if($seo_friendly_permalinks) // The above uses SEO-friendly permalinks?
+ // Here we cover query string variations that can be left behind after `redirect_canonical()` does its thing.
+ // In the case of author-related feeds, most of the URL is converted to SEO-friendly format.
+ // Everything except `?author=` which is what we deal with below.
+ {
+ $feed_cache_path_regexs[] = $build_cache_path_regex(add_query_arg(urlencode_deep(array('author' => $post->post_author)), $home_url.'feed/'.urlencode($default_feed).'/'));
+ $feed_cache_path_regexs[] = $build_cache_path_regex(add_query_arg(urlencode_deep(array('author' => $post->post_author)), $home_url.'feed/rdf/'));
+ $feed_cache_path_regexs[] = $build_cache_path_regex(add_query_arg(urlencode_deep(array('author' => $post->post_author)), $home_url.'feed/rss/'));
+ $feed_cache_path_regexs[] = $build_cache_path_regex(add_query_arg(urlencode_deep(array('author' => $post->post_author)), $home_url.'feed/rss2/'));
+ $feed_cache_path_regexs[] = $build_cache_path_regex(add_query_arg(urlencode_deep(array('author' => $post->post_author)), $home_url.'feed/atom/'));
+
+ if(($_post_author = get_userdata($post->post_author)) && !empty($_post_author->user_nicename)) // By author nicename.
+ {
+ $feed_cache_path_regexs[] = $build_cache_path_regex(add_query_arg(urlencode_deep(array('author' => $_post_author->user_nicename)), $home_url.'feed/'.urlencode($default_feed).'/'));
+ $feed_cache_path_regexs[] = $build_cache_path_regex(add_query_arg(urlencode_deep(array('author' => $_post_author->user_nicename)), $home_url.'feed/rdf/'));
+ $feed_cache_path_regexs[] = $build_cache_path_regex(add_query_arg(urlencode_deep(array('author' => $_post_author->user_nicename)), $home_url.'feed/rss/'));
+ $feed_cache_path_regexs[] = $build_cache_path_regex(add_query_arg(urlencode_deep(array('author' => $_post_author->user_nicename)), $home_url.'feed/rss2/'));
+ $feed_cache_path_regexs[] = $build_cache_path_regex(add_query_arg(urlencode_deep(array('author' => $_post_author->user_nicename)), $home_url.'feed/atom/'));
+ }
+ unset($_post_author); // Housekeeping.
+ }
+ break; // Break switch handler.
+
+ case 'post-terms': // Feeds related to terms that a post has.
+
+ if(!$post_id) break; // Nothing to do here.
+ if(!($post = get_post($post_id))) break;
+
+ $post_terms = array(); // Initialize array of all post terms.
+
+ if(!is_array($_post_taxonomies = get_object_taxonomies($post, 'objects')) || !$_post_taxonomies)
+ break; // Nothing to do here; post has no terms.
+
+ foreach($_post_taxonomies as $_post_taxonomy) // Collect terms for each taxonomy.
+ if(is_array($_post_taxonomy_terms = wp_get_post_terms($post->ID, $_post_taxonomy->name)) && $_post_taxonomy_terms)
+ $post_terms = array_merge($post_terms, $_post_taxonomy_terms);
+
+ $post_term_cache_path_variations = function ($post_term_feed_link, $post_term) use ($build_cache_path_regex)
+ {
+ $post_term_feed_link = (string)$post_term_feed_link; // Force string.
+ $variations = array(); // Initialize the array of variations.
+ if($post_term_feed_link) $variations[] = $build_cache_path_regex($post_term_feed_link);
+
+ /* NOTE: We CANNOT reliably include permalink variations here that use query string vars.
+ This is because Quick Cache hashes query string variables via MD5 checksums.
+ For this reason, we deal with SEO-friendly permalink variations only here. */
+
+ if($post_term_feed_link && strpos($post_term_feed_link, '?') === FALSE
+ && is_object($post_term) && !empty($post_term->term_id) && !empty($post_term->slug)
+ )// Create variations that deal with SEO-friendly permalink variations.
+ {
+ // Quick example: `(?:123|slug)`; to consider both.
+ $_term_id_or_slug = '(?:'.preg_quote($post_term->term_id, '/').
+ '|'.preg_quote(preg_replace('/[^a-z0-9\/.]/i', '-', $post_term->slug), '/').')';
+
+ // Quick example: `http://www.example.com/tax/term/feed`;
+ // with a wildcard this becomes: `http://www.example.com/tax/*/feed`
+ $_wildcarded = preg_replace('/\/[^\/]+\/feed([\/?#]|$)/', '/*/feed'.'${1}', $post_term_feed_link);
+
+ // Quick example: `http://www.example.com/tax/*/feed`;
+ // becomes: `www\.example\.com\/tax\/.*?(?=[\/\-]?(?:123|slug)[\/\-]).*?\/feed`
+ // ... this covers variations that use: `/tax/term,term/feed/`
+ // ... also covers variations that use: `/tax/term/tax/term/feed/`
+ $variations[] = $build_cache_path_regex($_wildcarded, '.*?(?=[\/\-]?'.$_term_id_or_slug.'[\/\-]).*?');
+ // NOTE: This may also pick up false-positives. Not much we can do about this.
+ // For instance, if another feed has the same word/slug in what is actually a longer/different term.
+ // Or, if another feed has the same word/slug in what is actually the name of a taxonomy.
+
+ unset($_term_id_or_slug, $_wildcarded); // Housekeeping.
+ }
+ return $variations; // Zero or more variations.
+ };
+ foreach($post_terms as $_post_term) // See:
+ {
+ $_post_term_feed_link = get_term_feed_link($_post_term->term_id, $_post_term->taxonomy, $default_feed);
+ $feed_cache_path_regexs = array_merge($feed_cache_path_regexs, $post_term_cache_path_variations($_post_term_feed_link, $_post_term));
+
+ $_post_term_feed_link = get_term_feed_link($_post_term->term_id, $_post_term->taxonomy, 'rdf');
+ $feed_cache_path_regexs = array_merge($feed_cache_path_regexs, $post_term_cache_path_variations($_post_term_feed_link, $_post_term));
+
+ $_post_term_feed_link = get_term_feed_link($_post_term->term_id, $_post_term->taxonomy, 'rss');
+ $feed_cache_path_regexs = array_merge($feed_cache_path_regexs, $post_term_cache_path_variations($_post_term_feed_link, $_post_term));
+
+ $_post_term_feed_link = get_term_feed_link($_post_term->term_id, $_post_term->taxonomy, 'rss2');
+ $feed_cache_path_regexs = array_merge($feed_cache_path_regexs, $post_term_cache_path_variations($_post_term_feed_link, $_post_term));
+
+ $_post_term_feed_link = get_term_feed_link($_post_term->term_id, $_post_term->taxonomy, 'atom');
+ $feed_cache_path_regexs = array_merge($feed_cache_path_regexs, $post_term_cache_path_variations($_post_term_feed_link, $_post_term));
+
+ if($seo_friendly_permalinks && ($_post_term_taxonomy = get_taxonomy($_post_term->taxonomy))/* The above uses SEO-friendly permalinks? */)
+ // Here we cover query string variations that can be left behind after `redirect_canonical()` does its thing.
+ // In the case of term-related feeds, most of the URL is converted to SEO-friendly format.
+ // Everything except `?[tax query var]=` which is what we deal with below.
+ {
+ if($_post_term_taxonomy->name === 'category')
+ $_post_term_taxonomy_query_var = 'cat'; // Special query var.
+ else $_post_term_taxonomy_query_var = $_post_term_taxonomy->query_var;
+
+ $feed_cache_path_regexs[] = $build_cache_path_regex(add_query_arg(urlencode_deep(array($_post_term_taxonomy_query_var => $_post_term->term_id)), $home_url.'feed/'.urlencode($default_feed).'/'));
+ $feed_cache_path_regexs[] = $build_cache_path_regex(add_query_arg(urlencode_deep(array($_post_term_taxonomy_query_var => $_post_term->term_id)), $home_url.'feed/rdf/'));
+ $feed_cache_path_regexs[] = $build_cache_path_regex(add_query_arg(urlencode_deep(array($_post_term_taxonomy_query_var => $_post_term->term_id)), $home_url.'feed/rss/'));
+ $feed_cache_path_regexs[] = $build_cache_path_regex(add_query_arg(urlencode_deep(array($_post_term_taxonomy_query_var => $_post_term->term_id)), $home_url.'feed/rss2/'));
+ $feed_cache_path_regexs[] = $build_cache_path_regex(add_query_arg(urlencode_deep(array($_post_term_taxonomy_query_var => $_post_term->term_id)), $home_url.'feed/atom/'));
+
+ $feed_cache_path_regexs[] = $build_cache_path_regex(add_query_arg(urlencode_deep(array($_post_term_taxonomy_query_var => $_post_term->slug)), $home_url.'feed/'.urlencode($default_feed).'/'));
+ $feed_cache_path_regexs[] = $build_cache_path_regex(add_query_arg(urlencode_deep(array($_post_term_taxonomy_query_var => $_post_term->slug)), $home_url.'feed/rdf/'));
+ $feed_cache_path_regexs[] = $build_cache_path_regex(add_query_arg(urlencode_deep(array($_post_term_taxonomy_query_var => $_post_term->slug)), $home_url.'feed/rss/'));
+ $feed_cache_path_regexs[] = $build_cache_path_regex(add_query_arg(urlencode_deep(array($_post_term_taxonomy_query_var => $_post_term->slug)), $home_url.'feed/rss2/'));
+ $feed_cache_path_regexs[] = $build_cache_path_regex(add_query_arg(urlencode_deep(array($_post_term_taxonomy_query_var => $_post_term->slug)), $home_url.'feed/atom/'));
+ }
+ unset($_post_term_taxonomy, $_post_term_taxonomy_query_var); // Housekeeping.
+ }
+ unset($_post_taxonomies, $_post_taxonomy, $_post_taxonomy_terms, $_post_term, $_post_term_feed_link);
+
+ break; // Break switch handler.
+ }
+ foreach($feed_cache_path_regexs as $_key => $_feed_cache_path_regex)
+ if(!is_string($_feed_cache_path_regex) || !$_feed_cache_path_regex) unset($feed_cache_path_regexs[$_key]);
+ unset($_key, $_feed_cache_path_regex); // Housekeeping.
+
+ if(!$feed_cache_path_regexs || !($feed_cache_path_regexs = array_unique($feed_cache_path_regexs)))
+ return $counter; // Nothing to do here.
+
+ $in_sets_of = apply_filters(__METHOD__.'__in_sets_of', 10, get_defined_vars());
+
+ for($_i = 0; $_i < count($feed_cache_path_regexs); $_i = $_i + $in_sets_of)
+ // This prevents the regex from hitting a backtrack limit in some environments.
+ {
+ $_feed_cache_path_regexs = array_slice($feed_cache_path_regexs, $_i, $in_sets_of);
+ $_regex = '/^'.preg_quote($cache_dir, '/').'\/[^\/]+\/(?:'.implode('|', $_feed_cache_path_regexs).')\./';
+ $counter += $this->delete_files_from_host_cache_dir($_regex);
+ }
+ unset($_i, $_feed_cache_path_regexs, $_regex); // Housekeeping.
+
+ if($counter && $this->options['change_notifications_enable'] && is_admin())
+ $this->enqueue_notice(' '.
+ sprintf(__('Quick Cache: detected changes. Found XML feeds of type %1$s
(auto-purging).', $this->text_domain), esc_html($type)));
+
+ return apply_filters(__METHOD__, $counter, get_defined_vars());
+ }
+
/**
* Automatically purges cache files related to XML sitemaps.
*
@@ -1636,6 +1900,8 @@ public function auto_purge_home_page_cache()
}
unset($_file); // Just a little housekeeping.
+ $counter += $this->auto_purge_xml_feeds_cache('blog');
+
return apply_filters(__METHOD__, $counter, get_defined_vars());
}
@@ -1709,6 +1975,8 @@ public function auto_purge_posts_page_cache()
}
unset($_file); // Just a little housekeeping.
+ $counter += $this->auto_purge_xml_feeds_cache('blog');
+
return apply_filters(__METHOD__, $counter, get_defined_vars());
}
@@ -1813,6 +2081,9 @@ public function auto_purge_author_page_cache($post_ID, \WP_Post $post_after, \WP
}
unset($_file, $_author); // Just a little housekeeping.
+ $counter += $this->auto_purge_xml_feeds_cache('blog');
+ $counter += $this->auto_purge_xml_feeds_cache('post-authors', $post_ID);
+
return apply_filters(__METHOD__, $counter, get_defined_vars());
}
@@ -1963,6 +2234,8 @@ public function auto_purge_post_terms_cache($id, $force = FALSE)
}
unset($_term, $_file); // Just a little housekeeping.
+ $counter += $this->auto_purge_xml_feeds_cache('post-terms', $id);
+
return apply_filters(__METHOD__, $counter, get_defined_vars());
}
@@ -2001,12 +2274,15 @@ public function auto_purge_comment_post_cache($id)
return $counter; // Nothing we can do.
if($comment->comment_approved === 'spam' || $comment->comment_approved === '0')
+ // Don't allow next `auto_purge_post_cache()` call to clear post cache.
+ // Also, don't allow spam to clear cache.
{
- static::$static['___allow_auto_purge_post_cache'] = FALSE; // Don't allow next `auto_purge_post_cache()` call to clear post cache.
- return $counter; // Don't allow spam to clear cache.
+ static::$static['___allow_auto_purge_post_cache'] = FALSE;
+ return $counter; // Nothing to do here.
}
-
- $counter = $this->auto_purge_post_cache($comment->comment_post_ID);
+ $counter += $this->auto_purge_xml_feeds_cache('blog-comments');
+ $counter += $this->auto_purge_xml_feeds_cache('post-comments', $comment->comment_post_ID);
+ $counter += $this->auto_purge_post_cache($comment->comment_post_ID);
return apply_filters(__METHOD__, $counter, get_defined_vars());
}
@@ -2044,15 +2320,15 @@ public function auto_purge_comment_transition($new_status, $old_status, $comment
if(empty($comment->comment_post_ID))
return $counter; // Nothing we can do.
- if($old_status === 'approved' || ($old_status === 'unapproved' && $new_status === 'approved'))
- {
- $counter = $this->auto_purge_post_cache($comment->comment_post_ID);
- }
- else
+ if(!($old_status === 'approved' || ($old_status === 'unapproved' && $new_status === 'approved')))
+ // If excluded here, don't allow next `auto_purge_post_cache()` call to clear post cache.
{
- static::$static['___allow_auto_purge_post_cache'] = FALSE; // Don't allow next `auto_purge_post_cache()` call to clear post cache.
- return $counter; // Don't allow Unapproved comments not being Approved to clear cache.
+ static::$static['___allow_auto_purge_post_cache'] = FALSE;
+ return $counter; // Nothing to do here.
}
+ $counter += $this->auto_purge_xml_feeds_cache('blog-comments');
+ $counter += $this->auto_purge_xml_feeds_cache('post-comments', $comment->comment_post_ID);
+ $counter += $this->auto_purge_post_cache($comment->comment_post_ID);
return apply_filters(__METHOD__, $counter, get_defined_vars());
}