Make WordPress Core

Changeset 34997


Ignore:
Timestamp:
10/10/2015 01:58:37 AM (11 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.