Make WordPress Core

Changeset 34997


Ignore:
Timestamp:
10/10/2015 01:58:37 AM (9 years ago)
Author:
boonebgorges
Message:

Introduce WP_Term.

get_term() now returns a WP_Term object, instead of a stdClass object.
Cache support and sanitization filters for individual terms are now more
centralized. For example, get_term_by() is able to cast results of its query
to a WP_Term object by passing it through get_term().

The $taxonomy parameter for get_term() is now optional, as terms ought to
be unique to a taxonomy (ie, shared terms no longer exist). In cases where
get_term() detects that the term matching the specified term_id is from the
wrong taxonomy, it checks to see if you've requested a shared term, and if so,
it splits the term. This is used only for fallback purposes.

The elimination of shared terms allows the caching strategy for terms to be
simplified. Individual terms are now cached in a single 'terms' bucket.

Props flixos90, boonebgorges, scribu, dipesh.kakadiya.
See #14162.

Location:
trunk
Files:
1 added
6 edited

Legend:

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

    r34419 r34997  
    320320 *
    321321 * @since 2.3.0
     322 * @since 4.4.0 The `$category` parameter now also accepts a WP_Term object.
    322323 * @access private
    323324 *
    324  * @param array|object $category Category Row object or array
     325 * @param array|object|WP_Term $category Category Row object or array
    325326 */
    326327function _make_cat_compat( &$category ) {
    327328    if ( is_object( $category ) && ! is_wp_error( $category ) ) {
    328         $category->cat_ID = &$category->term_id;
    329         $category->category_count = &$category->count;
    330         $category->category_description = &$category->description;
    331         $category->cat_name = &$category->name;
    332         $category->category_nicename = &$category->slug;
    333         $category->category_parent = &$category->parent;
     329        $category->cat_ID = $category->term_id;
     330        $category->category_count = $category->count;
     331        $category->category_description = $category->description;
     332        $category->cat_name = $category->name;
     333        $category->category_nicename = $category->slug;
     334        $category->category_parent = $category->parent;
    334335    } elseif ( is_array( $category ) && isset( $category['term_id'] ) ) {
    335336        $category['cat_ID'] = &$category['term_id'];
  • trunk/src/wp-includes/taxonomy-functions.php

    r34891 r34997  
    709709 *
    710710 * @since 2.3.0
     711 * @since 4.4.0 Converted to return a WP_Term object if `$output` is `OBJECT`.
     712 *              The `$taxonomy` parameter was made optional.
    711713 *
    712714 * @global wpdb $wpdb WordPress database abstraction object.
    713715 * @see sanitize_term_field() The $context param lists the available values for get_term_by() $filter param.
    714716 *
    715  * @param int|object $term     If integer, will get from database. If object will apply filters and return $term.
    716  * @param string     $taxonomy Taxonomy name that $term is part of.
     717 * @param int|WP_Term|object $term If integer, term data will be fetched from the database, or from the cache if
     718 *                                 available. If stdClass object (as in the results of a database query), will apply
     719 *                                 filters and return a `WP_Term` object corresponding to the `$term` data. If `WP_Term`,
     720 *                                 will return `$term`.
     721 * @param string     $taxonomy Optional. Taxonomy name that $term is part of.
    717722 * @param string     $output   Constant OBJECT, ARRAY_A, or ARRAY_N
    718723 * @param string     $filter   Optional, default is raw or no WordPress defined filter will applied.
    719  * @return object|array|null|WP_Error Term Row from database. Will return null if $term is empty. If taxonomy does not
    720  * exist then WP_Error will be returned.
    721  */
    722 function get_term($term, $taxonomy, $output = OBJECT, $filter = 'raw') {
    723     global $wpdb;
    724 
     724 * @return mixed Type corresponding to `$output` on success or null on failure. When `$output` is `OBJECT`,
     725 *               a WP_Term instance is returned. If taxonomy does not exist then WP_Error will be returned.
     726 */
     727function get_term( $term, $taxonomy = '', $output = OBJECT, $filter = 'raw' ) {
    725728    if ( empty( $term ) ) {
    726729        return new WP_Error( 'invalid_term', __( 'Empty Term' ) );
    727730    }
    728731
    729     if ( ! taxonomy_exists( $taxonomy ) ) {
     732    if ( $taxonomy && ! taxonomy_exists( $taxonomy ) ) {
    730733        return new WP_Error( 'invalid_taxonomy', __( 'Invalid taxonomy' ) );
    731734    }
    732735
    733     if ( is_object($term) && empty($term->filter) ) {
    734         wp_cache_add( $term->term_id, $term, $taxonomy );
     736    if ( $term instanceof WP_Term ) {
    735737        $_term = $term;
     738    } elseif ( is_object( $term ) ) {
     739        if ( empty( $term->filter ) || 'raw' === $term->filter ) {
     740            $_term = sanitize_term( $term, $taxonomy, 'raw' );
     741            $_term = new WP_Term( $_term );
     742        } else {
     743            $_term = WP_Term::get_instance( $term->term_id );
     744        }
    736745    } else {
    737         if ( is_object($term) )
    738             $term = $term->term_id;
    739         if ( !$term = (int) $term )
    740             return null;
    741         if ( ! $_term = wp_cache_get( $term, $taxonomy ) ) {
    742             $_term = $wpdb->get_row( $wpdb->prepare( "SELECT t.*, tt.* FROM $wpdb->terms AS t INNER JOIN $wpdb->term_taxonomy AS tt ON t.term_id = tt.term_id WHERE tt.taxonomy = %s AND t.term_id = %d LIMIT 1", $taxonomy, $term) );
    743             if ( ! $_term )
    744                 return null;
    745             wp_cache_add( $term, $_term, $taxonomy );
    746         }
     746        $_term = WP_Term::get_instance( $term );
     747    }
     748
     749    // If `$taxonomy` was provided, make sure it matches the taxonomy of the located term.
     750    if ( $_term && $taxonomy && $taxonomy !== $_term->taxonomy ) {
     751        // If there are two terms with the same ID, split the other one to a new term.
     752        $new_term_id = _split_shared_term( $_term->term_id, $_term->term_taxonomy_id );
     753
     754        // If no split occurred, this is an invalid request.
     755        if ( $new_term_id === $_term->term_id ) {
     756            return new WP_Error( 'invalid_term', __( 'Empty Term' ) );
     757
     758        // The term has been split. Refetch the term from the proper taxonomy.
     759        } else {
     760            return get_term( $_term->term_id, $taxonomy, $output, $filter );
     761        }
     762    }
     763
     764    if ( ! $_term ) {
     765        return null;
    747766    }
    748767
     
    751770     *
    752771     * @since 2.3.0
    753      *
    754      * @param int|object $_term    Term object or ID.
    755      * @param string     $taxonomy The taxonomy slug.
     772     * @since 4.4.0 `$_term` can now also be a WP_Term object.
     773     *
     774     * @param int|WP_Term $_term    Term object or ID.
     775     * @param string      $taxonomy The taxonomy slug.
    756776     */
    757777    $_term = apply_filters( 'get_term', $_term, $taxonomy );
     
    764784     *
    765785     * @since 2.3.0
    766      *
    767      * @param int|object $_term    Term object or ID.
    768      * @param string     $taxonomy The taxonomy slug.
     786     * @since 4.4.0 `$_term` can now also be a WP_Term object.
     787     *
     788     * @param int|WP_Term $_term    Term object or ID.
     789     * @param string      $taxonomy The taxonomy slug.
    769790     */
    770791    $_term = apply_filters( "get_$taxonomy", $_term, $taxonomy );
    771     $_term = sanitize_term($_term, $taxonomy, $filter);
    772 
    773     if ( $output == OBJECT ) {
    774         return $_term;
    775     } elseif ( $output == ARRAY_A ) {
    776         $__term = get_object_vars($_term);
    777         return $__term;
     792
     793    // Sanitize term, according to the specified filter.
     794    $_term->filter( $filter );
     795
     796    if ( $output == ARRAY_A ) {
     797        return $_term->to_array();
    778798    } elseif ( $output == ARRAY_N ) {
    779         $__term = array_values(get_object_vars($_term));
    780         return $__term;
    781     } else {
    782         return $_term;
    783     }
     799        return array_values( $_term->to_array() );
     800    }
     801
     802    return $_term;
    784803}
    785804
     
    799818 *
    800819 * @since 2.3.0
    801  * @since 4.4.0 `$taxonomy` is optional if `$field` is 'term_taxonomy_id'.
     820 * @since 4.4.0 `$taxonomy` is optional if `$field` is 'term_taxonomy_id'. Converted to return
     821 *              a WP_Term object if `$output` is `OBJECT`.
    802822 *
    803823 * @global wpdb $wpdb WordPress database abstraction object.
     
    809829 * @param string     $output   Constant OBJECT, ARRAY_A, or ARRAY_N
    810830 * @param string     $filter   Optional, default is raw or no WordPress defined filter will applied.
    811  * @return object|array|null|WP_Error|false Term Row from database.
    812  *                                          Will return false if $taxonomy does not exist or $term was not found.
     831 * @return WP_Term|bool WP_Term instance on success. Will return false if `$taxonomy` does not exist
     832 *                      or `$term` was not found.
    813833 */
    814834function get_term_by( $field, $value, $taxonomy = '', $output = OBJECT, $filter = 'raw' ) {
     
    823843
    824844    if ( 'slug' == $field ) {
    825         $field = 't.slug';
     845        $_field = 't.slug';
    826846        $value = sanitize_title($value);
    827847        if ( empty($value) )
     
    830850        // Assume already escaped
    831851        $value = wp_unslash($value);
    832         $field = 't.name';
     852        $_field = 't.name';
    833853    } elseif ( 'term_taxonomy_id' == $field ) {
    834854        $value = (int) $value;
    835         $field = 'tt.term_taxonomy_id';
     855        $_field = 'tt.term_taxonomy_id';
    836856
    837857        // No `taxonomy` clause when searching by 'term_taxonomy_id'.
     
    845865    }
    846866
    847     $term = $wpdb->get_row( $wpdb->prepare( "SELECT t.*, tt.* FROM $wpdb->terms AS t INNER JOIN $wpdb->term_taxonomy AS tt ON t.term_id = tt.term_id WHERE $field = %s $tax_clause LIMIT 1", $value ) );
     867    $term = $wpdb->get_row( $wpdb->prepare( "SELECT t.*, tt.* FROM $wpdb->terms AS t INNER JOIN $wpdb->term_taxonomy AS tt ON t.term_id = tt.term_id WHERE $_field = %s $tax_clause LIMIT 1", $value ) );
    848868    if ( ! $term )
    849869        return false;
     
    854874    }
    855875
    856     wp_cache_add( $term->term_id, $term, $taxonomy );
    857 
    858     /** This filter is documented in wp-includes/taxonomy-functions.php */
    859     $term = apply_filters( 'get_term', $term, $taxonomy );
    860 
    861     /** This filter is documented in wp-includes/taxonomy-functions.php */
    862     $term = apply_filters( "get_$taxonomy", $term, $taxonomy );
    863 
    864     $term = sanitize_term($term, $taxonomy, $filter);
    865 
    866     if ( $output == OBJECT ) {
    867         return $term;
    868     } elseif ( $output == ARRAY_A ) {
    869         return get_object_vars($term);
    870     } elseif ( $output == ARRAY_N ) {
    871         return array_values(get_object_vars($term));
    872     } else {
    873         return $term;
    874     }
     876    wp_cache_add( $term->term_id, $term, 'terms' );
     877
     878    return get_term( $term, $taxonomy, $output, $filter );
    875879}
    876880
     
    34233427            $taxonomies[] = $term->taxonomy;
    34243428            $ids[] = $term->term_id;
    3425             wp_cache_delete($term->term_id, $term->taxonomy);
     3429            wp_cache_delete( $term->term_id, 'terms' );
    34263430        }
    34273431        $taxonomies = array_unique($taxonomies);
     
    34303434        foreach ( $taxonomies as $taxonomy ) {
    34313435            foreach ( $ids as $id ) {
    3432                 wp_cache_delete($id, $taxonomy);
     3436                wp_cache_delete( $id, 'terms' );
    34333437            }
    34343438        }
     
    35533557            $term_taxonomy = $term->taxonomy;
    35543558
    3555         wp_cache_add( $term->term_id, $term, $term_taxonomy );
     3559        wp_cache_add( $term->term_id, $term, 'terms' );
    35563560    }
    35573561}
  • trunk/src/wp-includes/taxonomy.php

    r34404 r34997  
    1111require_once( ABSPATH . WPINC . '/taxonomy-functions.php' );
    1212
     13/** WP_Term class */
     14require_once( ABSPATH . WPINC . '/class-wp-term.php' );
     15
    1316/** WP_Tax_Query class */
    1417require_once( ABSPATH . WPINC . '/class-wp-tax-query.php' );
  • trunk/tests/phpunit/tests/term/cache.php

    r30954 r34997  
    104104
    105105        $term_object = get_term( $term, 'wptests_tax' );
    106         wp_cache_delete( $term, 'wptests_tax' );
     106        wp_cache_delete( $term, 'terms' );
    107107
    108108        // Affirm that the cache is empty.
    109         $this->assertEmpty( wp_cache_get( $term, 'wptests_tax' ) );
     109        $this->assertEmpty( wp_cache_get( $term, 'terms' ) );
    110110
    111111        $num_queries = $wpdb->num_queries;
     
    129129        ) );
    130130
    131         wp_cache_delete( $term, 'wptests_tax' );
     131        wp_cache_delete( $term, 'terms' );
    132132
    133133        // Affirm that the cache is empty.
    134         $this->assertEmpty( wp_cache_get( $term, 'wptests_tax' ) );
     134        $this->assertEmpty( wp_cache_get( $term, 'terms' ) );
    135135
    136136        $num_queries = $wpdb->num_queries;
     
    138138        // Prime cache.
    139139        $term_object = get_term( $term, 'wptests_tax' );
    140         $this->assertNotEmpty( wp_cache_get( $term, 'wptests_tax' ) );
     140        $this->assertNotEmpty( wp_cache_get( $term, 'terms' ) );
    141141        $this->assertSame( $num_queries + 1, $wpdb->num_queries );
    142142
     
    156156        ) );
    157157
    158         wp_cache_delete( $term, 'wptests_tax' );
     158        wp_cache_delete( $term, 'terms' );
    159159
    160160        // Affirm that the cache is empty.
    161         $this->assertEmpty( wp_cache_get( $term, 'wptests_tax' ) );
     161        $this->assertEmpty( wp_cache_get( $term, 'terms' ) );
    162162
    163163        $num_queries = $wpdb->num_queries;
     
    165165        // Prime cache.
    166166        $term_object = get_term_by( 'id', $term, 'wptests_tax' );
    167         $this->assertNotEmpty( wp_cache_get( $term, 'wptests_tax' ) );
     167        $this->assertNotEmpty( wp_cache_get( $term, 'terms' ) );
    168168        $this->assertSame( $num_queries + 1, $wpdb->num_queries );
    169169
  • trunk/tests/phpunit/tests/term/getTerm.php

    r34035 r34997  
    3434
    3535        $this->assertSame( $num_queries, $wpdb->num_queries );
    36     }
    37 
    38     public function test_passing_term_object_should_not_skip_database_query_when_filter_property_is_set() {
    39         global $wpdb;
    40 
    41         $term = $this->factory->term->create_and_get( array( 'taxonomy' => 'wptests_tax' ) );
    42         clean_term_cache( $term->term_id, 'wptests_tax' );
    43 
    44         $num_queries = $wpdb->num_queries;
    45 
    46         $term_a = get_term( $term, 'wptests_tax' );
    47 
    48         $this->assertSame( $num_queries + 1, $wpdb->num_queries );
    4936    }
    5037
     
    9986        $this->assertInternalType( 'object', get_term( $t, 'wptests_tax', 'foo' ) );
    10087    }
     88
     89    /**
     90     * @ticket 14162
     91     */
     92    public function test_numeric_properties_should_be_cast_to_ints() {
     93        global $wpdb;
     94
     95        $t = $this->factory->term->create( array( 'taxonomy' => 'wptests_tax' ) );
     96
     97        // Get raw data from the database.
     98        $term_data = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->terms t JOIN $wpdb->term_taxonomy tt ON ( t.term_id = tt.term_id ) WHERE t.term_id = %d", $t ) );
     99
     100        $found = get_term( $term_data );
     101
     102        $this->assertTrue( $found instanceof WP_Term );
     103        $this->assertInternalType( 'int', $found->term_id );
     104        $this->assertInternalType( 'int', $found->term_taxonomy_id );
     105        $this->assertInternalType( 'int', $found->parent );
     106        $this->assertInternalType( 'int', $found->count );
     107        $this->assertInternalType( 'int', $found->term_group );
     108    }
    101109}
  • trunk/tests/phpunit/tests/term/getTermBy.php

    r34679 r34997  
    6060        $this->assertSame( $t, $found->term_id );
    6161    }
     62
     63    /**
     64     * @ticket 14162
     65     */
     66    public function test_should_prime_term_cache() {
     67        global $wpdb;
     68
     69        register_taxonomy( 'wptests_tax', 'post' );
     70        $t = $this->factory->term->create( array(
     71            'taxonomy' => 'wptests_tax',
     72            'slug' => 'foo',
     73        ) );
     74
     75        clean_term_cache( $t, 'wptests_tax' );
     76
     77        $num_queries = $wpdb->num_queries;
     78        $found = get_term_by( 'slug', 'foo', 'wptests_tax' );
     79        $num_queries++;
     80
     81        $this->assertTrue( $found instanceof WP_Term );
     82        $this->assertSame( $t, $found->term_id );
     83        $this->assertSame( $num_queries, $wpdb->num_queries );
     84
     85        // Calls to `get_term()` should now hit cache.
     86        $found2 = get_term( $t );
     87        $this->assertSame( $t, $found->term_id );
     88        $this->assertSame( $num_queries, $wpdb->num_queries );
     89    }
    6290}
Note: See TracChangeset for help on using the changeset viewer.