From e33ef0c6ac5797623a76f5dc39aa3031f6670740 Mon Sep 17 00:00:00 2001 From: Scott Taylor Date: Thu, 6 Feb 2014 01:40:05 +0000 Subject: [PATCH] Add cache invalidation when updating a term, example: create a category, assign it to a post, edit the category. Currently, the post's term cache is not updated. When updating terms in a given taxonomy, invalidate the object term caches linked to that taxonomy. Introduce `get_taxonomy_last_changed()`, `set_taxonomy_last_changed()`, and `post_taxonomy_is_fresh()`. `post_taxonomy_is_fresh()` is only called in `get_object_term_cache()` - at which point the taxonomy's `last_changed` value is checked against the post's `{$taxonomy}_last_changed` value. `set_taxonomy_last_changed()` is called whenever directory database queries are made that insert new terms or affect existing terms. Fixes #22526. git-svn-id: https://develop.svn.wordpress.org/trunk@27101 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/taxonomy.php | 71 +++++++++++++++++++++++++++++++++++- tests/phpunit/tests/term.php | 50 +++++++++++++++++++++++++ 2 files changed, 119 insertions(+), 2 deletions(-) diff --git a/src/wp-includes/taxonomy.php b/src/wp-includes/taxonomy.php index f5e3f22969..297b98d8be 100644 --- a/src/wp-includes/taxonomy.php +++ b/src/wp-includes/taxonomy.php @@ -1876,6 +1876,7 @@ function wp_delete_term( $term, $taxonomy, $args = array() ) { do_action( 'edit_term_taxonomies', $edit_tt_ids ); $wpdb->update( $wpdb->term_taxonomy, compact( 'parent' ), array( 'parent' => $term_obj->term_id) + compact( 'taxonomy' ) ); do_action( 'edited_term_taxonomies', $edit_tt_ids ); + set_taxonomy_last_changed( $taxonomy ); } $objects = $wpdb->get_col( $wpdb->prepare( "SELECT object_id FROM $wpdb->term_relationships WHERE term_taxonomy_id = %d", $tt_id ) ); @@ -1910,6 +1911,7 @@ function wp_delete_term( $term, $taxonomy, $args = array() ) { $wpdb->delete( $wpdb->terms, array( 'term_id' => $term ) ); clean_term_cache($term, $taxonomy); + set_taxonomy_last_changed( $taxonomy ); do_action( 'delete_term', $term, $tt_id, $taxonomy, $deleted_term ); do_action( "delete_$taxonomy", $term, $tt_id, $deleted_term ); @@ -2162,6 +2164,7 @@ function wp_insert_term( $term, $taxonomy, $args = array() ) { do_action( 'edit_terms', $alias->term_id, $taxonomy ); $wpdb->update($wpdb->terms, compact('term_group'), array('term_id' => $alias->term_id) ); do_action( 'edited_terms', $alias->term_id, $taxonomy ); + set_taxonomy_last_changed( $taxonomy ); } } @@ -2223,6 +2226,7 @@ function wp_insert_term( $term, $taxonomy, $args = array() ) { $term_id = apply_filters('term_id_filter', $term_id, $tt_id); clean_term_cache($term_id, $taxonomy); + set_taxonomy_last_changed( $taxonomy ); do_action("created_term", $term_id, $tt_id, $taxonomy); do_action("created_$taxonomy", $term_id, $tt_id); @@ -2329,6 +2333,7 @@ function wp_set_object_terms($object_id, $terms, $taxonomy, $append = false) { } wp_cache_delete( $object_id, $taxonomy . '_relationships' ); + set_taxonomy_last_changed( $taxonomy ); do_action('set_object_terms', $object_id, $terms, $tt_ids, $taxonomy, $append, $old_tt_ids); return $tt_ids; @@ -2407,6 +2412,7 @@ function wp_remove_object_terms( $object_id, $terms, $taxonomy ) { $deleted = $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->term_relationships WHERE object_id = %d AND term_taxonomy_id IN ($in_tt_ids)", $object_id ) ); do_action( 'deleted_term_relationships', $object_id, $tt_ids ); wp_update_term_count( $tt_ids, $taxonomy ); + set_taxonomy_last_changed( $taxonomy ); return (bool) $deleted; } @@ -2565,6 +2571,7 @@ function wp_update_term( $term_id, $taxonomy, $args = array() ) { do_action( 'edit_terms', $alias->term_id, $taxonomy ); $wpdb->update( $wpdb->terms, compact('term_group'), array( 'term_id' => $alias->term_id ) ); do_action( 'edited_terms', $alias->term_id, $taxonomy ); + set_taxonomy_last_changed( $taxonomy ); } } @@ -2600,6 +2607,7 @@ function wp_update_term( $term_id, $taxonomy, $args = array() ) { $term_id = apply_filters('term_id_filter', $term_id, $tt_id); clean_term_cache($term_id, $taxonomy); + set_taxonomy_last_changed( $taxonomy ); do_action("edited_term", $term_id, $tt_id, $taxonomy); do_action("edited_$taxonomy", $term_id, $tt_id); @@ -2738,9 +2746,12 @@ function clean_object_term_cache($object_ids, $object_type) { $taxonomies = get_object_taxonomies( $object_type ); - foreach ( $object_ids as $id ) - foreach ( $taxonomies as $taxonomy ) + foreach ( $object_ids as $id ) { + foreach ( $taxonomies as $taxonomy ) { wp_cache_delete($id, "{$taxonomy}_relationships"); + set_taxonomy_last_changed( $taxonomy ); + } + } do_action('clean_object_term_cache', $object_ids, $object_type); } @@ -2800,6 +2811,7 @@ function clean_term_cache($ids, $taxonomy = '', $clean_taxonomy = true) { } do_action('clean_term_cache', $ids, $taxonomy); + set_taxonomy_last_changed( $taxonomy ); } wp_cache_set( 'last_changed', microtime(), 'terms' ); @@ -2819,6 +2831,9 @@ function clean_term_cache($ids, $taxonomy = '', $clean_taxonomy = true) { * @return bool|array Empty array if $terms found, but not $taxonomy. False if nothing is in cache for $taxonomy and $id. */ function get_object_term_cache($id, $taxonomy) { + if ( ! post_taxonomy_is_fresh( $id, $taxonomy ) ) { + return false; + } $cache = wp_cache_get($id, "{$taxonomy}_relationships"); return $cache; } @@ -3117,6 +3132,7 @@ function _update_post_term_count( $terms, $taxonomy ) { $wpdb->update( $wpdb->term_taxonomy, compact( 'count' ), array( 'term_taxonomy_id' => $term ) ); do_action( 'edited_term_taxonomy', $term, $taxonomy ); } + set_taxonomy_last_changed( $taxonomy->name ); } /** @@ -3142,6 +3158,7 @@ function _update_generic_term_count( $terms, $taxonomy ) { $wpdb->update( $wpdb->term_taxonomy, compact( 'count' ), array( 'term_taxonomy_id' => $term ) ); do_action( 'edited_term_taxonomy', $term, $taxonomy ); } + set_taxonomy_last_changed( $taxonomy->name ); } /** @@ -3469,3 +3486,53 @@ function wp_check_term_hierarchy_for_loops( $parent, $term_id, $taxonomy ) { return $parent; } + +/** + * Retrieve the 'last_changed' value for the passed taxonomy. Retrieves + * from cache, if present + * + * @since 3.9.0 + * + * @param string $taxonomy + * @return int + */ +function get_taxonomy_last_changed( $taxonomy ) { + $last_changed = wp_cache_get( 'last_changed', $taxonomy ); + if ( ! $last_changed ) { + $last_changed = microtime(); + wp_cache_set( 'last_changed', $last_changed, $taxonomy ); + } + return $last_changed; +} + +/** + * Reset 'last_changed' for the passed taxonomy + * + * @since 3.9.0 + * + * @param string $taxonomy + * @return int + */ +function set_taxonomy_last_changed( $taxonomy ) { + wp_cache_delete( 'last_changed', $taxonomy ); + return get_taxonomy_last_changed( $taxonomy ); +} + +/** + * Determine if a post's cache for the passed taxonomy + * is in sync. + * @since 3.9.0 + * + * @param int $id + * @param string $taxonomy + * @return boolean + */ +function post_taxonomy_is_fresh( $id, $taxonomy ) { + $last_changed = get_taxonomy_last_changed( $taxonomy ); + $post_last_changed = wp_cache_get( $id, $taxonomy . '_last_changed' ); + if ( ! $post_last_changed || $last_changed !== $post_last_changed ) { + wp_cache_set( $id, $last_changed, $taxonomy . '_last_changed' ); + return false; + } + return true; +} \ No newline at end of file diff --git a/tests/phpunit/tests/term.php b/tests/phpunit/tests/term.php index b90fbef735..5fc16c8d4e 100644 --- a/tests/phpunit/tests/term.php +++ b/tests/phpunit/tests/term.php @@ -568,6 +568,56 @@ class Tests_Term extends WP_UnitTestCase { $this->assertEquals( 0, $count ); } + /** + * @ticket 22526 + */ + function test_get_taxonomy_last_changed() { + $last_changed = get_taxonomy_last_changed( 'category' ); + $last_changed_cache = wp_cache_get( 'last_changed', 'category' ); + $this->assertEquals( $last_changed, $last_changed_cache ); + wp_cache_delete( 'last_changed', 'category' ); + $this->assertEquals( $last_changed, $last_changed_cache ); + $last_changed = get_taxonomy_last_changed( 'category' ); + $this->assertNotEquals( $last_changed, $last_changed_cache ); + + $last_changed2 = get_taxonomy_last_changed( 'category' ); + $this->factory->category->create(); + $last_changed3 = get_taxonomy_last_changed( 'category' ); + $this->assertNotEquals( $last_changed2, $last_changed3 ); + } + + /** + * @ticket 22526 + */ + function test_set_taxonomy_last_changed() { + $last_changed1 = set_taxonomy_last_changed( 'category' ); + $last_changed2 = set_taxonomy_last_changed( 'category' ); + $this->assertNotEquals( $last_changed1, $last_changed2 ); + + $last_changed3 = set_taxonomy_last_changed( 'category' ); + $last_changed4 = get_taxonomy_last_changed( 'category' ); + $this->assertEquals( $last_changed3, $last_changed4 ); + } + + /** + * @ticket 22526 + */ + function test_post_taxonomy_is_fresh() { + $post_id = $this->factory->post->create(); + $term_id = $this->factory->category->create( array( 'name' => 'Foo' ) ); + wp_set_post_categories( $post_id, $term_id ); + + $this->assertFalse( post_taxonomy_is_fresh( $post_id, 'category' ) ); + $this->assertTrue( post_taxonomy_is_fresh( $post_id, 'category' ) ); + $this->assertTrue( post_taxonomy_is_fresh( $post_id, 'category' ) ); + + wp_update_term( $term_id, 'category', array( 'name' => 'Bar' ) ); + + $this->assertFalse( post_taxonomy_is_fresh( $post_id, 'category' ) ); + get_the_category( $post_id ); + $this->assertTrue( post_taxonomy_is_fresh( $post_id, 'category' ) ); + } + /** * @ticket 22526 */