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.
Built from https://develop.svn.wordpress.org/trunk@34997


git-svn-id: http://core.svn.wordpress.org/trunk@34962 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This commit is contained in:
Boone Gorges 2015-10-10 01:59:29 +00:00
parent f8a05879e8
commit 623e467712
5 changed files with 265 additions and 70 deletions

View File

@ -319,18 +319,19 @@ function clean_category_cache( $id ) {
* pass to it. This is one of the features with using pass by reference in PHP.
*
* @since 2.3.0
* @since 4.4.0 The `$category` parameter now also accepts a WP_Term object.
* @access private
*
* @param array|object $category Category Row object or array
* @param array|object|WP_Term $category Category Row object or array
*/
function _make_cat_compat( &$category ) {
if ( is_object( $category ) && ! is_wp_error( $category ) ) {
$category->cat_ID = &$category->term_id;
$category->category_count = &$category->count;
$category->category_description = &$category->description;
$category->cat_name = &$category->name;
$category->category_nicename = &$category->slug;
$category->category_parent = &$category->parent;
$category->cat_ID = $category->term_id;
$category->category_count = $category->count;
$category->category_description = $category->description;
$category->cat_name = $category->name;
$category->category_nicename = $category->slug;
$category->category_parent = $category->parent;
} elseif ( is_array( $category ) && isset( $category['term_id'] ) ) {
$category['cat_ID'] = &$category['term_id'];
$category['category_count'] = &$category['count'];

View File

@ -0,0 +1,187 @@
<?php
/**
* Taxonomy API: WP_Term class
*
* @package WordPress
* @subpackage Taxonomy
* @since 4.4.0
*/
/**
* Core class used to implement the WP_Term object.
*
* @since 4.4.0
*/
final class WP_Term {
/**
* Term ID.
*
* @since 4.4.0
* @access public
* @var int
*/
public $term_id;
/**
* The term's name.
*
* @since 4.4.0
* @access public
* @var string
*/
public $name = '';
/**
* The term's slug.
*
* @since 4.4.0
* @access public
* @var string
*/
public $slug = '';
/**
* The term's term_group.
*
* @since 4.4.0
* @access public
* @var string
*/
public $term_group = '';
/**
* Term Taxonomy ID.
*
* @since 4.4.0
* @access public
* @var int
*/
public $term_taxonomy_id = 0;
/**
* The term's taxonomy name.
*
* @since 4.4.0
* @access public
* @var string
*/
public $taxonomy = '';
/**
* The term's description.
*
* @since 4.4.0
* @access public
* @var string
*/
public $description = '';
/**
* ID of a term's parent term.
*
* @since 4.4.0
* @access public
* @var int
*/
public $parent = 0;
/**
* Cached object count for this term.
*
* @since 4.4.0
* @access public
* @var int
*/
public $count = 0;
/**
* Stores the term object's sanitization level.
*
* Does not correspond to a database field.
*
* @since 4.4.0
* @access public
* @var string
*/
public $filter = 'raw';
/**
* Retrieve WP_Term instance.
*
* @since 4.4.0
* @access public
* @static
*
* @global wpdb $wpdb WordPress database abstraction object.
*
* @param int $term_id Term ID.
* @return WP_Term|false Term object, false otherwise.
*/
public static function get_instance( $term_id ) {
global $wpdb;
$term_id = (int) $term_id;
if ( ! $term_id ) {
return false;
}
$_term = wp_cache_get( $term_id, 'terms' );
// If there isn't a cached version, hit the database.
if ( ! $_term ) {
$_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 t.term_id = %d LIMIT 1", $term_id ) );
if ( ! $_term ) {
return false;
}
$_term = sanitize_term( $_term, $_term->taxonomy, 'raw' );
wp_cache_add( $term_id, $_term, 'terms' );
}
return new WP_Term( $_term );
}
/**
* Constructor.
*
* @since 4.4.0
* @access public
*
* @param WP_Term|object $term Term object.
*/
public function __construct( $term ) {
foreach ( get_object_vars( $term ) as $key => $value ) {
$this->$key = $value;
}
}
/**
* Sanitizes term fields, according to the filter type provided.
*
* @since 4.4.0
* @access public
*
* @param string $filter Filter context. Accepts 'edit', 'db', 'display', 'attribute', 'js', 'raw'.
*/
public function filter( $filter ) {
// Term has already been filtered - nothing more to do.
if ( isset( $this->filter ) && $this->filter === $filter ) {
return;
}
sanitize_term( $this, $this->taxonomy, $filter );
}
/**
* Converts an object to array.
*
* @since 4.4.0
* @access public
*
* @return array Object as array.
*/
public function to_array() {
return get_object_vars( $this );
}
}

View File

@ -708,51 +708,71 @@ function get_tax_sql( $tax_query, $primary_table, $primary_id_column ) {
* @todo Better formatting for DocBlock
*
* @since 2.3.0
* @since 4.4.0 Converted to return a WP_Term object if `$output` is `OBJECT`.
* The `$taxonomy` parameter was made optional.
*
* @global wpdb $wpdb WordPress database abstraction object.
* @see sanitize_term_field() The $context param lists the available values for get_term_by() $filter param.
*
* @param int|object $term If integer, will get from database. If object will apply filters and return $term.
* @param string $taxonomy Taxonomy name that $term is part of.
* @param int|WP_Term|object $term If integer, term data will be fetched from the database, or from the cache if
* available. If stdClass object (as in the results of a database query), will apply
* filters and return a `WP_Term` object corresponding to the `$term` data. If `WP_Term`,
* will return `$term`.
* @param string $taxonomy Optional. Taxonomy name that $term is part of.
* @param string $output Constant OBJECT, ARRAY_A, or ARRAY_N
* @param string $filter Optional, default is raw or no WordPress defined filter will applied.
* @return object|array|null|WP_Error Term Row from database. Will return null if $term is empty. If taxonomy does not
* exist then WP_Error will be returned.
* @return mixed Type corresponding to `$output` on success or null on failure. When `$output` is `OBJECT`,
* a WP_Term instance is returned. If taxonomy does not exist then WP_Error will be returned.
*/
function get_term($term, $taxonomy, $output = OBJECT, $filter = 'raw') {
global $wpdb;
function get_term( $term, $taxonomy = '', $output = OBJECT, $filter = 'raw' ) {
if ( empty( $term ) ) {
return new WP_Error( 'invalid_term', __( 'Empty Term' ) );
}
if ( ! taxonomy_exists( $taxonomy ) ) {
if ( $taxonomy && ! taxonomy_exists( $taxonomy ) ) {
return new WP_Error( 'invalid_taxonomy', __( 'Invalid taxonomy' ) );
}
if ( is_object($term) && empty($term->filter) ) {
wp_cache_add( $term->term_id, $term, $taxonomy );
if ( $term instanceof WP_Term ) {
$_term = $term;
} else {
if ( is_object($term) )
$term = $term->term_id;
if ( !$term = (int) $term )
return null;
if ( ! $_term = wp_cache_get( $term, $taxonomy ) ) {
$_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) );
if ( ! $_term )
return null;
wp_cache_add( $term, $_term, $taxonomy );
} elseif ( is_object( $term ) ) {
if ( empty( $term->filter ) || 'raw' === $term->filter ) {
$_term = sanitize_term( $term, $taxonomy, 'raw' );
$_term = new WP_Term( $_term );
} else {
$_term = WP_Term::get_instance( $term->term_id );
}
} else {
$_term = WP_Term::get_instance( $term );
}
// If `$taxonomy` was provided, make sure it matches the taxonomy of the located term.
if ( $_term && $taxonomy && $taxonomy !== $_term->taxonomy ) {
// If there are two terms with the same ID, split the other one to a new term.
$new_term_id = _split_shared_term( $_term->term_id, $_term->term_taxonomy_id );
// If no split occurred, this is an invalid request.
if ( $new_term_id === $_term->term_id ) {
return new WP_Error( 'invalid_term', __( 'Empty Term' ) );
// The term has been split. Refetch the term from the proper taxonomy.
} else {
return get_term( $_term->term_id, $taxonomy, $output, $filter );
}
}
if ( ! $_term ) {
return null;
}
/**
* Filter a term.
*
* @since 2.3.0
* @since 4.4.0 `$_term` can now also be a WP_Term object.
*
* @param int|object $_term Term object or ID.
* @param string $taxonomy The taxonomy slug.
* @param int|WP_Term $_term Term object or ID.
* @param string $taxonomy The taxonomy slug.
*/
$_term = apply_filters( 'get_term', $_term, $taxonomy );
@ -763,24 +783,23 @@ function get_term($term, $taxonomy, $output = OBJECT, $filter = 'raw') {
* to the taxonomy slug.
*
* @since 2.3.0
* @since 4.4.0 `$_term` can now also be a WP_Term object.
*
* @param int|object $_term Term object or ID.
* @param string $taxonomy The taxonomy slug.
* @param int|WP_Term $_term Term object or ID.
* @param string $taxonomy The taxonomy slug.
*/
$_term = apply_filters( "get_$taxonomy", $_term, $taxonomy );
$_term = sanitize_term($_term, $taxonomy, $filter);
if ( $output == OBJECT ) {
return $_term;
} elseif ( $output == ARRAY_A ) {
$__term = get_object_vars($_term);
return $__term;
// Sanitize term, according to the specified filter.
$_term->filter( $filter );
if ( $output == ARRAY_A ) {
return $_term->to_array();
} elseif ( $output == ARRAY_N ) {
$__term = array_values(get_object_vars($_term));
return $__term;
} else {
return $_term;
return array_values( $_term->to_array() );
}
return $_term;
}
/**
@ -798,7 +817,8 @@ function get_term($term, $taxonomy, $output = OBJECT, $filter = 'raw') {
* @todo Better formatting for DocBlock.
*
* @since 2.3.0
* @since 4.4.0 `$taxonomy` is optional if `$field` is 'term_taxonomy_id'.
* @since 4.4.0 `$taxonomy` is optional if `$field` is 'term_taxonomy_id'. Converted to return
* a WP_Term object if `$output` is `OBJECT`.
*
* @global wpdb $wpdb WordPress database abstraction object.
* @see sanitize_term_field() The $context param lists the available values for get_term_by() $filter param.
@ -808,8 +828,8 @@ function get_term($term, $taxonomy, $output = OBJECT, $filter = 'raw') {
* @param string $taxonomy Taxonomy name. Optional, if `$field` is 'term_taxonomy_id'.
* @param string $output Constant OBJECT, ARRAY_A, or ARRAY_N
* @param string $filter Optional, default is raw or no WordPress defined filter will applied.
* @return object|array|null|WP_Error|false Term Row from database.
* Will return false if $taxonomy does not exist or $term was not found.
* @return WP_Term|bool WP_Term instance on success. Will return false if `$taxonomy` does not exist
* or `$term` was not found.
*/
function get_term_by( $field, $value, $taxonomy = '', $output = OBJECT, $filter = 'raw' ) {
global $wpdb;
@ -822,17 +842,17 @@ function get_term_by( $field, $value, $taxonomy = '', $output = OBJECT, $filter
$tax_clause = $wpdb->prepare( "AND tt.taxonomy = %s", $taxonomy );
if ( 'slug' == $field ) {
$field = 't.slug';
$_field = 't.slug';
$value = sanitize_title($value);
if ( empty($value) )
return false;
} elseif ( 'name' == $field ) {
// Assume already escaped
$value = wp_unslash($value);
$field = 't.name';
$_field = 't.name';
} elseif ( 'term_taxonomy_id' == $field ) {
$value = (int) $value;
$field = 'tt.term_taxonomy_id';
$_field = 'tt.term_taxonomy_id';
// No `taxonomy` clause when searching by 'term_taxonomy_id'.
$tax_clause = '';
@ -844,7 +864,7 @@ function get_term_by( $field, $value, $taxonomy = '', $output = OBJECT, $filter
return $term;
}
$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 ) );
$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 ) );
if ( ! $term )
return false;
@ -853,25 +873,9 @@ function get_term_by( $field, $value, $taxonomy = '', $output = OBJECT, $filter
$taxonomy = $term->taxonomy;
}
wp_cache_add( $term->term_id, $term, $taxonomy );
wp_cache_add( $term->term_id, $term, 'terms' );
/** This filter is documented in wp-includes/taxonomy-functions.php */
$term = apply_filters( 'get_term', $term, $taxonomy );
/** This filter is documented in wp-includes/taxonomy-functions.php */
$term = apply_filters( "get_$taxonomy", $term, $taxonomy );
$term = sanitize_term($term, $taxonomy, $filter);
if ( $output == OBJECT ) {
return $term;
} elseif ( $output == ARRAY_A ) {
return get_object_vars($term);
} elseif ( $output == ARRAY_N ) {
return array_values(get_object_vars($term));
} else {
return $term;
}
return get_term( $term, $taxonomy, $output, $filter );
}
/**
@ -3422,14 +3426,14 @@ function clean_term_cache($ids, $taxonomy = '', $clean_taxonomy = true) {
foreach ( (array) $terms as $term ) {
$taxonomies[] = $term->taxonomy;
$ids[] = $term->term_id;
wp_cache_delete($term->term_id, $term->taxonomy);
wp_cache_delete( $term->term_id, 'terms' );
}
$taxonomies = array_unique($taxonomies);
} else {
$taxonomies = array($taxonomy);
foreach ( $taxonomies as $taxonomy ) {
foreach ( $ids as $id ) {
wp_cache_delete($id, $taxonomy);
wp_cache_delete( $id, 'terms' );
}
}
}
@ -3552,7 +3556,7 @@ function update_term_cache( $terms, $taxonomy = '' ) {
if ( empty($term_taxonomy) )
$term_taxonomy = $term->taxonomy;
wp_cache_add( $term->term_id, $term, $term_taxonomy );
wp_cache_add( $term->term_id, $term, 'terms' );
}
}

View File

@ -10,5 +10,8 @@
/** Core taxonomy functionality */
require_once( ABSPATH . WPINC . '/taxonomy-functions.php' );
/** WP_Term class */
require_once( ABSPATH . WPINC . '/class-wp-term.php' );
/** WP_Tax_Query class */
require_once( ABSPATH . WPINC . '/class-wp-tax-query.php' );

View File

@ -4,7 +4,7 @@
*
* @global string $wp_version
*/
$wp_version = '4.4-alpha-34996';
$wp_version = '4.4-alpha-34997';
/**
* Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.