WordPress.org

Make WordPress Core

Changeset 31207


Ignore:
Timestamp:
01/16/2015 05:09:11 PM (7 years ago)
Author:
boonebgorges
Message:

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.

Location:
trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/taxonomy.php

    r31206 r31207  
    38263826 *
    38273827 * @param int $term_id The ancestor term: all returned terms should be descendants of $term_id.
    3828  * @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.
    3829  * @param string $taxonomy The taxonomy which determines the hierarchy of the terms.
     3828 * @param array  $terms     The set of terms - either an array of term objects or term IDs - from which those that
     3829 *                          are descendants of $term_id will be chosen.
     3830 * @param string $taxonomy  The taxonomy which determines the hierarchy of the terms.
     3831 * @param array  $ancestors Term ancestors that have already been identified. Passed by reference, to keep track of
     3832 *                          found terms when recursing the hierarchy. The array of located ancestors is used to prevent
     3833 *                          infinite recursion loops.
    38303834 * @return array The subset of $terms that are descendants of $term_id.
    38313835 */
    3832 function _get_term_children($term_id, $terms, $taxonomy) {
     3836function _get_term_children( $term_id, $terms, $taxonomy, &$ancestors = array() ) {
    38333837    $empty_array = array();
    38343838    if ( empty($terms) )
     
    38403844    if  ( ( 0 != $term_id ) && ! isset($has_children[$term_id]) )
    38413845        return $empty_array;
     3846
     3847    // Include the term itself in the ancestors array, so we can properly detect when a loop has occurred.
     3848    if ( empty( $ancestors ) ) {
     3849        $ancestors[] = $term_id;
     3850    }
    38423851
    38433852    foreach ( (array) $terms as $term ) {
     
    38503859        }
    38513860
    3852         if ( $term->term_id == $term_id ) {
     3861        // Don't recurse if we've already identified the term as a child - this indicates a loop.
     3862        if ( in_array( $term->term_id, $ancestors ) ) {
    38533863            continue;
    38543864        }
     
    38633873                continue;
    38643874
    3865             if ( $children = _get_term_children($term->term_id, $terms, $taxonomy) )
     3875            if ( $use_id ) {
     3876                $ancestors = array_merge( $ancestors, $term_list );
     3877            } else {
     3878                $ancestors = array_merge( $ancestors, wp_list_pluck( $term_list, 'term_id' ) );
     3879            }
     3880
     3881            if ( $children = _get_term_children( $term->term_id, $terms, $taxonomy, $ancestors) )
    38663882                $term_list = array_merge($term_list, $children);
    38673883        }
  • trunk/tests/phpunit/tests/term/getTerms.php

    r31206 r31207  
    393393
    394394        add_filter( 'wp_update_term_parent', 'wp_check_term_hierarchy_for_loops', 10, 3 );
     395    }
     396
     397    /**
     398     * @covers ::_get_term_children
     399     * @ticket 24461
     400     */
     401    public function test__get_term_children_handles_cycles() {
     402        remove_filter( 'wp_update_term_parent', 'wp_check_term_hierarchy_for_loops', 10 );
     403
     404        $c1 = $this->factory->category->create();
     405        $c2 = $this->factory->category->create( array( 'parent' => $c1 ) );
     406        $c3 = $this->factory->category->create( array( 'parent' => $c2 ) );
     407        wp_update_term( $c1, 'category', array( 'parent' => $c3 ) );
     408
     409        add_filter( 'wp_update_term_parent', 'wp_check_term_hierarchy_for_loops', 10, 3 );
     410
     411        $result = _get_term_children( $c1, array( $c1, $c2, $c3 ), 'category' );
     412
     413        $this->assertEqualSets( array( $c2, $c3 ), $result );
     414    }
     415
     416    /**
     417     * @covers ::_get_term_children
     418     * @ticket 24461
     419     */
     420    public function test__get_term_children_handles_cycles_when_terms_argument_contains_objects() {
     421        remove_filter( 'wp_update_term_parent', 'wp_check_term_hierarchy_for_loops', 10 );
     422
     423        $c1 = $this->factory->category->create_and_get();
     424        $c2 = $this->factory->category->create_and_get( array( 'parent' => $c1->term_id ) );
     425        $c3 = $this->factory->category->create_and_get( array( 'parent' => $c2->term_id ) );
     426        wp_update_term( $c1->term_id, 'category', array( 'parent' => $c3->term_id ) );
     427
     428        add_filter( 'wp_update_term_parent', 'wp_check_term_hierarchy_for_loops', 10, 3 );
     429
     430        $result = _get_term_children( $c1->term_id, array( $c1, $c2, $c3 ), 'category' );
     431
     432        $this->assertEqualSets( array( $c2, $c3 ), $result );
    395433    }
    396434
Note: See TracChangeset for help on using the changeset viewer.