diff --git a/src/wp-includes/class-wp-metadata-lazyloader.php b/src/wp-includes/class-wp-metadata-lazyloader.php index f9c02391d2b1d..ae3cca4823167 100644 --- a/src/wp-includes/class-wp-metadata-lazyloader.php +++ b/src/wp-includes/class-wp-metadata-lazyloader.php @@ -65,6 +65,10 @@ public function __construct() { 'filter' => 'get_blog_metadata', 'callback' => array( $this, 'lazyload_meta_callback' ), ), + 'user' => array( + 'filter' => 'get_user_metadata', + 'callback' => array( $this, 'lazyload_meta_callback' ), + ), ); } diff --git a/src/wp-includes/pluggable.php b/src/wp-includes/pluggable.php index 5edd0c760cbb2..786b9b6522f3d 100644 --- a/src/wp-includes/pluggable.php +++ b/src/wp-includes/pluggable.php @@ -125,7 +125,8 @@ function get_user_by( $field, $value ) { function cache_users( $user_ids ) { global $wpdb; - update_meta_cache( 'user', $user_ids ); + $user_ids = array_unique( array_map( 'intval', $user_ids ), SORT_NUMERIC ); + wp_lazyload_user_meta( $user_ids ); $clean = _get_non_cached_ids( $user_ids, 'users' ); diff --git a/src/wp-includes/user.php b/src/wp-includes/user.php index c5f20f7d5fe67..22334f9a5e973 100644 --- a/src/wp-includes/user.php +++ b/src/wp-includes/user.php @@ -1320,6 +1320,21 @@ function update_user_meta( $user_id, $meta_key, $meta_value, $prev_value = '' ) return update_metadata( 'user', $user_id, $meta_key, $meta_value, $prev_value ); } +/** + * Queue user meta for lazy-loading. + * + * @since 6.9.0 + * + * @param int[] $user_ids List of user IDs. + */ +function wp_lazyload_user_meta( array $user_ids ) { + if ( empty( $user_ids ) ) { + return; + } + $lazyloader = wp_metadata_lazyloader(); + $lazyloader->queue_objects( 'user', $user_ids ); +} + /** * Counts number of users who have each of the user roles. * diff --git a/tests/phpunit/includes/abstract-testcase.php b/tests/phpunit/includes/abstract-testcase.php index 29ce8d8b530bf..e58d1654df502 100644 --- a/tests/phpunit/includes/abstract-testcase.php +++ b/tests/phpunit/includes/abstract-testcase.php @@ -303,6 +303,7 @@ protected function reset_lazyload_queue() { $lazyloader->reset_queue( 'term' ); $lazyloader->reset_queue( 'comment' ); $lazyloader->reset_queue( 'blog' ); + $lazyloader->reset_queue( 'user' ); } /** diff --git a/tests/phpunit/tests/post/updatePostAuthorCaches.php b/tests/phpunit/tests/post/updatePostAuthorCaches.php index 0c5823561bf3a..0a43f04c92eff 100644 --- a/tests/phpunit/tests/post/updatePostAuthorCaches.php +++ b/tests/phpunit/tests/post/updatePostAuthorCaches.php @@ -67,6 +67,28 @@ public function test_update_post_author_caches() { $q->the_post(); } + $this->assertSame( 0, $action->get_call_count(), 'Ensure that user meta are not primed' ); + } + + /** + * @ticket 63021 + */ + public function test_update_post_author_caches_force_load_meta() { + $action = new MockAction(); + add_filter( 'update_user_metadata_cache', array( $action, 'filter' ), 10, 2 ); + + $q = new WP_Query( + array( + 'post_type' => 'post', + 'posts_per_page' => self::$post_author_count, + ) + ); + + while ( $q->have_posts() ) { + $q->the_post(); + get_the_author_meta(); // Force loading of author meta. + } + $args = $action->get_args(); $last_args = end( $args ); diff --git a/tests/phpunit/tests/query/cacheResults.php b/tests/phpunit/tests/query/cacheResults.php index 02bb702a2b6aa..76cb51d1367dc 100644 --- a/tests/phpunit/tests/query/cacheResults.php +++ b/tests/phpunit/tests/query/cacheResults.php @@ -1982,11 +1982,10 @@ public function test_author_cache_warmed_by_the_loop( $fields ) { $query_1->the_post(); $num_loop_queries = get_num_queries() - $start_loop_queries; /* - * Two expected queries: - * 1: User meta data, - * 2: User data. + * One expected query: + * 1: User data. */ - $this->assertSame( 2, $num_loop_queries, 'Unexpected number of queries while initializing the loop.' ); + $this->assertSame( 1, $num_loop_queries, 'Unexpected number of queries while initializing the loop.' ); $start_author_queries = get_num_queries(); get_user_by( 'ID', self::$author_id ); diff --git a/tests/phpunit/tests/query/thePost.php b/tests/phpunit/tests/query/thePost.php index 6032a01dbeb1b..c07523a3552b3 100644 --- a/tests/phpunit/tests/query/thePost.php +++ b/tests/phpunit/tests/query/thePost.php @@ -265,10 +265,10 @@ public function test_the_loop_primes_the_author_cache( $fields, $expected_querie */ public function data_the_loop_fields() { return array( - 'all fields' => array( 'all', 2 ), - 'all fields (empty fields)' => array( '', 2 ), - 'post IDs' => array( 'ids', 4 ), - 'post ids and parent' => array( 'id=>parent', 4 ), + 'all fields' => array( 'all', 1 ), + 'all fields (empty fields)' => array( '', 1 ), + 'post IDs' => array( 'ids', 3 ), + 'post ids and parent' => array( 'id=>parent', 3 ), ); } diff --git a/tests/phpunit/tests/user/lazyLoadMeta.php b/tests/phpunit/tests/user/lazyLoadMeta.php new file mode 100644 index 0000000000000..050153c40cb3b --- /dev/null +++ b/tests/phpunit/tests/user/lazyLoadMeta.php @@ -0,0 +1,70 @@ +user->create_many( 3 ); + // Clear any existing cache. + wp_cache_delete_multiple( $user_ids, 'user_meta' ); + wp_lazyload_user_meta( $user_ids ); + $filter = new MockAction(); + add_filter( 'update_user_metadata_cache', array( $filter, 'filter' ), 10, 2 ); + get_user_meta( $user_ids[0] ); + + $args = $filter->get_args(); + $first = reset( $args ); + $load_user_ids = end( $first ); + $this->assertSameSets( $user_ids, $load_user_ids, 'Ensure all user IDs are loaded in a single batch' ); + } + + /** + * @ticket 63021 + */ + public function test_lazy_load_meta_sets() { + $user_ids1 = self::factory()->user->create_many( 3 ); + $user_ids2 = self::factory()->user->create_many( 3 ); + $user_ids = array_merge( $user_ids1, $user_ids2 ); + // Clear any existing cache. + wp_cache_delete_multiple( $user_ids, 'user_meta' ); + wp_lazyload_user_meta( $user_ids ); + $filter = new MockAction(); + add_filter( 'update_user_metadata_cache', array( $filter, 'filter' ), 10, 2 ); + get_user_meta( $user_ids[0] ); + + $args = $filter->get_args(); + $first = reset( $args ); + $load_user_ids = end( $first ); + $this->assertSameSets( $user_ids, $load_user_ids, 'Ensure all user IDs are loaded in a single batch' ); + } + + /** + * @ticket 63021 + */ + public function test_lazy_load_meta_not_in_queue() { + $user_ids1 = self::factory()->user->create_many( 3 ); + $user_ids2 = self::factory()->user->create_many( 3 ); + $user_ids = array_merge( $user_ids1, $user_ids2 ); + $new_user_id = self::factory()->user->create(); + wp_lazyload_user_meta( $user_ids ); + // Add a user not in the lazy load queue. + $user_ids[] = $new_user_id; + // Clear any existing cache including the new user not in the queue. + wp_cache_delete_multiple( $user_ids, 'user_meta' ); + + $filter = new MockAction(); + add_filter( 'update_user_metadata_cache', array( $filter, 'filter' ), 10, 2 ); + get_user_meta( $new_user_id ); + + $args = $filter->get_args(); + $first = reset( $args ); + $load_user_ids = end( $first ); + $this->assertSameSets( $user_ids, $load_user_ids, 'Ensure all user IDs are loaded, including the one not in the queue' ); + } +}