Skip to content

Commit 673857f

Browse files
author
boonebgorges
committed
Store only term IDs in object term relationships caches.
Previously, objects containing all data about a term were stored in each object's term cache. Besides being wasteful, this approach caused invalidation issues, as when a modified term count required a flush for all objects belonging to the term. Backward compatibility is maintained for plugins that continue to put object data directly into the `{$taxonomy}_relationships` cache bucket. Fixes #36814. git-svn-id: https://develop.svn.wordpress.org/trunk@37573 602fd350-edb4-49c9-b593-d223f7449a82
1 parent 8489862 commit 673857f

File tree

5 files changed

+110
-36
lines changed

5 files changed

+110
-36
lines changed

src/wp-admin/includes/taxonomy.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ function get_terms_to_edit( $post_id, $taxonomy = 'post_tag' ) {
242242
$terms = get_object_term_cache( $post_id, $taxonomy );
243243
if ( false === $terms ) {
244244
$terms = wp_get_object_terms( $post_id, $taxonomy );
245-
wp_cache_add( $post_id, $terms, $taxonomy . '_relationships' );
245+
wp_cache_add( $post_id, wp_list_pluck( $terms, 'term_id' ), $taxonomy . '_relationships' );
246246
}
247247

248248
if ( ! $terms ) {

src/wp-admin/includes/template.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,7 @@ function get_inline_data($post) {
311311
$terms = get_object_term_cache( $post->ID, $taxonomy_name );
312312
if ( false === $terms ) {
313313
$terms = wp_get_object_terms( $post->ID, $taxonomy_name );
314-
wp_cache_add( $post->ID, $terms, $taxonomy_name . '_relationships' );
314+
wp_cache_add( $post->ID, wp_list_pluck( $terms, 'term_id' ), $taxonomy_name . '_relationships' );
315315
}
316316
$term_ids = empty( $terms ) ? array() : wp_list_pluck( $terms, 'term_id' );
317317

src/wp-includes/category-template.php

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1187,18 +1187,11 @@ function get_the_terms( $post, $taxonomy ) {
11871187
if ( false === $terms ) {
11881188
$terms = wp_get_object_terms( $post->ID, $taxonomy );
11891189
if ( ! is_wp_error( $terms ) ) {
1190-
$to_cache = array();
1191-
foreach ( $terms as $key => $term ) {
1192-
$to_cache[ $key ] = $term->data;
1193-
}
1194-
wp_cache_add( $post->ID, $to_cache, $taxonomy . '_relationships' );
1190+
$term_ids = wp_list_pluck( $terms, 'term_id' );
1191+
wp_cache_add( $post->ID, $term_ids, $taxonomy . '_relationships' );
11951192
}
11961193
}
11971194

1198-
if ( ! is_wp_error( $terms ) ) {
1199-
$terms = array_map( 'get_term', $terms );
1200-
}
1201-
12021195
/**
12031196
* Filters the list of terms attached to the given post.
12041197
*

src/wp-includes/taxonomy.php

Lines changed: 60 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3239,15 +3239,42 @@ function clean_term_cache($ids, $taxonomy = '', $clean_taxonomy = true) {
32393239
/**
32403240
* Retrieves the taxonomy relationship to the term object id.
32413241
*
3242+
* Upstream functions (like `get_the_terms()` and `is_object_in_term()`) are responsible for populating the
3243+
* object-term relationship cache. The current function only fetches relationship data that is already in the cache.
3244+
*
32423245
* @since 2.3.0
32433246
*
32443247
* @param int $id Term object ID.
32453248
* @param string $taxonomy Taxonomy name.
3246-
* @return bool|mixed Empty array if $terms found, but not `$taxonomy`. False if nothing is in cache
3247-
* for `$taxonomy` and `$id`.
3249+
* @return bool|array Array of `WP_Term` objects, if cached False if cache is empty for `$taxonomy` and `$id`.
32483250
*/
32493251
function get_object_term_cache( $id, $taxonomy ) {
3250-
return wp_cache_get( $id, "{$taxonomy}_relationships" );
3252+
$_term_ids = wp_cache_get( $id, "{$taxonomy}_relationships" );
3253+
3254+
// We leave the priming of relationship caches to upstream functions.
3255+
if ( false === $_term_ids ) {
3256+
return false;
3257+
}
3258+
3259+
// Backward compatibility for if a plugin is putting objects into the cache, rather than IDs.
3260+
$term_ids = array();
3261+
foreach ( $_term_ids as $term_id ) {
3262+
if ( is_numeric( $term_id ) ) {
3263+
$term_ids[] = intval( $term_id );
3264+
} elseif ( isset( $term_id->term_id ) ) {
3265+
$term_ids[] = intval( $term_id->term_id );
3266+
}
3267+
}
3268+
3269+
// Fill the term objects.
3270+
_prime_term_caches( $term_ids );
3271+
3272+
$terms = array();
3273+
foreach ( $term_ids as $term_id ) {
3274+
$terms[] = wp_cache_get( $term_id, 'terms' );
3275+
}
3276+
3277+
return array_map( 'get_term', $terms );
32513278
}
32523279

32533280
/**
@@ -3297,8 +3324,9 @@ function update_object_term_cache($object_ids, $object_type) {
32973324
) );
32983325

32993326
$object_terms = array();
3300-
foreach ( (array) $terms as $term )
3301-
$object_terms[$term->object_id][$term->taxonomy][] = $term;
3327+
foreach ( (array) $terms as $term ) {
3328+
$object_terms[ $term->object_id ][ $term->taxonomy ][] = $term->term_id;
3329+
}
33023330

33033331
foreach ( $ids as $id ) {
33043332
foreach ( $taxonomies as $taxonomy ) {
@@ -3504,6 +3532,32 @@ function _pad_term_counts( &$terms, $taxonomy ) {
35043532
$terms_by_id[$id]->count = count($items);
35053533
}
35063534

3535+
/**
3536+
* Adds any terms from the given IDs to the cache that do not already exist in cache.
3537+
*
3538+
* @since 4.6.0
3539+
* @access private
3540+
*
3541+
* @global wpdb $wpdb WordPress database abstraction object.
3542+
*
3543+
* @param array $term_ids Array of term IDs.
3544+
* @param bool $update_meta_cache Optional. Whether to update the meta cache. Default true.
3545+
*/
3546+
function _prime_term_caches( $term_ids, $update_meta_cache = true ) {
3547+
global $wpdb;
3548+
3549+
$non_cached_ids = _get_non_cached_ids( $term_ids, 'terms' );
3550+
if ( ! empty( $non_cached_ids ) ) {
3551+
$fresh_terms = $wpdb->get_results( sprintf( "SELECT t.*, tt.* FROM $wpdb->terms AS t INNER JOIN $wpdb->term_taxonomy AS tt ON t.term_id = tt.term_id WHERE t.term_id IN (%s)", join( ",", array_map( 'intval', $non_cached_ids ) ) ) );
3552+
3553+
update_term_cache( $fresh_terms, $update_meta_cache );
3554+
3555+
if ( $update_meta_cache ) {
3556+
update_termmeta_cache( $non_cached_ids );
3557+
}
3558+
}
3559+
}
3560+
35073561
//
35083562
// Default callbacks
35093563
//
@@ -4211,7 +4265,7 @@ function is_object_in_term( $object_id, $taxonomy, $terms = null ) {
42114265
$object_terms = get_object_term_cache( $object_id, $taxonomy );
42124266
if ( false === $object_terms ) {
42134267
$object_terms = wp_get_object_terms( $object_id, $taxonomy, array( 'update_term_meta_cache' => false ) );
4214-
wp_cache_set( $object_id, $object_terms, "{$taxonomy}_relationships" );
4268+
wp_cache_set( $object_id, wp_list_pluck( $object_terms, 'term_id' ), "{$taxonomy}_relationships" );
42154269
}
42164270

42174271
if ( is_wp_error( $object_terms ) )

tests/phpunit/tests/term/getTheTerms.php

Lines changed: 46 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -75,25 +75,6 @@ function test_object_term_cache_when_term_changes() {
7575
$this->assertEquals( 'This description is even more amazing!', $terms[0]->description );
7676
}
7777

78-
/**
79-
* @ticket 34262
80-
*/
81-
public function test_get_the_terms_should_not_cache_wp_term_objects() {
82-
$p = self::$post_ids[0];
83-
register_taxonomy( 'wptests_tax', 'post' );
84-
$t = self::factory()->term->create( array( 'taxonomy' => 'wptests_tax' ) );
85-
wp_set_object_terms( $p, $t, 'wptests_tax' );
86-
87-
// Prime the cache.
88-
$terms = get_the_terms( $p, 'wptests_tax' );
89-
90-
$cached = get_object_term_cache( $p, 'wptests_tax' );
91-
92-
$this->assertNotEmpty( $cached );
93-
$this->assertSame( $t, (int) $cached[0]->term_id );
94-
$this->assertNotInstanceOf( 'WP_Term', $cached[0] );
95-
}
96-
9778
/**
9879
* @ticket 34262
9980
*/
@@ -170,4 +151,50 @@ function test_get_the_terms_should_return_wp_error_when_taxonomy_is_unregistered
170151
$terms = get_the_terms( $p, 'this-taxonomy-does-not-exist' );
171152
$this->assertTrue( is_wp_error( $terms ) );
172153
}
154+
155+
/**
156+
* @ticket 36814
157+
*/
158+
public function test_count_should_not_be_improperly_cached() {
159+
register_taxonomy( 'wptests_tax', 'post' );
160+
161+
$t = self::factory()->term->create( array( 'taxonomy' => 'wptests_tax' ) );
162+
163+
wp_set_object_terms( self::$post_ids[0], $t, 'wptests_tax' );
164+
165+
$terms = get_the_terms( self::$post_ids[0], 'wptests_tax' );
166+
$this->assertSame( 1, $terms[0]->count );
167+
168+
wp_set_object_terms( self::$post_ids[1], $t, 'wptests_tax' );
169+
170+
$terms = get_the_terms( self::$post_ids[0], 'wptests_tax' );
171+
$this->assertSame( 2, $terms[0]->count );
172+
}
173+
174+
/**
175+
* @ticket 36814
176+
*/
177+
public function test_uncached_terms_should_be_primed_with_a_single_query() {
178+
global $wpdb;
179+
180+
register_taxonomy( 'wptests_tax', 'post' );
181+
182+
$terms = self::factory()->term->create_many( 3, array( 'taxonomy' => 'wptests_tax' ) );
183+
184+
wp_set_object_terms( self::$post_ids[0], $terms, 'wptests_tax' );
185+
186+
get_the_terms( self::$post_ids[0], 'wptests_tax' );
187+
188+
// Clean cache for two of the terms.
189+
clean_term_cache( array( $terms[0], $terms[1] ), 'wptests_tax', false );
190+
191+
$num_queries = $wpdb->num_queries;
192+
$found = get_the_terms( self::$post_ids[0], 'wptests_tax' );
193+
194+
$this->assertEqualSets( $terms, wp_list_pluck( $found, 'term_id' ) );
195+
196+
$num_queries++;
197+
$this->assertSame( $num_queries, $wpdb->num_queries );
198+
199+
}
173200
}

0 commit comments

Comments
 (0)