Make WordPress Core

Ticket #35381: 35381.diff

File 35381.diff, 50.2 KB (added by flixos90, 8 years ago)

WP_Term_Query class

  • src/wp-includes/class-wp-term-query.php

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

    Property changes on: src/wp-includes/class-wp-term-query.php
    ___________________________________________________________________
    Added: svn:executable
    ## -0,0 +1 ##
    +*
    \ No newline at end of property
     
    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        $query = new WP_Term_Query();
    12031178
    12041179        /*
    12051180         * Legacy argument format ($taxonomy, $args) takes precedence.
     
    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( $query->query_var_defaults, (array) $args );
    12121187        $do_legacy_args = $deprecated || empty( $key_intersect );
    12131188
    12141189        $taxonomies = null;
     
    12221197
    12231198        $empty_array = array();
    12241199
     1200        // Save queries by not crawling the tree in the case of multiple taxes or a flat tax.
     1201        $has_hierarchical_tax = false;
    12251202        if ( $taxonomies ) {
    12261203                foreach ( $taxonomies as $taxonomy ) {
    12271204                        if ( ! taxonomy_exists($taxonomy) ) {
    12281205                                return new WP_Error( 'invalid_taxonomy', __( 'Invalid taxonomy' ) );
    12291206                        }
    1230                 }
    1231         }
    1232 
    1233         /**
    1234          * Filter 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 ) ) {
     1207                        if ( is_taxonomy_hierarchical( $taxonomy ) ) {
    12531208                                $has_hierarchical_tax = true;
    12541209                        }
    12551210                }
     
    12601215                $args['pad_counts'] = false;
    12611216        }
    12621217
    1263         // 'parent' overrides 'child_of'.
    1264         if ( 0 < intval( $args['parent'] ) ) {
    1265                 $args['child_of'] = false;
    1266         }
     1218        $args['taxonomy'] = $taxonomies;
    12671219
    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         }
    1275 
    1276         /**
    1277          * Filter 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          * Filter 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          * Filter the terms to exclude from the terms query.
    1412          *
    1413          * @since 2.3.0
    1414          *
    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          * Filter 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.
    1580          */
    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          * Filter 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                  * Filter 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 );
     1220        return $query->query( $args );
    17421221}
    17431222
    17441223/**
     
    38563335        }
    38573336}
    38583337
     3338/**
     3339 * Adds any terms from the given IDs to the cache that do not already exist in cache.
     3340 *
     3341 * @since 4.6.0
     3342 * @access private
     3343 *
     3344 * @see update_term_cache()
     3345 * @global wpdb $wpdb WordPress database abstraction object.
     3346 *
     3347 * @param array $term_ids          Array of term IDs.
     3348 * @param bool  $update_meta_cache Optional. Whether to update the meta cache. Default true.
     3349 */
     3350function _prime_term_caches( $term_ids, $update_meta_cache = true ) {
     3351        global $wpdb;
     3352
     3353        $non_cached_ids = _get_non_cached_ids( $term_ids, 'terms' );
     3354        if ( ! empty( $non_cached_ids ) ) {
     3355                $fresh_terms = $wpdb->get_results( sprintf( "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 IN (%s)", join( ",", array_map( 'intval', $non_cached_ids ) ) ) );
     3356
     3357                update_term_cache( $fresh_terms, $update_meta_cache );
     3358
     3359                if ( $update_meta_cache ) {
     3360                        update_termmeta_cache( $non_cached_ids );
     3361                }
     3362        }
     3363}
     3364
    38593365//
    38603366// Private
    38613367//
  • src/wp-settings.php

     
    167167require( ABSPATH . WPINC . '/script-loader.php' );
    168168require( ABSPATH . WPINC . '/taxonomy.php' );
    169169require( ABSPATH . WPINC . '/class-wp-term.php' );
     170require( ABSPATH . WPINC . '/class-wp-term-query.php' );
    170171require( ABSPATH . WPINC . '/class-wp-tax-query.php' );
    171172require( ABSPATH . WPINC . '/update.php' );
    172173require( ABSPATH . WPINC . '/canonical.php' );