From 40768e4ebe5f74a2aceceeb029c5179e2f444ac2 Mon Sep 17 00:00:00 2001 From: Jonny Harris Date: Fri, 22 Jul 2022 20:50:31 +0000 Subject: [PATCH] Cache API: Add `wp_cache_flush_group` function. Add a new plugable function called `wp_cache_flush_group`, that will allow developers to clear whole cache groups with a single call. Developers can detect if their current implementation of an object cache supports flushing by group, by calling `wp_cache_supports_group_flush` which returns true if it is supported. If the developers of the object cache drop-in has not implemented `wp_cache_flush_group` and `wp_cache_supports_group_flush`, these functions are polyfilled and `wp_cache_supports_group_flush` defaults to false. Props Spacedmonkey, filosofo, ryan, sc0ttkclark, SergeyBiryukov, scribu, Ste_95, dd32, dhilditch, dougal, lucasbustamante, dg12345, tillkruess, peterwilsoncc, flixos90, pbearne. Fixes #4476. git-svn-id: https://develop.svn.wordpress.org/trunk@53763 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/cache-compat.php | 46 +++++++++++++++++++++++ src/wp-includes/cache.php | 32 ++++++++++++++++ src/wp-includes/class-wp-object-cache.php | 14 +++++++ tests/phpunit/includes/object-cache.php | 11 ++++++ tests/phpunit/tests/cache.php | 32 ++++++++++++++++ tests/phpunit/tests/pluggable.php | 2 + 6 files changed, 137 insertions(+) diff --git a/src/wp-includes/cache-compat.php b/src/wp-includes/cache-compat.php index 1088f1c74b448..c225d915158f9 100644 --- a/src/wp-includes/cache-compat.php +++ b/src/wp-includes/cache-compat.php @@ -141,3 +141,49 @@ function wp_cache_flush_runtime() { return wp_using_ext_object_cache() ? false : wp_cache_flush(); } endif; + +if ( ! function_exists( 'wp_cache_flush_group' ) ) : + /** + * Removes all cache items in a group, if the object cache implementation supports it. + * Before calling this method, always check for group flushing support using the + * `wp_cache_supports_group_flush()` method. + * + * @since 6.1.0 + * + * @see WP_Object_Cache::flush_group() + * @global WP_Object_Cache $wp_object_cache Object cache global instance. + * + * @param string $group Name of group to remove from cache. + * @return bool True if group was flushed, false otherwise. + */ + function wp_cache_flush_group( $group ) { + global $wp_object_cache; + + if ( ! wp_cache_supports_group_flush() ) { + _doing_it_wrong( + __FUNCTION__, + __( 'Your object cache implementation does not support flushing individual groups.' ), + '6.1.0' + ); + + return false; + } + + return $wp_object_cache->flush_group( $group ); + } +endif; + +if ( ! function_exists( 'wp_cache_supports_group_flush' ) ) : + /** + * Whether the object cache implementation supports flushing individual cache groups. + * + * @since 6.1.0 + * + * @see WP_Object_Cache::flush_group() + * + * @return bool True if group flushing is supported, false otherwise. + */ + function wp_cache_supports_group_flush() { + return false; + } +endif; diff --git a/src/wp-includes/cache.php b/src/wp-includes/cache.php index 1999dd00ff2dc..9d68b1bb047fd 100644 --- a/src/wp-includes/cache.php +++ b/src/wp-includes/cache.php @@ -22,6 +22,19 @@ function wp_cache_init() { $GLOBALS['wp_object_cache'] = new WP_Object_Cache(); } +/** + * Whether the object cache implementation supports flushing individual cache groups. + * + * @since 6.1.0 + * + * @see WP_Object_Cache::flush_group() + * + * @return bool True if group flushing is supported, false otherwise. + */ +function wp_cache_supports_group_flush() { + return true; +} + /** * Adds data to the cache, if the cache key doesn't already exist. * @@ -281,6 +294,25 @@ function wp_cache_flush_runtime() { return wp_cache_flush(); } +/** + * Removes all cache items in a group, if the object cache implementation supports it. + * Before calling this method, always check for group flushing support using the + * `wp_cache_supports_group_flush()` method. + * + * @since 6.1.0 + * + * @see WP_Object_Cache::flush_group() + * @global WP_Object_Cache $wp_object_cache Object cache global instance. + * + * @param string $group Name of group to remove from cache. + * @return bool True if group was flushed, false otherwise. + */ +function wp_cache_flush_group( $group ) { + global $wp_object_cache; + + return $wp_object_cache->flush_group( $group ); +} + /** * Closes the cache. * diff --git a/src/wp-includes/class-wp-object-cache.php b/src/wp-includes/class-wp-object-cache.php index 89fd6ff011b50..e145bebcf38ad 100644 --- a/src/wp-includes/class-wp-object-cache.php +++ b/src/wp-includes/class-wp-object-cache.php @@ -290,6 +290,20 @@ public function set_multiple( array $data, $group = '', $expire = 0 ) { return $values; } + /** + * Removes all cache items in a group. + * + * @since 6.1.0 + * + * @param string $group Name of group to remove from cache. + * @return true Always returns true. + */ + public function flush_group( $group ) { + unset( $this->cache[ $group ] ); + + return true; + } + /** * Retrieves the cache contents, if it exists. * diff --git a/tests/phpunit/includes/object-cache.php b/tests/phpunit/includes/object-cache.php index 377dca3db9ae3..6eb2712f29cff 100644 --- a/tests/phpunit/includes/object-cache.php +++ b/tests/phpunit/includes/object-cache.php @@ -745,6 +745,17 @@ function wp_cache_init() { $wp_object_cache = new WP_Object_Cache(); } +/** + * Whether the object cache implementation supports flushing individual cache groups. + * + * @since 6.1.0 + * + * @return bool True if group flushing is supported, false otherwise. + */ +function wp_cache_supports_group_flush() { + return false; +} + /** * Adds a group or set of groups to the list of non-persistent groups. * diff --git a/tests/phpunit/tests/cache.php b/tests/phpunit/tests/cache.php index 5e706c4f6cf1d..a873fd2e65fae 100644 --- a/tests/phpunit/tests/cache.php +++ b/tests/phpunit/tests/cache.php @@ -415,4 +415,36 @@ public function test_wp_cache_delete_multiple() { $this->assertSame( $expected, $found ); } + + /** + * @ticket 4476 + * @ticket 9773 + * + * test wp_cache_flush_group + * + * @covers ::wp_cache_flush_group + */ + public function test_wp_cache_flush_group() { + $key = 'my-key'; + $val = 'my-val'; + + wp_cache_set( $key, $val, 'group-test' ); + wp_cache_set( $key, $val, 'group-kept' ); + + $this->assertSame( $val, wp_cache_get( $key, 'group-test' ), 'test_wp_cache_flush_group: group-test should contain my-val' ); + + if ( wp_using_ext_object_cache() ) { + $this->setExpectedIncorrectUsage( 'wp_cache_flush_group' ); + } + + $results = wp_cache_flush_group( 'group-test' ); + + if ( wp_using_ext_object_cache() ) { + $this->assertFalse( $results ); + } else { + $this->assertTrue( $results ); + $this->assertFalse( wp_cache_get( $key, 'group-test' ), 'test_wp_cache_flush_group: group-test should return false' ); + $this->assertSame( $val, wp_cache_get( $key, 'group-kept' ), 'test_wp_cache_flush_group: group-kept should still contain my-val' ); + } + } } diff --git a/tests/phpunit/tests/pluggable.php b/tests/phpunit/tests/pluggable.php index 7bf32f66f5e77..e9e7c14934dc4 100644 --- a/tests/phpunit/tests/pluggable.php +++ b/tests/phpunit/tests/pluggable.php @@ -268,6 +268,7 @@ public function get_pluggable_function_signatures() { // wp-includes/cache.php: 'wp_cache_init' => array(), + 'wp_cache_supports_group_flush' => array(), 'wp_cache_add' => array( 'key', 'data', @@ -327,6 +328,7 @@ public function get_pluggable_function_signatures() { ), 'wp_cache_flush' => array(), 'wp_cache_flush_runtime' => array(), + 'wp_cache_flush_group' => array( 'group' ), 'wp_cache_close' => array(), 'wp_cache_add_global_groups' => array( 'groups' ), 'wp_cache_add_non_persistent_groups' => array( 'groups' ),