From 8ae64f6384766ee16a88a04db38c5a27189dd73b Mon Sep 17 00:00:00 2001 From: Boone Gorges Date: Fri, 16 Jan 2015 16:45:21 +0000 Subject: [PATCH] Bail out of hierarchy loops in `_pad_term_counts()`. Taxonomy hierarchy loops should not occur naturally, but when they do, the logic of `_pad_term_counts()` could result in infinite loops, leading to timeouts. We avoid this by breaking when a loop is detected. Fixes #20635. git-svn-id: https://develop.svn.wordpress.org/trunk@31206 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/taxonomy.php | 6 ++++++ tests/phpunit/tests/term/getTerms.php | 25 +++++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/src/wp-includes/taxonomy.php b/src/wp-includes/taxonomy.php index 991f59552f..caf2255856 100644 --- a/src/wp-includes/taxonomy.php +++ b/src/wp-includes/taxonomy.php @@ -3916,9 +3916,15 @@ function _pad_term_counts(&$terms, $taxonomy) { } // Touch every ancestor's lookup row for each post in each term + $ancestors = array(); foreach ( $term_ids as $term_id ) { + $ancestors[] = $term_id; $child = $term_id; while ( !empty( $terms_by_id[$child] ) && $parent = $terms_by_id[$child]->parent ) { + if ( in_array( $parent, $ancestors ) ) { + break; + } + if ( !empty( $term_items[$term_id] ) ) foreach ( $term_items[$term_id] as $item_id => $touches ) { $term_items[$parent][$item_id] = isset($term_items[$parent][$item_id]) ? ++$term_items[$parent][$item_id]: 1; diff --git a/tests/phpunit/tests/term/getTerms.php b/tests/phpunit/tests/term/getTerms.php index 8e0abce271..7b04ac8807 100644 --- a/tests/phpunit/tests/term/getTerms.php +++ b/tests/phpunit/tests/term/getTerms.php @@ -1039,6 +1039,31 @@ class Tests_Term_getTerms extends WP_UnitTestCase { $this->assertEqualSets( $expected, $actual ); } + /* + * @ticket 20635 + */ + public function test_pad_counts_should_not_recurse_infinitely_when_term_hierarchy_has_a_loop() { + remove_filter( 'wp_update_term_parent', 'wp_check_term_hierarchy_for_loops', 10 ); + + $c1 = $this->factory->category->create(); + $c2 = $this->factory->category->create( array( 'parent' => $c1 ) ); + $c3 = $this->factory->category->create( array( 'parent' => $c2 ) ); + wp_update_term( $c1, 'category', array( 'parent' => $c3 ) ); + + add_filter( 'wp_update_term_parent', 'wp_check_term_hierarchy_for_loops', 10, 3 ); + + $posts = $this->factory->post->create_many( 3 ); + wp_set_post_terms( $posts[0], $c1, 'category' ); + wp_set_post_terms( $posts[1], $c2, 'category' ); + wp_set_post_terms( $posts[2], $c3, 'category' ); + + $terms = get_terms( 'category', array( + 'pad_counts' => true, + ) ); + + $this->assertEqualSets( array( $c1, $c2, $c3 ), wp_list_pluck( $terms, 'term_id' ) ); + } + protected function create_hierarchical_terms_and_posts() { $terms = array();