diff --git a/composer.json b/composer.json index aba06a05..e48a0f05 100644 --- a/composer.json +++ b/composer.json @@ -39,7 +39,12 @@ "cache decr", "cache delete", "cache flush", + "cache flush-comment", "cache flush-group", + "cache flush-option", + "cache flush-post", + "cache flush-term", + "cache flush-user", "cache get", "cache incr", "cache patch", diff --git a/features/cache-flush-granular.feature b/features/cache-flush-granular.feature new file mode 100644 index 00000000..f0123cef --- /dev/null +++ b/features/cache-flush-granular.feature @@ -0,0 +1,98 @@ +Feature: Granular cache flushing operations + + @skip-object-cache + Scenario: Flush specific post cache + Given a WP install + And a wp-content/mu-plugins/test-harness.php file: + """ + 123, 'post_title' => 'Test' ), 'posts' ); + wp_cache_set( 'meta_123', array( 'key' => 'value' ), 'post_meta' ); + }; + WP_CLI::add_hook( 'before_invoke:cache flush-post', $cache_post ); + """ + + When I run `wp cache flush-post 123` + Then STDOUT should contain: + """ + Success: Post cache for ID 123 cleared. + """ + + @skip-object-cache + Scenario: Flush specific term cache + Given a WP install + And a wp-content/mu-plugins/test-harness.php file: + """ + 5 ), 'terms' ); + wp_cache_set( 'term_meta_5', array(), 'term_meta' ); + }; + WP_CLI::add_hook( 'before_invoke:cache flush-term', $cache_term ); + """ + + When I run `wp cache flush-term 5` + Then STDOUT should contain: + """ + Success: Term cache for ID 5 cleared. + """ + + @skip-object-cache + Scenario: Flush specific comment cache + Given a WP install + And a wp-content/mu-plugins/test-harness.php file: + """ + 1 ), 'users' ); + wp_cache_set( 'user_meta_1', array(), 'user_meta' ); + }; + WP_CLI::add_hook( 'before_invoke:cache flush-user', $cache_user ); + """ + + When I run `wp cache flush-user 1` + Then STDOUT should contain: + """ + Success: User cache for ID 1 cleared. + """ + + @skip-object-cache + Scenario: Flush specific option cache + Given a WP install + And a wp-content/mu-plugins/test-harness.php file: + """ + 'value' ), 'options' ); + }; + WP_CLI::add_hook( 'before_invoke:cache flush-option', $cache_option ); + """ + + When I run `wp cache flush-option my_option` + Then STDOUT should contain: + """ + Success: Option cache for 'my_option' cleared. + """ + + diff --git a/src/Cache_Command.php b/src/Cache_Command.php index ab5c8b56..a62a440e 100644 --- a/src/Cache_Command.php +++ b/src/Cache_Command.php @@ -611,4 +611,194 @@ function ( $key ) { } } } + + /** + * Clears post related caches. + * + * @subcommand flush-post + * + * ## OPTIONS + * + * [] + * : Post ID. If not specified, clears all post caches. + * + * ## EXAMPLES + * + * # Clear all post caches. + * $ wp cache flush-post + * Success: Post caches cleared. + * + * # Clear cache for a specific post. + * $ wp cache flush-post 123 + * Success: Post cache for ID 123 cleared. + * + * @param array $args Positional arguments. + */ + public function flush_post( $args ) { + $post_id = ! empty( $args ) ? (int) $args[0] : null; + + if ( $post_id ) { + clean_post_cache( $post_id ); + WP_CLI::success( "Post cache for ID $post_id cleared." ); + } else { + if ( ! wp_using_ext_object_cache() || ! function_exists( 'wp_cache_supports' ) || ! wp_cache_supports( 'flush_group' ) ) { + WP_CLI::error( 'Flushing all post caches requires WordPress 6.1+' ); + } + wp_cache_flush_group( 'posts' ); + wp_cache_flush_group( 'post_meta' ); + WP_CLI::success( 'Post caches cleared.' ); + } + } + + /** + * Clears term related caches. + * + * @subcommand flush-term + * + * ## OPTIONS + * + * [] + * : Term ID. If not specified, clears all term caches. + * + * ## EXAMPLES + * + * # Clear all term caches. + * $ wp cache flush-term + * Success: Term caches cleared. + * + * # Clear cache for a specific term. + * $ wp cache flush-term 5 + * Success: Term cache for ID 5 cleared. + * + * @param array $args Positional arguments. + */ + public function flush_term( $args ) { + $term_id = ! empty( $args ) ? (int) $args[0] : null; + + if ( $term_id ) { + clean_term_cache( $term_id ); + WP_CLI::success( "Term cache for ID $term_id cleared." ); + } else { + if ( ! wp_using_ext_object_cache() || ! function_exists( 'wp_cache_supports' ) || ! wp_cache_supports( 'flush_group' ) ) { + WP_CLI::error( 'Flushing all term caches requires WordPress 6.1+' ); + } + wp_cache_flush_group( 'terms' ); + wp_cache_flush_group( 'term_meta' ); + WP_CLI::success( 'Term caches cleared.' ); + } + } + + /** + * Clears comment related caches. + * + * @subcommand flush-comment + * + * ## OPTIONS + * + * [] + * : Comment ID. If not specified, clears all comment caches. + * + * ## EXAMPLES + * + * # Clear all comment caches. + * $ wp cache flush-comment + * Success: Comment caches cleared. + * + * # Clear cache for a specific comment. + * $ wp cache flush-comment 42 + * Success: Comment cache for ID 42 cleared. + * + * @param array $args Positional arguments. + */ + public function flush_comment( $args ) { + $comment_id = ! empty( $args ) ? (int) $args[0] : null; + + if ( $comment_id ) { + clean_comment_cache( $comment_id ); + WP_CLI::success( "Comment cache for ID $comment_id cleared." ); + } else { + if ( ! wp_using_ext_object_cache() || ! function_exists( 'wp_cache_supports' ) || ! wp_cache_supports( 'flush_group' ) ) { + WP_CLI::error( 'Flushing all comment caches requires WordPress 6.1+' ); + } + wp_cache_flush_group( 'comment' ); + wp_cache_flush_group( 'comment_meta' ); + WP_CLI::success( 'Comment caches cleared.' ); + } + } + + /** + * Clears user related caches. + * + * @subcommand flush-user + * + * ## OPTIONS + * + * [] + * : User ID. If not specified, clears all user caches. + * + * ## EXAMPLES + * + * # Clear all user caches. + * $ wp cache flush-user + * Success: User caches cleared. + * + * # Clear cache for a specific user. + * $ wp cache flush-user 1 + * Success: User cache for ID 1 cleared. + * + * @param array $args Positional arguments. + */ + public function flush_user( $args ) { + $user_id = ! empty( $args ) ? (int) $args[0] : null; + + if ( $user_id ) { + clean_user_cache( $user_id ); + WP_CLI::success( "User cache for ID $user_id cleared." ); + } else { + if ( ! wp_using_ext_object_cache() || ! function_exists( 'wp_cache_supports' ) || ! wp_cache_supports( 'flush_group' ) ) { + WP_CLI::error( 'Flushing all user caches requires WordPress 6.1+' ); + } + wp_cache_flush_group( 'users' ); + wp_cache_flush_group( 'user_meta' ); + WP_CLI::success( 'User caches cleared.' ); + } + } + + /** + * Clears option related caches. + * + * @subcommand flush-option + * + * ## OPTIONS + * + * [] + * : Option name. If not specified, clears all option caches. + * + * ## EXAMPLES + * + * # Clear all option caches. + * $ wp cache flush-option + * Success: Option caches cleared. + * + * # Clear cache for a specific option. + * $ wp cache flush-option my_option + * Success: Option cache for 'my_option' cleared. + * + * @param array $args Positional arguments. + */ + public function flush_option( $args ) { + $option_name = ! empty( $args ) ? $args[0] : null; + + if ( $option_name ) { + wp_cache_delete( 'alloptions', 'options' ); + wp_cache_delete( $option_name, 'options' ); + WP_CLI::success( "Option cache for '$option_name' cleared." ); + } else { + if ( ! wp_using_ext_object_cache() || ! function_exists( 'wp_cache_supports' ) || ! wp_cache_supports( 'flush_group' ) ) { + WP_CLI::error( 'Flushing all option caches requires WordPress 6.1+' ); + } + wp_cache_flush_group( 'options' ); + WP_CLI::success( 'Option caches cleared.' ); + } + } }