feat: rejected-cookie cache exclusions for WooCommerce cart
Add wp_recache_rejected_cookies option (defaults: woocommerce_items_in_cart, wp_woocommerce_session_*, wp-postpass, comment_author_*, wordpress_*) that bypasses page cache when any listed cookie is present. Both the WP-level check (wp_recache_should_exclude) and the standalone drop-in apply the same exact/prefix matching logic. Editing the option regenerates the drop-in and purges all cache.
This commit is contained in:
+39
-4
@@ -48,6 +48,13 @@ class WP_Recache_Activator {
|
|||||||
'cache_enabled' => true,
|
'cache_enabled' => true,
|
||||||
'separate_mobile_cache' => false,
|
'separate_mobile_cache' => false,
|
||||||
'exclude_urls' => ['/cart', '/checkout', '/my-account'],
|
'exclude_urls' => ['/cart', '/checkout', '/my-account'],
|
||||||
|
'rejected_cookies' => [
|
||||||
|
'woocommerce_items_in_cart',
|
||||||
|
'wp_woocommerce_session_*',
|
||||||
|
'wp-postpass',
|
||||||
|
'comment_author_*',
|
||||||
|
'wordpress_*',
|
||||||
|
],
|
||||||
'minify_css' => false,
|
'minify_css' => false,
|
||||||
'minify_js' => false,
|
'minify_js' => false,
|
||||||
'lazyload_images' => false,
|
'lazyload_images' => false,
|
||||||
@@ -64,8 +71,20 @@ class WP_Recache_Activator {
|
|||||||
/**
|
/**
|
||||||
* Write the standalone cache server required by the advanced-cache.php
|
* Write the standalone cache server required by the advanced-cache.php
|
||||||
* drop-in. Must run without WordPress loaded.
|
* drop-in. Must run without WordPress loaded.
|
||||||
|
*
|
||||||
|
* Public so Admin can regenerate the drop-in when options change.
|
||||||
*/
|
*/
|
||||||
private static function create_cache_server() {
|
public static function create_cache_server() {
|
||||||
|
$rejected_cookies = get_option('wp_recache_rejected_cookies', [
|
||||||
|
'woocommerce_items_in_cart',
|
||||||
|
'wp_woocommerce_session_*',
|
||||||
|
'wp-postpass',
|
||||||
|
'comment_author_*',
|
||||||
|
'wordpress_*',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$cookies_php = var_export($rejected_cookies, true);
|
||||||
|
|
||||||
$content = '<?php
|
$content = '<?php
|
||||||
/**
|
/**
|
||||||
* WP Recache early cache serving.
|
* WP Recache early cache serving.
|
||||||
@@ -82,9 +101,25 @@ if (!empty($_SERVER[\'QUERY_STRING\'])) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($_COOKIE as $name => $value) {
|
$rejected_cookies = ' . $cookies_php . ';
|
||||||
if (strpos($name, \'wordpress_logged_in\') === 0) {
|
|
||||||
return;
|
foreach ($rejected_cookies as $cookie) {
|
||||||
|
$cookie = trim($cookie);
|
||||||
|
if ($cookie === \'\') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (substr($cookie, -1) === \'*\') {
|
||||||
|
$prefix = substr($cookie, 0, -1);
|
||||||
|
foreach ($_COOKIE as $name => $value) {
|
||||||
|
if (strpos($name, $prefix) === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (isset($_COOKIE[$cookie])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,10 @@ class WP_Recache_Admin {
|
|||||||
add_action('admin_enqueue_scripts', 'wp_recache_enqueue_admin_bar_script');
|
add_action('admin_enqueue_scripts', 'wp_recache_enqueue_admin_bar_script');
|
||||||
// New exclusions must not leave stale files that the drop-in would still serve.
|
// New exclusions must not leave stale files that the drop-in would still serve.
|
||||||
add_action('update_option_wp_recache_exclude_urls', 'wp_recache_delete_all_cache');
|
add_action('update_option_wp_recache_exclude_urls', 'wp_recache_delete_all_cache');
|
||||||
|
add_action('update_option_wp_recache_rejected_cookies', function () {
|
||||||
|
WP_Recache_Activator::create_cache_server();
|
||||||
|
wp_recache_delete_all_cache();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -59,6 +63,18 @@ class WP_Recache_Admin {
|
|||||||
'default' => ['/cart', '/checkout', '/my-account'],
|
'default' => ['/cart', '/checkout', '/my-account'],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
register_setting('wp_recache_settings', 'wp_recache_rejected_cookies', [
|
||||||
|
'type' => 'array',
|
||||||
|
'sanitize_callback' => [$this, 'sanitize_rejected_cookies'],
|
||||||
|
'default' => [
|
||||||
|
'woocommerce_items_in_cart',
|
||||||
|
'wp_woocommerce_session_*',
|
||||||
|
'wp-postpass',
|
||||||
|
'comment_author_*',
|
||||||
|
'wordpress_*',
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
register_setting('wp_recache_settings', 'wp_recache_minify_css', [
|
register_setting('wp_recache_settings', 'wp_recache_minify_css', [
|
||||||
'type' => 'boolean',
|
'type' => 'boolean',
|
||||||
'sanitize_callback' => 'rest_sanitize_boolean',
|
'sanitize_callback' => 'rest_sanitize_boolean',
|
||||||
@@ -100,6 +116,22 @@ class WP_Recache_Admin {
|
|||||||
return array_map('sanitize_text_field', $lines);
|
return array_map('sanitize_text_field', $lines);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sanitize rejected cookies.
|
||||||
|
*
|
||||||
|
* @param mixed $input Input value.
|
||||||
|
* @return array Sanitized cookie names.
|
||||||
|
*/
|
||||||
|
public function sanitize_rejected_cookies($input) {
|
||||||
|
if (is_array($input)) {
|
||||||
|
$input = implode("\n", $input);
|
||||||
|
}
|
||||||
|
|
||||||
|
$lines = array_filter(array_map('trim', explode("\n", (string) $input)));
|
||||||
|
|
||||||
|
return array_map('sanitize_text_field', $lines);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render settings page.
|
* Render settings page.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -52,6 +52,19 @@ $stats = WP_Recache_Admin::get_cache_stats();
|
|||||||
<p class="description">One URL per line. These pages will not be cached.</p>
|
<p class="description">One URL per line. These pages will not be cached.</p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">Rejected Cookies</th>
|
||||||
|
<td>
|
||||||
|
<textarea name="wp_recache_rejected_cookies" rows="5" cols="50" class="large-text"><?php echo esc_textarea(implode("\n", get_option('wp_recache_rejected_cookies', [
|
||||||
|
'woocommerce_items_in_cart',
|
||||||
|
'wp_woocommerce_session_*',
|
||||||
|
'wp-postpass',
|
||||||
|
'comment_author_*',
|
||||||
|
'wordpress_*',
|
||||||
|
]))); ?></textarea>
|
||||||
|
<p class="description">One cookie name per line. Visitors with these cookies bypass the cache. Use <code>*</code> as a suffix to match a prefix (e.g. <code>comment_author_*</code>).</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<h2>Cache Statistics</h2>
|
<h2>Cache Statistics</h2>
|
||||||
|
|||||||
@@ -58,6 +58,10 @@ function wp_recache_should_exclude() {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (wp_recache_has_rejected_cookie()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Dynamic query-string requests are never cached (story 11).
|
// Dynamic query-string requests are never cached (story 11).
|
||||||
if (!empty($_SERVER['QUERY_STRING'])) {
|
if (!empty($_SERVER['QUERY_STRING'])) {
|
||||||
return true;
|
return true;
|
||||||
@@ -83,6 +87,47 @@ function wp_recache_should_exclude() {
|
|||||||
return apply_filters('wp_recache_exclude', false);
|
return apply_filters('wp_recache_exclude', false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a rejected cookie is present in the request.
|
||||||
|
*
|
||||||
|
* @return bool True if any rejected cookie is set.
|
||||||
|
*/
|
||||||
|
function wp_recache_has_rejected_cookie() {
|
||||||
|
if (empty($_COOKIE)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$rejected = wp_recache_get_option('rejected_cookies', [
|
||||||
|
'woocommerce_items_in_cart',
|
||||||
|
'wp_woocommerce_session_*',
|
||||||
|
'wp-postpass',
|
||||||
|
'comment_author_*',
|
||||||
|
'wordpress_*',
|
||||||
|
]);
|
||||||
|
|
||||||
|
foreach ($rejected as $cookie) {
|
||||||
|
$cookie = trim($cookie);
|
||||||
|
if ($cookie === '') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (substr($cookie, -1) === '*') {
|
||||||
|
$prefix = substr($cookie, 0, -1);
|
||||||
|
foreach ($_COOKIE as $name => $value) {
|
||||||
|
if (strpos($name, $prefix) === 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (isset($_COOKIE[$cookie])) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if request is for a robot/crawler.
|
* Check if request is for a robot/crawler.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -52,4 +52,38 @@ class WP_Recache_Admin_Test extends WP_UnitTestCase {
|
|||||||
|
|
||||||
$this->assertGreaterThanOrEqual(2, $stats['files']);
|
$this->assertGreaterThanOrEqual(2, $stats['files']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function test_sanitize_rejected_cookies_trims_whitespace() {
|
||||||
|
$admin = new WP_Recache_Admin();
|
||||||
|
$result = $admin->sanitize_rejected_cookies(" wp-postpass \n comment_author_* ");
|
||||||
|
$this->assertEquals(['wp-postpass', 'comment_author_*'], $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_sanitize_rejected_cookies_filters_empty_lines() {
|
||||||
|
$admin = new WP_Recache_Admin();
|
||||||
|
$result = $admin->sanitize_rejected_cookies("wp-postpass\n\n\ncomment_author_*\n");
|
||||||
|
$this->assertEquals(['wp-postpass', 'comment_author_*'], $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_sanitize_rejected_cookies_handles_array_input() {
|
||||||
|
$admin = new WP_Recache_Admin();
|
||||||
|
$result = $admin->sanitize_rejected_cookies(['wp-postpass', 'comment_author_*']);
|
||||||
|
$this->assertEquals(['wp-postpass', 'comment_author_*'], $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_rejected_cookies_option_is_registered() {
|
||||||
|
$admin = new WP_Recache_Admin();
|
||||||
|
$admin->init();
|
||||||
|
do_action('admin_init');
|
||||||
|
|
||||||
|
$default = [
|
||||||
|
'woocommerce_items_in_cart',
|
||||||
|
'wp_woocommerce_session_*',
|
||||||
|
'wp-postpass',
|
||||||
|
'comment_author_*',
|
||||||
|
'wordpress_*',
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->assertEquals($default, get_option('wp_recache_rejected_cookies', $default));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,6 +88,48 @@ class WP_Recache_Helpers_Test extends WP_UnitTestCase {
|
|||||||
$this->assertTrue(wp_recache_should_exclude());
|
$this->assertTrue(wp_recache_should_exclude());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function test_wp_recache_has_rejected_cookie_returns_true_for_exact_match() {
|
||||||
|
wp_set_current_user(0);
|
||||||
|
update_option('wp_recache_rejected_cookies', ['wp-postpass']);
|
||||||
|
$_COOKIE = ['wp-postpass' => 'abc123'];
|
||||||
|
$this->assertTrue(wp_recache_has_rejected_cookie());
|
||||||
|
$_COOKIE = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_wp_recache_has_rejected_cookie_returns_true_for_prefix_match() {
|
||||||
|
wp_set_current_user(0);
|
||||||
|
update_option('wp_recache_rejected_cookies', ['comment_author_*']);
|
||||||
|
$_COOKIE = ['comment_author_12345' => 'test'];
|
||||||
|
$this->assertTrue(wp_recache_has_rejected_cookie());
|
||||||
|
$_COOKIE = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_wp_recache_has_rejected_cookie_returns_false_when_no_match() {
|
||||||
|
wp_set_current_user(0);
|
||||||
|
update_option('wp_recache_rejected_cookies', ['wp-postpass']);
|
||||||
|
$_COOKIE = ['unrelated_cookie' => 'value'];
|
||||||
|
$this->assertFalse(wp_recache_has_rejected_cookie());
|
||||||
|
$_COOKIE = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_wp_recache_has_rejected_cookie_returns_false_when_empty_cookies() {
|
||||||
|
wp_set_current_user(0);
|
||||||
|
update_option('wp_recache_rejected_cookies', ['wp-postpass']);
|
||||||
|
$_COOKIE = [];
|
||||||
|
$this->assertFalse(wp_recache_has_rejected_cookie());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_wp_recache_should_exclude_returns_true_for_rejected_cookie() {
|
||||||
|
wp_set_current_user(0);
|
||||||
|
update_option('wp_recache_rejected_cookies', ['woocommerce_items_in_cart']);
|
||||||
|
$_COOKIE = ['woocommerce_items_in_cart' => 'yes'];
|
||||||
|
$_SERVER['REQUEST_METHOD'] = 'GET';
|
||||||
|
$_SERVER['REQUEST_URI'] = '/test-page/';
|
||||||
|
unset($_SERVER['HTTP_USER_AGENT']);
|
||||||
|
$this->assertTrue(wp_recache_should_exclude());
|
||||||
|
$_COOKIE = [];
|
||||||
|
}
|
||||||
|
|
||||||
public function test_wp_recache_delete_cache_removes_file() {
|
public function test_wp_recache_delete_cache_removes_file() {
|
||||||
$url = 'https://example.com/test-page/';
|
$url = 'https://example.com/test-page/';
|
||||||
$path = wp_recache_get_cache_path($url);
|
$path = wp_recache_get_cache_path($url);
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ $options = [
|
|||||||
'wp_recache_cache_enabled',
|
'wp_recache_cache_enabled',
|
||||||
'wp_recache_separate_mobile_cache',
|
'wp_recache_separate_mobile_cache',
|
||||||
'wp_recache_exclude_urls',
|
'wp_recache_exclude_urls',
|
||||||
|
'wp_recache_rejected_cookies',
|
||||||
'wp_recache_minify_css',
|
'wp_recache_minify_css',
|
||||||
'wp_recache_minify_js',
|
'wp_recache_minify_js',
|
||||||
'wp_recache_lazyload_images',
|
'wp_recache_lazyload_images',
|
||||||
|
|||||||
Reference in New Issue
Block a user