Bail out of hierarchy loops in `_get_term_children()`.
This prevents infinite loops that lead to PHP nesting limit fatal errors. Props boonebgorges, sgrant. Fixes #24461. git-svn-id: https://develop.svn.wordpress.org/trunk@31207 602fd350-edb4-49c9-b593-d223f7449a82
This commit is contained in:
parent
8ae64f6384
commit
60f2983b2e
|
@ -3825,11 +3825,15 @@ function _get_term_hierarchy($taxonomy) {
|
||||||
* @since 2.3.0
|
* @since 2.3.0
|
||||||
*
|
*
|
||||||
* @param int $term_id The ancestor term: all returned terms should be descendants of $term_id.
|
* @param int $term_id The ancestor term: all returned terms should be descendants of $term_id.
|
||||||
* @param array $terms The set of terms---either an array of term objects or term IDs---from which those that are descendants of $term_id will be chosen.
|
* @param array $terms The set of terms - either an array of term objects or term IDs - from which those that
|
||||||
|
* are descendants of $term_id will be chosen.
|
||||||
* @param string $taxonomy The taxonomy which determines the hierarchy of the terms.
|
* @param string $taxonomy The taxonomy which determines the hierarchy of the terms.
|
||||||
|
* @param array $ancestors Term ancestors that have already been identified. Passed by reference, to keep track of
|
||||||
|
* found terms when recursing the hierarchy. The array of located ancestors is used to prevent
|
||||||
|
* infinite recursion loops.
|
||||||
* @return array The subset of $terms that are descendants of $term_id.
|
* @return array The subset of $terms that are descendants of $term_id.
|
||||||
*/
|
*/
|
||||||
function _get_term_children($term_id, $terms, $taxonomy) {
|
function _get_term_children( $term_id, $terms, $taxonomy, &$ancestors = array() ) {
|
||||||
$empty_array = array();
|
$empty_array = array();
|
||||||
if ( empty($terms) )
|
if ( empty($terms) )
|
||||||
return $empty_array;
|
return $empty_array;
|
||||||
|
@ -3840,6 +3844,11 @@ function _get_term_children($term_id, $terms, $taxonomy) {
|
||||||
if ( ( 0 != $term_id ) && ! isset($has_children[$term_id]) )
|
if ( ( 0 != $term_id ) && ! isset($has_children[$term_id]) )
|
||||||
return $empty_array;
|
return $empty_array;
|
||||||
|
|
||||||
|
// Include the term itself in the ancestors array, so we can properly detect when a loop has occurred.
|
||||||
|
if ( empty( $ancestors ) ) {
|
||||||
|
$ancestors[] = $term_id;
|
||||||
|
}
|
||||||
|
|
||||||
foreach ( (array) $terms as $term ) {
|
foreach ( (array) $terms as $term ) {
|
||||||
$use_id = false;
|
$use_id = false;
|
||||||
if ( !is_object($term) ) {
|
if ( !is_object($term) ) {
|
||||||
|
@ -3849,7 +3858,8 @@ function _get_term_children($term_id, $terms, $taxonomy) {
|
||||||
$use_id = true;
|
$use_id = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( $term->term_id == $term_id ) {
|
// Don't recurse if we've already identified the term as a child - this indicates a loop.
|
||||||
|
if ( in_array( $term->term_id, $ancestors ) ) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3862,7 +3872,13 @@ function _get_term_children($term_id, $terms, $taxonomy) {
|
||||||
if ( !isset($has_children[$term->term_id]) )
|
if ( !isset($has_children[$term->term_id]) )
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if ( $children = _get_term_children($term->term_id, $terms, $taxonomy) )
|
if ( $use_id ) {
|
||||||
|
$ancestors = array_merge( $ancestors, $term_list );
|
||||||
|
} else {
|
||||||
|
$ancestors = array_merge( $ancestors, wp_list_pluck( $term_list, 'term_id' ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $children = _get_term_children( $term->term_id, $terms, $taxonomy, $ancestors) )
|
||||||
$term_list = array_merge($term_list, $children);
|
$term_list = array_merge($term_list, $children);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -394,6 +394,44 @@ class Tests_Term_getTerms extends WP_UnitTestCase {
|
||||||
add_filter( 'wp_update_term_parent', 'wp_check_term_hierarchy_for_loops', 10, 3 );
|
add_filter( 'wp_update_term_parent', 'wp_check_term_hierarchy_for_loops', 10, 3 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers ::_get_term_children
|
||||||
|
* @ticket 24461
|
||||||
|
*/
|
||||||
|
public function test__get_term_children_handles_cycles() {
|
||||||
|
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 );
|
||||||
|
|
||||||
|
$result = _get_term_children( $c1, array( $c1, $c2, $c3 ), 'category' );
|
||||||
|
|
||||||
|
$this->assertEqualSets( array( $c2, $c3 ), $result );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers ::_get_term_children
|
||||||
|
* @ticket 24461
|
||||||
|
*/
|
||||||
|
public function test__get_term_children_handles_cycles_when_terms_argument_contains_objects() {
|
||||||
|
remove_filter( 'wp_update_term_parent', 'wp_check_term_hierarchy_for_loops', 10 );
|
||||||
|
|
||||||
|
$c1 = $this->factory->category->create_and_get();
|
||||||
|
$c2 = $this->factory->category->create_and_get( array( 'parent' => $c1->term_id ) );
|
||||||
|
$c3 = $this->factory->category->create_and_get( array( 'parent' => $c2->term_id ) );
|
||||||
|
wp_update_term( $c1->term_id, 'category', array( 'parent' => $c3->term_id ) );
|
||||||
|
|
||||||
|
add_filter( 'wp_update_term_parent', 'wp_check_term_hierarchy_for_loops', 10, 3 );
|
||||||
|
|
||||||
|
$result = _get_term_children( $c1->term_id, array( $c1, $c2, $c3 ), 'category' );
|
||||||
|
|
||||||
|
$this->assertEqualSets( array( $c2, $c3 ), $result );
|
||||||
|
}
|
||||||
|
|
||||||
public function test_get_terms_by_slug() {
|
public function test_get_terms_by_slug() {
|
||||||
$t1 = $this->factory->tag->create( array( 'slug' => 'foo' ) );
|
$t1 = $this->factory->tag->create( array( 'slug' => 'foo' ) );
|
||||||
$t2 = $this->factory->tag->create( array( 'slug' => 'bar' ) );
|
$t2 = $this->factory->tag->create( array( 'slug' => 'bar' ) );
|
||||||
|
|
Loading…
Reference in New Issue