Make WordPress Core

Ticket #35381: 35381.2.diff

File 35381.2.diff, 48.1 KB (added by boonebgorges, 8 years ago)
  • new file src/wp-includes/class-wp-term-query.php

    diff --git src/wp-includes/class-wp-term-query.php src/wp-includes/class-wp-term-query.php
    new file mode 100644
    index 0000000..38fac79
    - +  
     1<?php
     2
     3/**
     4 * Taxonomy API: WP_Term_Query class
     5 *
     6 * @package WordPress
     7 * @subpackage Taxonomy
     8 * @since 4.6.0
     9 */
     10
     11/**
     12 * Core class used for querying terms.
     13 *
     14 * @since 4.6.0
     15 *
     16 * @see WP_Term_Query::__construct() for accepted arguments.
     17 */
     18class WP_Term_Query {
     19
     20        /**
     21         * SQL for database query.
     22         *
     23         * @since 4.6.0
     24         * @access public
     25         * @var string
     26         */
     27        public $request;
     28
     29        /**
     30         * Metadata query container
     31         *
     32         * @since 4.6.0
     33         * @access public
     34         * @var object WP_Meta_Query
     35         */
     36        public $meta_query = false;
     37
     38        /**
     39         * Metadata query clauses.
     40         *
     41         * @since 4.6.0
     42         * @access protected
     43         * @var array
     44         */
     45        protected $meta_query_clauses;
     46
     47        /**
     48         * SQL query clauses.
     49         *
     50         * @since 4.6.0
     51         * @access protected
     52         * @var array
     53         */
     54        protected $sql_clauses = array(
     55                'select'  => '',
     56                'from'    => '',
     57                'where'   => array(),
     58                'orderby' => '',
     59                'limits'  => '',
     60        );
     61
     62        /**
     63         * Query vars set by the user.
     64         *
     65         * @since 4.6.0
     66         * @access public
     67         * @var array
     68         */
     69        public $query_vars;
     70
     71        /**
     72         * Default values for query vars.
     73         *
     74         * @since 4.6.0
     75         * @access public
     76         * @var array
     77         */
     78        public $query_var_defaults;
     79
     80        /**
     81         * List of terms located by the query.
     82         *
     83         * @since 4.6.0
     84         * @access public
     85         * @var array
     86         */
     87        public $terms;
     88
     89        /**
     90         * Constructor.
     91         *
     92         * Sets up the term query, based on the query vars passed.
     93         *
     94         * @since 4.6.0
     95         * @access public
     96         *
     97         * @param string|array $query {
     98         *     Optional. Array or query string of term query parameters. Default empty.
     99         *
     100         *     @type string|array $taxonomy               Taxonomy name, or array of taxonomies, to which results should
     101         *                                                be limited.
     102         *     @type string       $orderby                Field(s) to order terms by. Accepts term fields ('name', 'slug',
     103         *                                                'term_group', 'term_id', 'id', 'description'), 'count' for term
     104         *                                                taxonomy count, 'include' to match the 'order' of the $include param,
     105         *                                                'meta_value', 'meta_value_num', the value of `$meta_key`, the array
     106         *                                                keys of `$meta_query`, or 'none' to omit the ORDER BY clause.
     107         *                                                Defaults to 'name'.
     108         *     @type string       $order                  Whether to order terms in ascending or descending order.
     109         *                                                Accepts 'ASC' (ascending) or 'DESC' (descending).
     110         *                                                Default 'ASC'.
     111         *     @type bool|int     $hide_empty             Whether to hide terms not assigned to any posts. Accepts
     112         *                                                1|true or 0|false. Default 1|true.
     113         *     @type array|string $include                Array or comma/space-separated string of term ids to include.
     114         *                                                Default empty array.
     115         *     @type array|string $exclude                Array or comma/space-separated string of term ids to exclude.
     116         *                                                If $include is non-empty, $exclude is ignored.
     117         *                                                Default empty array.
     118         *     @type array|string $exclude_tree           Array or comma/space-separated string of term ids to exclude
     119         *                                                along with all of their descendant terms. If $include is
     120         *                                                non-empty, $exclude_tree is ignored. Default empty array.
     121         *     @type int|string   $number                 Maximum number of terms to return. Accepts ''|0 (all) or any
     122         *                                                positive number. Default ''|0 (all).
     123         *     @type int          $offset                 The number by which to offset the terms query. Default empty.
     124         *     @type string       $fields                 Term fields to query for. Accepts 'all' (returns an array of complete
     125         *                                                term objects), 'ids' (returns an array of ids), 'id=>parent' (returns
     126         *                                                an associative array with ids as keys, parent term IDs as values),
     127         *                                                'names' (returns an array of term names), 'count' (returns the number
     128         *                                                of matching terms), 'id=>name' (returns an associative array with ids
     129         *                                                as keys, term names as values), or 'id=>slug' (returns an associative
     130         *                                                array with ids as keys, term slugs as values). Default 'all'.
     131         *     @type bool         $count                  Whether to return a term count (true) or array of term objects (false).
     132         *                                                Will take precedence over `$fields` if true. Default false.
     133         *     @type string|array $name                   Optional. Name or array of names to return term(s) for. Default empty.
     134         *     @type string|array $slug                   Optional. Slug or array of slugs to return term(s) for. Default empty.
     135         *     @type bool         $hierarchical           Whether to include terms that have non-empty descendants (even
     136         *                                                if $hide_empty is set to true). Default true.
     137         *     @type string       $search                 Search criteria to match terms. Will be SQL-formatted with
     138         *                                                wildcards before and after. Default empty.
     139         *     @type string       $name__like             Retrieve terms with criteria by which a term is LIKE $name__like.
     140         *                                                Default empty.
     141         *     @type string       $description__like      Retrieve terms where the description is LIKE $description__like.
     142         *                                                Default empty.
     143         *     @type bool         $pad_counts             Whether to pad the quantity of a term's children in the quantity
     144         *                                                of each term's "count" object variable. Default false.
     145         *     @type string       $get                    Whether to return terms regardless of ancestry or whether the terms
     146         *                                                are empty. Accepts 'all' or empty (disabled). Default empty.
     147         *     @type int          $child_of               Term ID to retrieve child terms of. If multiple taxonomies
     148         *                                                are passed, $child_of is ignored. Default 0.
     149         *     @type int|string   $parent                 Parent term ID to retrieve direct-child terms of. Default empty.
     150         *     @type bool         $childless              True to limit results to terms that have no children. This parameter
     151         *                                                has no effect on non-hierarchical taxonomies. Default false.
     152         *     @type string       $cache_domain           Unique cache key to be produced when this query is stored in an
     153         *                                                object cache. Default is 'core'.
     154         *     @type bool         $update_term_meta_cache Whether to prime meta caches for matched terms. Default true.
     155         *     @type array        $meta_query             Meta query clauses to limit retrieved terms by.
     156         *                                                See `WP_Meta_Query`. Default empty.
     157         *     @type string       $meta_key               Limit terms to those matching a specific metadata key. Can be used in
     158         *                                                conjunction with `$meta_value`.
     159         *     @type string       $meta_value             Limit terms to those matching a specific metadata value. Usually used
     160         *                                                in conjunction with `$meta_key`.
     161         * }
     162         */
     163        public function __construct( $query = '' ) {
     164                $this->query_var_defaults = array(
     165                        'taxonomy'               => null,
     166                        'orderby'                => 'name',
     167                        'order'                  => 'ASC',
     168                        'hide_empty'             => true,
     169                        'include'                => array(),
     170                        'exclude'                => array(),
     171                        'exclude_tree'           => array(),
     172                        'number'                 => '',
     173                        'offset'                 => '',
     174                        'fields'                 => 'all',
     175                        'count'                  => false,
     176                        'name'                   => '',
     177                        'slug'                   => '',
     178                        'hierarchical'           => true,
     179                        'search'                 => '',
     180                        'name__like'             => '',
     181                        'description__like'      => '',
     182                        'pad_counts'             => false,
     183                        'get'                    => '',
     184                        'child_of'               => 0,
     185                        'parent'                 => '',
     186                        'childless'              => false,
     187                        'cache_domain'           => 'core',
     188                        'update_term_meta_cache' => true,
     189                        'meta_query'             => ''
     190                );
     191
     192                if ( ! empty( $query ) ) {
     193                        $this->query( $query );
     194                }
     195        }
     196
     197        /**
     198         * Parse arguments passed to the term query with default query parameters.
     199         *
     200         * @since 4.6.0
     201         * @access public
     202         *
     203         * @param string|array $query WP_Term_Query arguments. See WP_Term_Query::__construct()
     204         */
     205        public function parse_query( $query = '' ) {
     206                if ( empty( $query ) ) {
     207                        $query = $this->query_vars;
     208                }
     209
     210                $taxonomies = isset( $query['taxonomy'] ) ? $query['taxonomy'] : null;
     211
     212                /**
     213                 * Filters the terms query default arguments.
     214                 *
     215                 * Use 'get_terms_args' to filter the passed arguments.
     216                 *
     217                 * @since 4.4.0
     218                 *
     219                 * @param array $defaults   An array of default get_terms() arguments.
     220                 * @param array $taxonomies An array of taxonomies.
     221                 */
     222                $this->query_var_defaults = apply_filters( 'get_terms_defaults', $this->query_var_defaults, $taxonomies );
     223
     224                $query = wp_parse_args( $query, $this->query_var_defaults );
     225
     226                $query['number'] = absint( $query['number'] );
     227                $query['offset'] = absint( $query['offset'] );
     228
     229                // 'parent' overrides 'child_of'.
     230                if ( 0 < intval( $query['parent'] ) ) {
     231                        $query['child_of'] = false;
     232                }
     233
     234                if ( 'all' == $query['get'] ) {
     235                        $query['childless'] = false;
     236                        $query['child_of'] = 0;
     237                        $query['hide_empty'] = 0;
     238                        $query['hierarchical'] = false;
     239                        $query['pad_counts'] = false;
     240                }
     241
     242                $query['taxonomy'] = $taxonomies;
     243
     244                /**
     245                 * Filters the terms query arguments.
     246                 *
     247                 * @since 3.1.0
     248                 *
     249                 * @param array $args       An array of get_terms() arguments.
     250                 * @param array $taxonomies An array of taxonomies.
     251                 */
     252                $this->query_vars = apply_filters( 'get_terms_args', $query, $taxonomies );
     253
     254                /**
     255                 * Fires after term query vars have been parsed.
     256                 *
     257                 * @since 4.6.0
     258                 *
     259                 * @param WP_Term_Query $this Current instance of WP_Term_Query.
     260                 */
     261                do_action( 'parse_term_query', $this );
     262        }
     263
     264        /**
     265         * Sets up the query for retrieving terms.
     266         *
     267         * @since 4.6.0
     268         * @access public
     269         *
     270         * @param string|array $query Array or URL query string of parameters.
     271         * @return array|int List of terms, or number of terms when 'count' is passed as a query var.
     272         */
     273        public function query( $query ) {
     274                $this->query_vars = wp_parse_args( $query );
     275                return $this->get_terms();
     276        }
     277
     278        /**
     279         * Get terms, based on query_vars.
     280         *
     281         * @param 4.6.0
     282         * @access public
     283         *
     284         * @global wpdb $wpdb WordPress database abstraction object.
     285         *
     286         * @return array
     287         */
     288        public function get_terms() {
     289                global $wpdb;
     290
     291                $this->parse_query( $this->query_vars );
     292                $args = $this->query_vars;
     293
     294                // Set up meta_query so it's available to 'pre_get_terms'.
     295                $this->meta_query = new WP_Meta_Query();
     296                $this->meta_query->parse_query_vars( $args );
     297
     298                /**
     299                 * Fires before terms are retrieved.
     300                 *
     301                 * @since 4.6.0
     302                 *
     303                 * @param WP_Term_Query $this Current instance of WP_Term_Query.
     304                 */
     305                do_action( 'pre_get_terms', $this );
     306
     307                $taxonomies = $args['taxonomy'];
     308
     309                // Save queries by not crawling the tree in the case of multiple taxes or a flat tax.
     310                $has_hierarchical_tax = false;
     311                if ( $taxonomies ) {
     312                        foreach ( $taxonomies as $_tax ) {
     313                                if ( is_taxonomy_hierarchical( $_tax ) ) {
     314                                        $has_hierarchical_tax = true;
     315                                }
     316                        }
     317                }
     318
     319                if ( ! $has_hierarchical_tax ) {
     320                        $args['hierarchical'] = false;
     321                        $args['pad_counts'] = false;
     322                }
     323
     324                // 'parent' overrides 'child_of'.
     325                if ( 0 < intval( $args['parent'] ) ) {
     326                        $args['child_of'] = false;
     327                }
     328
     329                if ( 'all' == $args['get'] ) {
     330                        $args['childless'] = false;
     331                        $args['child_of'] = 0;
     332                        $args['hide_empty'] = 0;
     333                        $args['hierarchical'] = false;
     334                        $args['pad_counts'] = false;
     335                }
     336
     337                /**
     338                 * Filters the terms query arguments.
     339                 *
     340                 * @since 3.1.0
     341                 *
     342                 * @param array $args       An array of get_terms() arguments.
     343                 * @param array $taxonomies An array of taxonomies.
     344                 */
     345                $args = apply_filters( 'get_terms_args', $args, $taxonomies );
     346
     347                // Avoid the query if the queried parent/child_of term has no descendants.
     348                $child_of = $args['child_of'];
     349                $parent   = $args['parent'];
     350
     351                if ( $child_of ) {
     352                        $_parent = $child_of;
     353                } elseif ( $parent ) {
     354                        $_parent = $parent;
     355                } else {
     356                        $_parent = false;
     357                }
     358
     359                if ( $_parent ) {
     360                        $in_hierarchy = false;
     361                        foreach ( $taxonomies as $_tax ) {
     362                                $hierarchy = _get_term_hierarchy( $_tax );
     363
     364                                if ( isset( $hierarchy[ $_parent ] ) ) {
     365                                        $in_hierarchy = true;
     366                                }
     367                        }
     368
     369                        if ( ! $in_hierarchy ) {
     370                                return array();
     371                        }
     372                }
     373
     374                $orderby = $this->parse_orderby( $this->query_vars['orderby'] );
     375                $order = $this->parse_order( $this->query_vars['order'] );
     376
     377                $order = strtoupper( $args['order'] );
     378
     379                if ( '' !== $order && ! in_array( $order, array( 'ASC', 'DESC' ) ) ) {
     380                        $order = 'ASC';
     381                }
     382
     383                if ( $taxonomies ) {
     384                        $this->sql_clauses['where']['taxonomy'] = "tt.taxonomy IN ('" . implode("', '", array_map( 'esc_sql', $taxonomies ) ) . "')";
     385                }
     386
     387                $exclude = $args['exclude'];
     388                $exclude_tree = $args['exclude_tree'];
     389                $include = $args['include'];
     390
     391                $inclusions = '';
     392                if ( ! empty( $include ) ) {
     393                        $exclude = '';
     394                        $exclude_tree = '';
     395                        $inclusions = implode( ',', wp_parse_id_list( $include ) );
     396                }
     397
     398                if ( ! empty( $inclusions ) ) {
     399                        $this->sql_clauses['where']['inclusions'] = 't.term_id IN ( ' . $inclusions . ' )';
     400                }
     401
     402                $exclusions = array();
     403                if ( ! empty( $exclude_tree ) ) {
     404                        $exclude_tree = wp_parse_id_list( $exclude_tree );
     405                        $excluded_children = $exclude_tree;
     406                        foreach ( $exclude_tree as $extrunk ) {
     407                                $excluded_children = array_merge(
     408                                        $excluded_children,
     409                                        (array) get_terms( $taxonomies[0], array( 'child_of' => intval( $extrunk ), 'fields' => 'ids', 'hide_empty' => 0 ) )
     410                                );
     411                        }
     412                        $exclusions = array_merge( $excluded_children, $exclusions );
     413                }
     414
     415                if ( ! empty( $exclude ) ) {
     416                        $exclusions = array_merge( wp_parse_id_list( $exclude ), $exclusions );
     417                }
     418
     419                // 'childless' terms are those without an entry in the flattened term hierarchy.
     420                $childless = (bool) $args['childless'];
     421                if ( $childless ) {
     422                        foreach ( $taxonomies as $_tax ) {
     423                                $term_hierarchy = _get_term_hierarchy( $_tax );
     424                                $exclusions = array_merge( array_keys( $term_hierarchy ), $exclusions );
     425                        }
     426                }
     427
     428                if ( ! empty( $exclusions ) ) {
     429                        $exclusions = 't.term_id NOT IN (' . implode( ',', array_map( 'intval', $exclusions ) ) . ')';
     430                } else {
     431                        $exclusions = '';
     432                }
     433
     434                /**
     435                 * Filters the terms to exclude from the terms query.
     436                 *
     437                 * @since 2.3.0
     438                 *
     439                 * @param string $exclusions `NOT IN` clause of the terms query.
     440                 * @param array  $args       An array of terms query arguments.
     441                 * @param array  $taxonomies An array of taxonomies.
     442                 */
     443                $exclusions = apply_filters( 'list_terms_exclusions', $exclusions, $args, $taxonomies );
     444
     445                if ( ! empty( $exclusions ) ) {
     446                        // Must do string manipulation here for backward compatibility with filter.
     447                        $this->sql_clauses['where']['exclusions'] = preg_replace( '/^\s*AND\s*/', '', $exclusions );
     448                }
     449
     450                if ( ! empty( $args['name'] ) ) {
     451                        $names = (array) $args['name'];
     452                        foreach ( $names as &$_name ) {
     453                                // `sanitize_term_field()` returns slashed data.
     454                                $_name = stripslashes( sanitize_term_field( 'name', $_name, 0, reset( $taxonomies ), 'db' ) );
     455                        }
     456
     457                        $this->sql_clauses['where']['name'] = "t.name IN ('" . implode( "', '", array_map( 'esc_sql', $names ) ) . "')";
     458                }
     459
     460                if ( ! empty( $args['slug'] ) ) {
     461                        if ( is_array( $args['slug'] ) ) {
     462                                $slug = array_map( 'sanitize_title', $args['slug'] );
     463                                $this->sql_clauses['where']['slug'] = "t.slug IN ('" . implode( "', '", $slug ) . "')";
     464                        } else {
     465                                $slug = sanitize_title( $args['slug'] );
     466                                $this->sql_clauses['where']['slug'] = "t.slug = '$slug'";
     467                        }
     468                }
     469
     470                if ( ! empty( $args['name__like'] ) ) {
     471                        $this->sql_clauses['where']['name__like'] = $wpdb->prepare( "t.name LIKE %s", '%' . $wpdb->esc_like( $args['name__like'] ) . '%' );
     472                }
     473
     474                if ( ! empty( $args['description__like'] ) ) {
     475                        $this->sql_clauses['where']['description__like'] = $wpdb->prepare( "tt.description LIKE %s", '%' . $wpdb->esc_like( $args['description__like'] ) . '%' );
     476                }
     477
     478                if ( '' !== $parent ) {
     479                        $parent = (int) $parent;
     480                        $this->sql_clauses['where']['parent'] = "tt.parent = '$parent'";
     481                }
     482
     483                $hierarchical = $args['hierarchical'];
     484                if ( 'count' == $args['fields'] ) {
     485                        $hierarchical = false;
     486                }
     487                if ( $args['hide_empty'] && !$hierarchical ) {
     488                        $this->sql_clauses['where']['count'] = 'tt.count > 0';
     489                }
     490
     491                $number = $args['number'];
     492                $offset = $args['offset'];
     493
     494                // Don't limit the query results when we have to descend the family tree.
     495                if ( $number && ! $hierarchical && ! $child_of && '' === $parent ) {
     496                        if ( $offset ) {
     497                                $limits = 'LIMIT ' . $offset . ',' . $number;
     498                        } else {
     499                                $limits = 'LIMIT ' . $number;
     500                        }
     501                } else {
     502                        $limits = '';
     503                }
     504
     505
     506                if ( ! empty( $args['search'] ) ) {
     507                        $this->sql_clauses['where']['search'] = $this->get_search_sql( $args['search'] );
     508                }
     509
     510                // Meta query support.
     511                $join = '';
     512                $distinct = '';
     513
     514                // Reparse meta_query query_vars, in case they were modified in a 'pre_get_terms' callback.
     515                $this->meta_query->parse_query_vars( $this->query_vars );
     516                $mq_sql = $this->meta_query->get_sql( 'term', 't', 'term_id' );
     517                $meta_clauses = $this->meta_query->get_clauses();
     518
     519                if ( ! empty( $meta_clauses ) ) {
     520                        $join .= $mq_sql['join'];
     521                        $this->sql_clauses['where']['meta_query'] = preg_replace( '/^\s*AND\s*/', '', $mq_sql['where'] );
     522                        $distinct .= "DISTINCT";
     523
     524                }
     525
     526                $selects = array();
     527                switch ( $args['fields'] ) {
     528                        case 'all':
     529                                $selects = array( 't.*', 'tt.*' );
     530                                break;
     531                        case 'ids':
     532                        case 'id=>parent':
     533                                $selects = array( 't.term_id', 'tt.parent', 'tt.count', 'tt.taxonomy' );
     534                                break;
     535                        case 'names':
     536                                $selects = array( 't.term_id', 'tt.parent', 'tt.count', 't.name', 'tt.taxonomy' );
     537                                break;
     538                        case 'count':
     539                                $orderby = '';
     540                                $order = '';
     541                                $selects = array( 'COUNT(*)' );
     542                                break;
     543                        case 'id=>name':
     544                                $selects = array( 't.term_id', 't.name', 'tt.count', 'tt.taxonomy' );
     545                                break;
     546                        case 'id=>slug':
     547                                $selects = array( 't.term_id', 't.slug', 'tt.count', 'tt.taxonomy' );
     548                                break;
     549                }
     550
     551                $_fields = $args['fields'];
     552
     553                /**
     554                 * Filters the fields to select in the terms query.
     555                 *
     556                 * Field lists modified using this filter will only modify the term fields returned
     557                 * by the function when the `$fields` parameter set to 'count' or 'all'. In all other
     558                 * cases, the term fields in the results array will be determined by the `$fields`
     559                 * parameter alone.
     560                 *
     561                 * Use of this filter can result in unpredictable behavior, and is not recommended.
     562                 *
     563                 * @since 2.8.0
     564                 *
     565                 * @param array $selects    An array of fields to select for the terms query.
     566                 * @param array $args       An array of term query arguments.
     567                 * @param array $taxonomies An array of taxonomies.
     568                 */
     569                $fields = implode( ', ', apply_filters( 'get_terms_fields', $selects, $args, $taxonomies ) );
     570
     571                $join .= " INNER JOIN $wpdb->term_taxonomy AS tt ON t.term_id = tt.term_id";
     572
     573                $where = implode( ' AND ', $this->sql_clauses['where'] );
     574
     575                $pieces = array( 'fields', 'join', 'where', 'distinct', 'orderby', 'order', 'limits' );
     576
     577                /**
     578                 * Filters the terms query SQL clauses.
     579                 *
     580                 * @since 3.1.0
     581                 *
     582                 * @param array $pieces     Terms query SQL clauses.
     583                 * @param array $taxonomies An array of taxonomies.
     584                 * @param array $args       An array of terms query arguments.
     585                 */
     586                $clauses = apply_filters( 'terms_clauses', compact( $pieces ), $taxonomies, $args );
     587
     588                $fields = isset( $clauses[ 'fields' ] ) ? $clauses[ 'fields' ] : '';
     589                $join = isset( $clauses[ 'join' ] ) ? $clauses[ 'join' ] : '';
     590                $where = isset( $clauses[ 'where' ] ) ? $clauses[ 'where' ] : '';
     591                $distinct = isset( $clauses[ 'distinct' ] ) ? $clauses[ 'distinct' ] : '';
     592                $orderby = isset( $clauses[ 'orderby' ] ) ? $clauses[ 'orderby' ] : '';
     593                $order = isset( $clauses[ 'order' ] ) ? $clauses[ 'order' ] : '';
     594                $limits = isset( $clauses[ 'limits' ] ) ? $clauses[ 'limits' ] : '';
     595
     596                if ( $where ) {
     597                        $where = "WHERE $where";
     598                }
     599
     600                $this->sql_clauses['select']  = "SELECT $distinct $fields";
     601                $this->sql_clauses['from']    = "FROM $wpdb->terms AS t $join";
     602                $this->sql_clauses['orderby'] = $orderby ? "ORDER BY $orderby $order" : '';
     603                $this->sql_clauses['limits']  = $limits;
     604
     605                $this->request = $this->request = "{$this->sql_clauses['select']} {$this->sql_clauses['from']} {$where} {$this->sql_clauses['orderby']} {$this->sql_clauses['limits']}";
     606
     607                // $args can be anything. Only use the args defined in defaults to compute the key.
     608                $key = md5( serialize( wp_array_slice_assoc( $args, array_keys( $this->query_var_defaults ) ) ) . serialize( $taxonomies ) . $this->request );
     609                $last_changed = wp_cache_get( 'last_changed', 'terms' );
     610                if ( ! $last_changed ) {
     611                        $last_changed = microtime();
     612                        wp_cache_set( 'last_changed', $last_changed, 'terms' );
     613                }
     614                $cache_key = "get_terms:$key:$last_changed";
     615                $cache = wp_cache_get( $cache_key, 'terms' );
     616                if ( false !== $cache ) {
     617                        if ( 'all' === $_fields ) {
     618                                $cache = array_map( 'get_term', $cache );
     619                        }
     620
     621                        return $cache;
     622                }
     623
     624                if ( 'count' == $_fields ) {
     625                        return $wpdb->get_var( $this->request );
     626                }
     627
     628                $terms = $wpdb->get_results( $this->request );
     629                if ( 'all' == $_fields ) {
     630                        update_term_cache( $terms );
     631                }
     632
     633                // Prime termmeta cache.
     634                if ( $args['update_term_meta_cache'] ) {
     635                        $term_ids = wp_list_pluck( $terms, 'term_id' );
     636                        update_termmeta_cache( $term_ids );
     637                }
     638
     639                if ( empty( $terms ) ) {
     640                        wp_cache_add( $cache_key, array(), 'terms', DAY_IN_SECONDS );
     641                        return array();
     642                }
     643
     644                if ( $child_of ) {
     645                        foreach ( $taxonomies as $_tax ) {
     646                                $children = _get_term_hierarchy( $_tax );
     647                                if ( ! empty( $children ) ) {
     648                                        $terms = _get_term_children( $child_of, $terms, $_tax );
     649                                }
     650                        }
     651                }
     652
     653                // Update term counts to include children.
     654                if ( $args['pad_counts'] && 'all' == $_fields ) {
     655                        foreach ( $taxonomies as $_tax ) {
     656                                _pad_term_counts( $terms, $_tax );
     657                        }
     658                }
     659
     660                // Make sure we show empty categories that have children.
     661                if ( $hierarchical && $args['hide_empty'] && is_array( $terms ) ) {
     662                        foreach ( $terms as $k => $term ) {
     663                                if ( ! $term->count ) {
     664                                        $children = get_term_children( $term->term_id, $term->taxonomy );
     665                                        if ( is_array( $children ) ) {
     666                                                foreach ( $children as $child_id ) {
     667                                                        $child = get_term( $child_id, $term->taxonomy );
     668                                                        if ( $child->count ) {
     669                                                                continue 2;
     670                                                        }
     671                                                }
     672                                        }
     673
     674                                        // It really is empty.
     675                                        unset($terms[$k]);
     676                                }
     677                        }
     678                }
     679
     680                $_terms = array();
     681                if ( 'id=>parent' == $_fields ) {
     682                        foreach ( $terms as $term ) {
     683                                $_terms[ $term->term_id ] = $term->parent;
     684                        }
     685                } elseif ( 'ids' == $_fields ) {
     686                        foreach ( $terms as $term ) {
     687                                $_terms[] = $term->term_id;
     688                        }
     689                } elseif ( 'names' == $_fields ) {
     690                        foreach ( $terms as $term ) {
     691                                $_terms[] = $term->name;
     692                        }
     693                } elseif ( 'id=>name' == $_fields ) {
     694                        foreach ( $terms as $term ) {
     695                                $_terms[ $term->term_id ] = $term->name;
     696                        }
     697                } elseif ( 'id=>slug' == $_fields ) {
     698                        foreach ( $terms as $term ) {
     699                                $_terms[ $term->term_id ] = $term->slug;
     700                        }
     701                }
     702
     703                if ( ! empty( $_terms ) ) {
     704                        $terms = $_terms;
     705                }
     706
     707                // Hierarchical queries are not limited, so 'offset' and 'number' must be handled now.
     708                if ( $hierarchical && $number && is_array( $terms ) ) {
     709                        if ( $offset >= count( $terms ) ) {
     710                                $terms = array();
     711                        } else {
     712                                $terms = array_slice( $terms, $offset, $number, true );
     713                        }
     714                }
     715
     716                wp_cache_add( $cache_key, $terms, 'terms', DAY_IN_SECONDS );
     717
     718                if ( 'all' === $_fields ) {
     719                        $terms = array_map( 'get_term', $terms );
     720                }
     721
     722                $this->terms = $terms;
     723                return $this->terms;
     724        }
     725
     726        /**
     727         * Parse and sanitize 'orderby' keys passed to the term query.
     728         *
     729         * @since 4.6.0
     730         * @access protected
     731         *
     732         * @global wpdb $wpdb WordPress database abstraction object.
     733         *
     734         * @param string $orderby_raw Alias for the field to order by.
     735         * @return string|false Value to used in the ORDER clause. False otherwise.
     736         */
     737        protected function parse_orderby( $orderby_raw ) {
     738                $_orderby = strtolower( $orderby_raw );
     739                $maybe_orderby_meta = false;
     740                if ( 'count' == $_orderby ) {
     741                        $orderby = 'tt.count';
     742                } elseif ( 'name' == $_orderby ) {
     743                        $orderby = 't.name';
     744                } elseif ( 'slug' == $_orderby ) {
     745                        $orderby = 't.slug';
     746                } elseif ( 'include' == $_orderby && ! empty( $this->query_vars['include'] ) ) {
     747                        $include = implode( ',', array_map( 'absint', $this->query_vars['include'] ) );
     748                        $orderby = "FIELD( t.term_id, $include )";
     749                } elseif ( 'term_group' == $_orderby ) {
     750                        $orderby = 't.term_group';
     751                } elseif ( 'description' == $_orderby ) {
     752                        $orderby = 'tt.description';
     753                } elseif ( 'none' == $_orderby ) {
     754                        $orderby = '';
     755                } elseif ( empty( $_orderby ) || 'id' == $_orderby || 'term_id' === $_orderby ) {
     756                        $orderby = 't.term_id';
     757                } else {
     758                        $orderby = 't.name';
     759
     760                        // This may be a value of orderby related to meta.
     761                        $maybe_orderby_meta = true;
     762                }
     763
     764                /**
     765                 * Filters the ORDERBY clause of the terms query.
     766                 *
     767                 * @since 2.8.0
     768                 *
     769                 * @param string $orderby    `ORDERBY` clause of the terms query.
     770                 * @param array  $args       An array of terms query arguments.
     771                 * @param array  $taxonomies An array of taxonomies.
     772                 */
     773                $orderby = apply_filters( 'get_terms_orderby', $orderby, $this->query_vars, $this->query_vars['taxonomy'] );
     774
     775                // Run after the 'get_terms_orderby' filter for backward compatibility.
     776                if ( $maybe_orderby_meta ) {
     777                        $maybe_orderby_meta = $this->parse_orderby_meta( $_orderby );
     778                        if ( $maybe_orderby_meta ) {
     779                                $orderby = $maybe_orderby_meta;
     780                        }
     781                }
     782
     783                return $orderby;
     784        }
     785
     786        /**
     787         * Generate the ORDER BY clause for an 'orderby' param that is potentially related to a meta query.
     788         *
     789         * @since 4.6.0
     790         * @access public
     791         *
     792         * @param string $orderby_raw Raw 'orderby' value passed to WP_Term_Query.
     793         * @return string
     794         */
     795        protected function parse_orderby_meta( $orderby_raw ) {
     796                $orderby = '';
     797
     798                $meta_clauses = $this->meta_query->get_clauses();
     799                if ( ! $meta_clauses || ! $orderby_raw ) {
     800                        return $orderby;
     801                }
     802
     803                $allowed_keys = array();
     804                $primary_meta_key = null;
     805                $primary_meta_query = reset( $meta_clauses );
     806                if ( ! empty( $primary_meta_query['key'] ) ) {
     807                        $primary_meta_key = $primary_meta_query['key'];
     808                        $allowed_keys[] = $primary_meta_key;
     809                }
     810                $allowed_keys[] = 'meta_value';
     811                $allowed_keys[] = 'meta_value_num';
     812                $allowed_keys   = array_merge( $allowed_keys, array_keys( $meta_clauses ) );
     813
     814                if ( ! in_array( $orderby_raw, $allowed_keys, true ) ) {
     815                        return $orderby;
     816                }
     817
     818                switch( $orderby_raw ) {
     819                        case $primary_meta_key:
     820                        case 'meta_value':
     821                                if ( ! empty( $primary_meta_query['type'] ) ) {
     822                                        $orderby = "CAST({$primary_meta_query['alias']}.meta_value AS {$primary_meta_query['cast']})";
     823                                } else {
     824                                        $orderby = "{$primary_meta_query['alias']}.meta_value";
     825                                }
     826                                break;
     827
     828                        case 'meta_value_num':
     829                                $orderby = "{$primary_meta_query['alias']}.meta_value+0";
     830                                break;
     831
     832                        default:
     833                                if ( array_key_exists( $orderby_raw, $meta_clauses ) ) {
     834                                        // $orderby corresponds to a meta_query clause.
     835                                        $meta_clause = $meta_clauses[ $orderby_raw ];
     836                                        $orderby = "CAST({$meta_clause['alias']}.meta_value AS {$meta_clause['cast']})";
     837                                }
     838                                break;
     839                }
     840
     841                return $orderby;
     842        }
     843
     844        /**
     845         * Parse an 'order' query variable and cast it to ASC or DESC as necessary.
     846         *
     847         * @since 4.6.0
     848         * @access protected
     849         *
     850         * @param string $order The 'order' query variable.
     851         * @return string The sanitized 'order' query variable.
     852         */
     853        protected function parse_order( $order ) {
     854                if ( ! is_string( $order ) || empty( $order ) ) {
     855                        return 'DESC';
     856                }
     857
     858                if ( 'ASC' === strtoupper( $order ) ) {
     859                        return 'ASC';
     860                } else {
     861                        return 'DESC';
     862                }
     863        }
     864
     865        /**
     866         * Used internally to generate a SQL string related to the 'search' parameter.
     867         *
     868         * @since 4.6.0
     869         * @access protected
     870         *
     871         * @global wpdb $wpdb WordPress database abstraction object.
     872         *
     873         * @param string $string
     874         * @return string
     875         */
     876        protected function get_search_sql( $string ) {
     877                global $wpdb;
     878
     879                $like = '%' . $wpdb->esc_like( $string ) . '%';
     880
     881                return $wpdb->prepare( '((t.name LIKE %s) OR (t.slug LIKE %s))', $like, $like );
     882        }
     883}
  • src/wp-includes/taxonomy.php

    diff --git src/wp-includes/taxonomy.php src/wp-includes/taxonomy.php
    index f2a0169..f5de20a 100644
    function get_term_to_edit( $id, $taxonomy ) { 
    11741174function get_terms( $args = array(), $deprecated = '' ) {
    11751175        global $wpdb;
    11761176
    1177         $defaults = array(
    1178                 'taxonomy'               => null,
    1179                 'orderby'                => 'name',
    1180                 'order'                  => 'ASC',
    1181                 'hide_empty'             => true,
    1182                 'include'                => array(),
    1183                 'exclude'                => array(),
    1184                 'exclude_tree'           => array(),
    1185                 'number'                 => '',
    1186                 'offset'                 => '',
    1187                 'fields'                 => 'all',
    1188                 'name'                   => '',
    1189                 'slug'                   => '',
    1190                 'hierarchical'           => true,
    1191                 'search'                 => '',
    1192                 'name__like'             => '',
    1193                 'description__like'      => '',
    1194                 'pad_counts'             => false,
    1195                 'get'                    => '',
    1196                 'child_of'               => 0,
    1197                 'parent'                 => '',
    1198                 'childless'              => false,
    1199                 'cache_domain'           => 'core',
    1200                 'update_term_meta_cache' => true,
    1201                 'meta_query'             => ''
    1202         );
     1177        $term_query = new WP_Term_Query();
    12031178
    12041179        /*
    12051180         * Legacy argument format ($taxonomy, $args) takes precedence.
    function get_terms( $args = array(), $deprecated = '' ) { 
    12081183         * (a) a second non-empty parameter is passed, or
    12091184         * (b) the first parameter shares no keys with the default array (ie, it's a list of taxonomies)
    12101185         */
    1211         $key_intersect  = array_intersect_key( $defaults, (array) $args );
     1186        $key_intersect  = array_intersect_key( $term_query->query_var_defaults, (array) $args );
    12121187        $do_legacy_args = $deprecated || empty( $key_intersect );
    12131188
    1214         $taxonomies = null;
    12151189        if ( $do_legacy_args ) {
    12161190                $taxonomies = (array) $args;
    12171191                $args = $deprecated;
     1192                $args['taxonomy'] = $taxonomies;
    12181193        } elseif ( isset( $args['taxonomy'] ) && null !== $args['taxonomy'] ) {
    1219                 $taxonomies = (array) $args['taxonomy'];
    1220                 unset( $args['taxonomy'] );
     1194                $args['taxonomy'] = (array) $args['taxonomy'];
    12211195        }
    12221196
    1223         $empty_array = array();
    1224 
    1225         if ( $taxonomies ) {
    1226                 foreach ( $taxonomies as $taxonomy ) {
    1227                         if ( ! taxonomy_exists($taxonomy) ) {
     1197        if ( ! empty( $args['taxonomy'] ) ) {
     1198                foreach ( $args['taxonomy'] as $taxonomy ) {
     1199                        if ( ! taxonomy_exists( $taxonomy ) ) {
    12281200                                return new WP_Error( 'invalid_taxonomy', __( 'Invalid taxonomy' ) );
    12291201                        }
    12301202                }
    12311203        }
    12321204
    1233         /**
    1234          * Filters the terms query default arguments.
    1235          *
    1236          * Use 'get_terms_args' to filter the passed arguments.
    1237          *
    1238          * @since 4.4.0
    1239          *
    1240          * @param array $defaults   An array of default get_terms() arguments.
    1241          * @param array $taxonomies An array of taxonomies.
    1242          */
    1243         $args = wp_parse_args( $args, apply_filters( 'get_terms_defaults', $defaults, $taxonomies ) );
    1244 
    1245         $args['number'] = absint( $args['number'] );
    1246         $args['offset'] = absint( $args['offset'] );
    1247 
    1248         // Save queries by not crawling the tree in the case of multiple taxes or a flat tax.
    1249         $has_hierarchical_tax = false;
    1250         if ( $taxonomies ) {
    1251                 foreach ( $taxonomies as $_tax ) {
    1252                         if ( is_taxonomy_hierarchical( $_tax ) ) {
    1253                                 $has_hierarchical_tax = true;
    1254                         }
    1255                 }
    1256         }
    1257 
    1258         if ( ! $has_hierarchical_tax ) {
    1259                 $args['hierarchical'] = false;
    1260                 $args['pad_counts'] = false;
    1261         }
    1262 
    1263         // 'parent' overrides 'child_of'.
    1264         if ( 0 < intval( $args['parent'] ) ) {
    1265                 $args['child_of'] = false;
    1266         }
    1267 
    1268         if ( 'all' == $args['get'] ) {
    1269                 $args['childless'] = false;
    1270                 $args['child_of'] = 0;
    1271                 $args['hide_empty'] = 0;
    1272                 $args['hierarchical'] = false;
    1273                 $args['pad_counts'] = false;
    1274         }
     1205        $terms = $term_query->query( $args );
    12751206
    12761207        /**
    1277          * Filters the terms query arguments.
    1278          *
    1279          * @since 3.1.0
    1280          *
    1281          * @param array $args       An array of get_terms() arguments.
    1282          * @param array $taxonomies An array of taxonomies.
    1283          */
    1284         $args = apply_filters( 'get_terms_args', $args, $taxonomies );
    1285 
    1286         // Avoid the query if the queried parent/child_of term has no descendants.
    1287         $child_of = $args['child_of'];
    1288         $parent   = $args['parent'];
    1289 
    1290         if ( $child_of ) {
    1291                 $_parent = $child_of;
    1292         } elseif ( $parent ) {
    1293                 $_parent = $parent;
    1294         } else {
    1295                 $_parent = false;
    1296         }
    1297 
    1298         if ( $_parent ) {
    1299                 $in_hierarchy = false;
    1300                 foreach ( $taxonomies as $_tax ) {
    1301                         $hierarchy = _get_term_hierarchy( $_tax );
    1302 
    1303                         if ( isset( $hierarchy[ $_parent ] ) ) {
    1304                                 $in_hierarchy = true;
    1305                         }
    1306                 }
    1307 
    1308                 if ( ! $in_hierarchy ) {
    1309                         return $empty_array;
    1310                 }
    1311         }
    1312 
    1313         $_orderby = strtolower( $args['orderby'] );
    1314         if ( 'count' == $_orderby ) {
    1315                 $orderby = 'tt.count';
    1316         } elseif ( 'name' == $_orderby ) {
    1317                 $orderby = 't.name';
    1318         } elseif ( 'slug' == $_orderby ) {
    1319                 $orderby = 't.slug';
    1320         } elseif ( 'include' == $_orderby && ! empty( $args['include'] ) ) {
    1321                 $include = implode( ',', array_map( 'absint', $args['include'] ) );
    1322                 $orderby = "FIELD( t.term_id, $include )";
    1323         } elseif ( 'term_group' == $_orderby ) {
    1324                 $orderby = 't.term_group';
    1325         } elseif ( 'description' == $_orderby ) {
    1326                 $orderby = 'tt.description';
    1327         } elseif ( 'none' == $_orderby ) {
    1328                 $orderby = '';
    1329         } elseif ( empty( $_orderby ) || 'id' == $_orderby || 'term_id' === $_orderby ) {
    1330                 $orderby = 't.term_id';
    1331         } else {
    1332                 $orderby = 't.name';
    1333         }
    1334 
    1335         /**
    1336          * Filters the ORDERBY clause of the terms query.
    1337          *
    1338          * @since 2.8.0
    1339          *
    1340          * @param string $orderby    `ORDERBY` clause of the terms query.
    1341          * @param array  $args       An array of terms query arguments.
    1342          * @param array  $taxonomies An array of taxonomies.
    1343          */
    1344         $orderby = apply_filters( 'get_terms_orderby', $orderby, $args, $taxonomies );
    1345 
    1346         $order = strtoupper( $args['order'] );
    1347         if ( ! empty( $orderby ) ) {
    1348                 $orderby = "ORDER BY $orderby";
    1349         } else {
    1350                 $order = '';
    1351         }
    1352 
    1353         if ( '' !== $order && ! in_array( $order, array( 'ASC', 'DESC' ) ) ) {
    1354                 $order = 'ASC';
    1355         }
    1356 
    1357         $where_conditions = array();
    1358 
    1359         if ( $taxonomies ) {
    1360                 $where_conditions[] = "tt.taxonomy IN ('" . implode("', '", array_map( 'esc_sql', $taxonomies ) ) . "')";
    1361         }
    1362 
    1363         $exclude = $args['exclude'];
    1364         $exclude_tree = $args['exclude_tree'];
    1365         $include = $args['include'];
    1366 
    1367         $inclusions = '';
    1368         if ( ! empty( $include ) ) {
    1369                 $exclude = '';
    1370                 $exclude_tree = '';
    1371                 $inclusions = implode( ',', wp_parse_id_list( $include ) );
    1372         }
    1373 
    1374         if ( ! empty( $inclusions ) ) {
    1375                 $where_conditions[] = 't.term_id IN ( ' . $inclusions . ' )';
    1376         }
    1377 
    1378         $exclusions = array();
    1379         if ( ! empty( $exclude_tree ) ) {
    1380                 $exclude_tree = wp_parse_id_list( $exclude_tree );
    1381                 $excluded_children = $exclude_tree;
    1382                 foreach ( $exclude_tree as $extrunk ) {
    1383                         $excluded_children = array_merge(
    1384                                 $excluded_children,
    1385                                 (array) get_terms( $taxonomies[0], array( 'child_of' => intval( $extrunk ), 'fields' => 'ids', 'hide_empty' => 0 ) )
    1386                         );
    1387                 }
    1388                 $exclusions = array_merge( $excluded_children, $exclusions );
    1389         }
    1390 
    1391         if ( ! empty( $exclude ) ) {
    1392                 $exclusions = array_merge( wp_parse_id_list( $exclude ), $exclusions );
    1393         }
    1394 
    1395         // 'childless' terms are those without an entry in the flattened term hierarchy.
    1396         $childless = (bool) $args['childless'];
    1397         if ( $childless ) {
    1398                 foreach ( $taxonomies as $_tax ) {
    1399                         $term_hierarchy = _get_term_hierarchy( $_tax );
    1400                         $exclusions = array_merge( array_keys( $term_hierarchy ), $exclusions );
    1401                 }
    1402         }
    1403 
    1404         if ( ! empty( $exclusions ) ) {
    1405                 $exclusions = 't.term_id NOT IN (' . implode( ',', array_map( 'intval', $exclusions ) ) . ')';
    1406         } else {
    1407                 $exclusions = '';
    1408         }
    1409 
    1410         /**
    1411          * Filters the terms to exclude from the terms query.
     1208         * Filters the found terms.
    14121209         *
    14131210         * @since 2.3.0
     1211         * @since 4.6.0 Added `$term_query`.
    14141212         *
    1415          * @param string $exclusions `NOT IN` clause of the terms query.
    1416          * @param array  $args       An array of terms query arguments.
    1417          * @param array  $taxonomies An array of taxonomies.
    1418          */
    1419         $exclusions = apply_filters( 'list_terms_exclusions', $exclusions, $args, $taxonomies );
    1420 
    1421         if ( ! empty( $exclusions ) ) {
    1422                 // Must do string manipulation here for backward compatibility with filter.
    1423                 $where_conditions[] = preg_replace( '/^\s*AND\s*/', '', $exclusions );
    1424         }
    1425 
    1426         if ( ! empty( $args['name'] ) ) {
    1427                 $names = (array) $args['name'];
    1428                 foreach ( $names as &$_name ) {
    1429                         // `sanitize_term_field()` returns slashed data.
    1430                         $_name = stripslashes( sanitize_term_field( 'name', $_name, 0, reset( $taxonomies ), 'db' ) );
    1431                 }
    1432 
    1433                 $where_conditions[] = "t.name IN ('" . implode( "', '", array_map( 'esc_sql', $names ) ) . "')";
    1434         }
    1435 
    1436         if ( ! empty( $args['slug'] ) ) {
    1437                 if ( is_array( $args['slug'] ) ) {
    1438                         $slug = array_map( 'sanitize_title', $args['slug'] );
    1439                         $where_conditions[] = "t.slug IN ('" . implode( "', '", $slug ) . "')";
    1440                 } else {
    1441                         $slug = sanitize_title( $args['slug'] );
    1442                         $where_conditions[] = "t.slug = '$slug'";
    1443                 }
    1444         }
    1445 
    1446         if ( ! empty( $args['name__like'] ) ) {
    1447                 $where_conditions[] = $wpdb->prepare( "t.name LIKE %s", '%' . $wpdb->esc_like( $args['name__like'] ) . '%' );
    1448         }
    1449 
    1450         if ( ! empty( $args['description__like'] ) ) {
    1451                 $where_conditions[] = $wpdb->prepare( "tt.description LIKE %s", '%' . $wpdb->esc_like( $args['description__like'] ) . '%' );
    1452         }
    1453 
    1454         if ( '' !== $parent ) {
    1455                 $parent = (int) $parent;
    1456                 $where_conditions[] = "tt.parent = '$parent'";
    1457         }
    1458 
    1459         $hierarchical = $args['hierarchical'];
    1460         if ( 'count' == $args['fields'] ) {
    1461                 $hierarchical = false;
    1462         }
    1463         if ( $args['hide_empty'] && !$hierarchical ) {
    1464                 $where_conditions[] = 'tt.count > 0';
    1465         }
    1466 
    1467         $number = $args['number'];
    1468         $offset = $args['offset'];
    1469 
    1470         // Don't limit the query results when we have to descend the family tree.
    1471         if ( $number && ! $hierarchical && ! $child_of && '' === $parent ) {
    1472                 if ( $offset ) {
    1473                         $limits = 'LIMIT ' . $offset . ',' . $number;
    1474                 } else {
    1475                         $limits = 'LIMIT ' . $number;
    1476                 }
    1477         } else {
    1478                 $limits = '';
    1479         }
    1480 
    1481         if ( ! empty( $args['search'] ) ) {
    1482                 $like = '%' . $wpdb->esc_like( $args['search'] ) . '%';
    1483                 $where_conditions[] = $wpdb->prepare( '((t.name LIKE %s) OR (t.slug LIKE %s))', $like, $like );
    1484         }
    1485 
    1486         // Meta query support.
    1487         $join = '';
    1488         $distinct = '';
    1489 
    1490         $mquery = new WP_Meta_Query();
    1491         $mquery->parse_query_vars( $args );
    1492         $mq_sql = $mquery->get_sql( 'term', 't', 'term_id' );
    1493         $meta_clauses = $mquery->get_clauses();
    1494 
    1495         if ( ! empty( $meta_clauses ) ) {
    1496                 $join .= $mq_sql['join'];
    1497                 $where_conditions[] = preg_replace( '/^\s*AND\s*/', '', $mq_sql['where'] );
    1498                 $distinct .= "DISTINCT";
    1499 
    1500                 // 'orderby' support.
    1501                 $allowed_keys = array();
    1502                 $primary_meta_key   = null;
    1503                 $primary_meta_query = reset( $meta_clauses );
    1504                 if ( ! empty( $primary_meta_query['key'] ) ) {
    1505                         $primary_meta_key = $primary_meta_query['key'];
    1506                         $allowed_keys[] = $primary_meta_key;
    1507                 }
    1508                 $allowed_keys[] = 'meta_value';
    1509                 $allowed_keys[] = 'meta_value_num';
    1510                 $allowed_keys   = array_merge( $allowed_keys, array_keys( $meta_clauses ) );
    1511 
    1512                 if ( ! empty( $args['orderby'] ) && in_array( $args['orderby'], $allowed_keys ) ) {
    1513                         switch( $args['orderby'] ) {
    1514                                 case $primary_meta_key:
    1515                                 case 'meta_value':
    1516                                         if ( ! empty( $primary_meta_query['type'] ) ) {
    1517                                                 $orderby = "ORDER BY CAST({$primary_meta_query['alias']}.meta_value AS {$primary_meta_query['cast']})";
    1518                                         } else {
    1519                                                 $orderby = "ORDER BY {$primary_meta_query['alias']}.meta_value";
    1520                                         }
    1521                                         break;
    1522 
    1523                                 case 'meta_value_num':
    1524                                         $orderby = "ORDER BY {$primary_meta_query['alias']}.meta_value+0";
    1525                                         break;
    1526 
    1527                                 default:
    1528                                         if ( array_key_exists( $args['orderby'], $meta_clauses ) ) {
    1529                                                 // $orderby corresponds to a meta_query clause.
    1530                                                 $meta_clause = $meta_clauses[ $args['orderby'] ];
    1531                                                 $orderby = "ORDER BY CAST({$meta_clause['alias']}.meta_value AS {$meta_clause['cast']})";
    1532                                         }
    1533                                         break;
    1534                         }
    1535                 }
    1536         }
    1537 
    1538         $selects = array();
    1539         switch ( $args['fields'] ) {
    1540                 case 'all':
    1541                         $selects = array( 't.*', 'tt.*' );
    1542                         break;
    1543                 case 'ids':
    1544                 case 'id=>parent':
    1545                         $selects = array( 't.term_id', 'tt.parent', 'tt.count', 'tt.taxonomy' );
    1546                         break;
    1547                 case 'names':
    1548                         $selects = array( 't.term_id', 'tt.parent', 'tt.count', 't.name', 'tt.taxonomy' );
    1549                         break;
    1550                 case 'count':
    1551                         $orderby = '';
    1552                         $order = '';
    1553                         $selects = array( 'COUNT(*)' );
    1554                         break;
    1555                 case 'id=>name':
    1556                         $selects = array( 't.term_id', 't.name', 'tt.count', 'tt.taxonomy' );
    1557                         break;
    1558                 case 'id=>slug':
    1559                         $selects = array( 't.term_id', 't.slug', 'tt.count', 'tt.taxonomy' );
    1560                         break;
    1561         }
    1562 
    1563         $_fields = $args['fields'];
    1564 
    1565         /**
    1566          * Filters the fields to select in the terms query.
    1567          *
    1568          * Field lists modified using this filter will only modify the term fields returned
    1569          * by the function when the `$fields` parameter set to 'count' or 'all'. In all other
    1570          * cases, the term fields in the results array will be determined by the `$fields`
    1571          * parameter alone.
    1572          *
    1573          * Use of this filter can result in unpredictable behavior, and is not recommended.
    1574          *
    1575          * @since 2.8.0
    1576          *
    1577          * @param array $selects    An array of fields to select for the terms query.
    1578          * @param array $args       An array of term query arguments.
    1579          * @param array $taxonomies An array of taxonomies.
     1213         * @param array         $cache      Cached array of terms for the given taxonomy.
     1214         * @param array         $taxonomies An array of taxonomies.
     1215         * @param array         $args       An array of get_terms() arguments.
     1216         * @param WP_Term_Query $term_query The WP_Term_Query object.
    15801217         */
    1581         $fields = implode( ', ', apply_filters( 'get_terms_fields', $selects, $args, $taxonomies ) );
    1582 
    1583         $join .= " INNER JOIN $wpdb->term_taxonomy AS tt ON t.term_id = tt.term_id";
    1584 
    1585         $where = implode( ' AND ', $where_conditions );
    1586 
    1587         $pieces = array( 'fields', 'join', 'where', 'distinct', 'orderby', 'order', 'limits' );
    1588 
    1589         /**
    1590          * Filters the terms query SQL clauses.
    1591          *
    1592          * @since 3.1.0
    1593          *
    1594          * @param array $pieces     Terms query SQL clauses.
    1595          * @param array $taxonomies An array of taxonomies.
    1596          * @param array $args       An array of terms query arguments.
    1597          */
    1598         $clauses = apply_filters( 'terms_clauses', compact( $pieces ), $taxonomies, $args );
    1599 
    1600         $fields = isset( $clauses[ 'fields' ] ) ? $clauses[ 'fields' ] : '';
    1601         $join = isset( $clauses[ 'join' ] ) ? $clauses[ 'join' ] : '';
    1602         $where = isset( $clauses[ 'where' ] ) ? $clauses[ 'where' ] : '';
    1603         $distinct = isset( $clauses[ 'distinct' ] ) ? $clauses[ 'distinct' ] : '';
    1604         $orderby = isset( $clauses[ 'orderby' ] ) ? $clauses[ 'orderby' ] : '';
    1605         $order = isset( $clauses[ 'order' ] ) ? $clauses[ 'order' ] : '';
    1606         $limits = isset( $clauses[ 'limits' ] ) ? $clauses[ 'limits' ] : '';
    1607 
    1608         if ( $where ) {
    1609                 $where = "WHERE $where";
    1610         }
    1611 
    1612         $query = "SELECT $distinct $fields FROM $wpdb->terms AS t $join $where $orderby $order $limits";
    1613 
    1614         // $args can be anything. Only use the args defined in defaults to compute the key.
    1615         $key = md5( serialize( wp_array_slice_assoc( $args, array_keys( $defaults ) ) ) . serialize( $taxonomies ) . $query );
    1616         $last_changed = wp_cache_get( 'last_changed', 'terms' );
    1617         if ( ! $last_changed ) {
    1618                 $last_changed = microtime();
    1619                 wp_cache_set( 'last_changed', $last_changed, 'terms' );
    1620         }
    1621         $cache_key = "get_terms:$key:$last_changed";
    1622         $cache = wp_cache_get( $cache_key, 'terms' );
    1623         if ( false !== $cache ) {
    1624                 if ( 'all' === $_fields ) {
    1625                         $cache = array_map( 'get_term', $cache );
    1626                 }
    1627 
    1628                 /**
    1629                  * Filters the given taxonomy's terms cache.
    1630                  *
    1631                  * @since 2.3.0
    1632                  *
    1633                  * @param array $cache      Cached array of terms for the given taxonomy.
    1634                  * @param array $taxonomies An array of taxonomies.
    1635                  * @param array $args       An array of get_terms() arguments.
    1636                  */
    1637                 return apply_filters( 'get_terms', $cache, $taxonomies, $args );
    1638         }
    1639 
    1640         if ( 'count' == $_fields ) {
    1641                 return $wpdb->get_var( $query );
    1642         }
    1643 
    1644         $terms = $wpdb->get_results($query);
    1645         if ( 'all' == $_fields ) {
    1646                 update_term_cache( $terms );
    1647         }
    1648 
    1649         // Prime termmeta cache.
    1650         if ( $args['update_term_meta_cache'] ) {
    1651                 $term_ids = wp_list_pluck( $terms, 'term_id' );
    1652                 update_termmeta_cache( $term_ids );
    1653         }
    1654 
    1655         if ( empty($terms) ) {
    1656                 wp_cache_add( $cache_key, array(), 'terms', DAY_IN_SECONDS );
    1657 
    1658                 /** This filter is documented in wp-includes/taxonomy.php */
    1659                 return apply_filters( 'get_terms', array(), $taxonomies, $args );
    1660         }
    1661 
    1662         if ( $child_of ) {
    1663                 foreach ( $taxonomies as $_tax ) {
    1664                         $children = _get_term_hierarchy( $_tax );
    1665                         if ( ! empty( $children ) ) {
    1666                                 $terms = _get_term_children( $child_of, $terms, $_tax );
    1667                         }
    1668                 }
    1669         }
    1670 
    1671         // Update term counts to include children.
    1672         if ( $args['pad_counts'] && 'all' == $_fields ) {
    1673                 foreach ( $taxonomies as $_tax ) {
    1674                         _pad_term_counts( $terms, $_tax );
    1675                 }
    1676         }
    1677 
    1678         // Make sure we show empty categories that have children.
    1679         if ( $hierarchical && $args['hide_empty'] && is_array( $terms ) ) {
    1680                 foreach ( $terms as $k => $term ) {
    1681                         if ( ! $term->count ) {
    1682                                 $children = get_term_children( $term->term_id, $term->taxonomy );
    1683                                 if ( is_array( $children ) ) {
    1684                                         foreach ( $children as $child_id ) {
    1685                                                 $child = get_term( $child_id, $term->taxonomy );
    1686                                                 if ( $child->count ) {
    1687                                                         continue 2;
    1688                                                 }
    1689                                         }
    1690                                 }
    1691 
    1692                                 // It really is empty.
    1693                                 unset($terms[$k]);
    1694                         }
    1695                 }
    1696         }
    1697 
    1698         $_terms = array();
    1699         if ( 'id=>parent' == $_fields ) {
    1700                 foreach ( $terms as $term ) {
    1701                         $_terms[ $term->term_id ] = $term->parent;
    1702                 }
    1703         } elseif ( 'ids' == $_fields ) {
    1704                 foreach ( $terms as $term ) {
    1705                         $_terms[] = $term->term_id;
    1706                 }
    1707         } elseif ( 'names' == $_fields ) {
    1708                 foreach ( $terms as $term ) {
    1709                         $_terms[] = $term->name;
    1710                 }
    1711         } elseif ( 'id=>name' == $_fields ) {
    1712                 foreach ( $terms as $term ) {
    1713                         $_terms[ $term->term_id ] = $term->name;
    1714                 }
    1715         } elseif ( 'id=>slug' == $_fields ) {
    1716                 foreach ( $terms as $term ) {
    1717                         $_terms[ $term->term_id ] = $term->slug;
    1718                 }
    1719         }
    1720 
    1721         if ( ! empty( $_terms ) ) {
    1722                 $terms = $_terms;
    1723         }
    1724 
    1725         // Hierarchical queries are not limited, so 'offset' and 'number' must be handled now.
    1726         if ( $hierarchical && $number && is_array( $terms ) ) {
    1727                 if ( $offset >= count( $terms ) ) {
    1728                         $terms = array();
    1729                 } else {
    1730                         $terms = array_slice( $terms, $offset, $number, true );
    1731                 }
    1732         }
    1733 
    1734         wp_cache_add( $cache_key, $terms, 'terms', DAY_IN_SECONDS );
    1735 
    1736         if ( 'all' === $_fields ) {
    1737                 $terms = array_map( 'get_term', $terms );
    1738         }
    1739 
    1740         /** This filter is documented in wp-includes/taxonomy.php */
    1741         return apply_filters( 'get_terms', $terms, $taxonomies, $args );
     1218        return apply_filters( 'get_terms', $terms, $term_query->query_vars['taxonomy'], $term_query->query_vars );
    17421219}
    17431220
    17441221/**
  • src/wp-settings.php

    diff --git src/wp-settings.php src/wp-settings.php
    index e9433be..e2cba9d 100644
    require( ABSPATH . WPINC . '/deprecated.php' ); 
    168168require( ABSPATH . WPINC . '/script-loader.php' );
    169169require( ABSPATH . WPINC . '/taxonomy.php' );
    170170require( ABSPATH . WPINC . '/class-wp-term.php' );
     171require( ABSPATH . WPINC . '/class-wp-term-query.php' );
    171172require( ABSPATH . WPINC . '/class-wp-tax-query.php' );
    172173require( ABSPATH . WPINC . '/update.php' );
    173174require( ABSPATH . WPINC . '/canonical.php' );