Ejemplo n.º 1
2
 /**
  * Parse and sanitize 'orderby' keys passed to the user query.
  *
  * @since 4.2.0
  * @access protected
  *
  * @global wpdb $wpdb WordPress database abstraction object.
  *
  * @param string $orderby Alias for the field to order by.
  * @return string Value to used in the ORDER clause, if `$orderby` is valid.
  */
 protected function parse_orderby($orderby)
 {
     global $wpdb;
     $meta_query_clauses = $this->meta_query->get_clauses();
     $_orderby = '';
     if (in_array($orderby, array('login', 'nicename', 'email', 'url', 'registered'))) {
         $_orderby = 'user_' . $orderby;
     } elseif (in_array($orderby, array('user_login', 'user_nicename', 'user_email', 'user_url', 'user_registered'))) {
         $_orderby = $orderby;
     } elseif ('name' == $orderby || 'display_name' == $orderby) {
         $_orderby = 'display_name';
     } elseif ('post_count' == $orderby) {
         // todo: avoid the JOIN
         $where = get_posts_by_author_sql('post');
         $this->query_from .= " LEFT OUTER JOIN (\n\t\t\t\tSELECT post_author, COUNT(*) as post_count\n\t\t\t\tFROM {$wpdb->posts}\n\t\t\t\t{$where}\n\t\t\t\tGROUP BY post_author\n\t\t\t) p ON ({$wpdb->users}.ID = p.post_author)\n\t\t\t";
         $_orderby = 'post_count';
     } elseif ('ID' == $orderby || 'id' == $orderby) {
         $_orderby = 'ID';
     } elseif ('meta_value' == $orderby || $this->get('meta_key') == $orderby) {
         $_orderby = "{$wpdb->usermeta}.meta_value";
     } elseif ('meta_value_num' == $orderby) {
         $_orderby = "{$wpdb->usermeta}.meta_value+0";
     } elseif ('include' === $orderby && !empty($this->query_vars['include'])) {
         $include = wp_parse_id_list($this->query_vars['include']);
         $include_sql = implode(',', $include);
         $_orderby = "FIELD( {$wpdb->users}.ID, {$include_sql} )";
     } elseif (isset($meta_query_clauses[$orderby])) {
         $meta_clause = $meta_query_clauses[$orderby];
         $_orderby = sprintf("CAST(%s.meta_value AS %s)", esc_sql($meta_clause['alias']), esc_sql($meta_clause['cast']));
     }
     return $_orderby;
 }
Ejemplo n.º 2
0
/**
 * Retrieve the terms in a given taxonomy or list of taxonomies.
 *
 * You can fully inject any customizations to the query before it is sent, as
 * well as control the output with a filter.
 *
 * The {@see 'get_terms'} filter will be called when the cache has the term and will
 * pass the found term along with the array of $taxonomies and array of $args.
 * This filter is also called before the array of terms is passed and will pass
 * the array of terms, along with the $taxonomies and $args.
 *
 * The {@see 'list_terms_exclusions'} filter passes the compiled exclusions along with
 * the $args.
 *
 * The {@see 'get_terms_orderby'} filter passes the `ORDER BY` clause for the query
 * along with the $args array.
 *
 * @since 2.3.0
 * @since 4.2.0 Introduced 'name' and 'childless' parameters.
 * @since 4.4.0 Introduced the ability to pass 'term_id' as an alias of 'id' for the `orderby` parameter.
 *              Introduced the 'meta_query' and 'update_term_meta_cache' parameters. Converted to return
 *              a list of WP_Term objects.
 * @since 4.5.0 Introduced 'meta_key' and 'meta_value' parameters. Introduced the ability to order results by metadata.
 *
 * @global wpdb  $wpdb WordPress database abstraction object.
 * @global array $wp_filter
 *
 * @param string|array $taxonomies Taxonomy name or list of Taxonomy names.
 * @param array|string $args {
 *     Optional. Array or string of arguments to get terms.
 *
 *     @type string       $orderby                Field(s) to order terms by. Accepts term fields ('name', 'slug',
 *                                                'term_group', 'term_id', 'id', 'description'), 'count' for term
 *                                                taxonomy count, 'include' to match the 'order' of the $include param,
 *                                                'meta_value', 'meta_value_num', the value of `$meta_key`, the array
 *                                                keys of `$meta_query`, or 'none' to omit the ORDER BY clause.
 *                                                Defaults to 'name'.
 *     @type string       $order                  Whether to order terms in ascending or descending order.
 *                                                Accepts 'ASC' (ascending) or 'DESC' (descending).
 *                                                Default 'ASC'.
 *     @type bool|int     $hide_empty             Whether to hide terms not assigned to any posts. Accepts
 *                                                1|true or 0|false. Default 1|true.
 *     @type array|string $include                Array or comma/space-separated string of term ids to include.
 *                                                Default empty array.
 *     @type array|string $exclude                Array or comma/space-separated string of term ids to exclude.
 *                                                If $include is non-empty, $exclude is ignored.
 *                                                Default empty array.
 *     @type array|string $exclude_tree           Array or comma/space-separated string of term ids to exclude
 *                                                along with all of their descendant terms. If $include is
 *                                                non-empty, $exclude_tree is ignored. Default empty array.
 *     @type int|string   $number                 Maximum number of terms to return. Accepts ''|0 (all) or any
 *                                                positive number. Default ''|0 (all).
 *     @type int          $offset                 The number by which to offset the terms query. Default empty.
 *     @type string       $fields                 Term fields to query for. Accepts 'all' (returns an array of complete
 *                                                term objects), 'ids' (returns an array of ids), 'id=>parent' (returns
 *                                                an associative array with ids as keys, parent term IDs as values),
 *                                                'names' (returns an array of term names), 'count' (returns the number
 *                                                of matching terms), 'id=>name' (returns an associative array with ids
 *                                                as keys, term names as values), or 'id=>slug' (returns an associative
 *                                                array with ids as keys, term slugs as values). Default 'all'.
 *     @type string|array $name                   Optional. Name or array of names to return term(s) for. Default empty.
 *     @type string|array $slug                   Optional. Slug or array of slugs to return term(s) for. Default empty.
 *     @type bool         $hierarchical           Whether to include terms that have non-empty descendants (even
 *                                                if $hide_empty is set to true). Default true.
 *     @type string       $search                 Search criteria to match terms. Will be SQL-formatted with
 *                                                wildcards before and after. Default empty.
 *     @type string       $name__like             Retrieve terms with criteria by which a term is LIKE $name__like.
 *                                                Default empty.
 *     @type string       $description__like      Retrieve terms where the description is LIKE $description__like.
 *                                                Default empty.
 *     @type bool         $pad_counts             Whether to pad the quantity of a term's children in the quantity
 *                                                of each term's "count" object variable. Default false.
 *     @type string       $get                    Whether to return terms regardless of ancestry or whether the terms
 *                                                are empty. Accepts 'all' or empty (disabled). Default empty.
 *     @type int          $child_of               Term ID to retrieve child terms of. If multiple taxonomies
 *                                                are passed, $child_of is ignored. Default 0.
 *     @type int|string   $parent                 Parent term ID to retrieve direct-child terms of. Default empty.
 *     @type bool         $childless              True to limit results to terms that have no children. This parameter
 *                                                has no effect on non-hierarchical taxonomies. Default false.
 *     @type string       $cache_domain           Unique cache key to be produced when this query is stored in an
 *                                                object cache. Default is 'core'.
 *     @type bool         $update_term_meta_cache Whether to prime meta caches for matched terms. Default true.
 *     @type array        $meta_query             Meta query clauses to limit retrieved terms by.
 *                                                See `WP_Meta_Query`. Default empty.
 *     @type string       $meta_key               Limit terms to those matching a specific metadata key. Can be used in
 *                                                conjunction with `$meta_value`.
 *     @type string       $meta_value             Limit terms to those matching a specific metadata value. Usually used
 *                                                in conjunction with `$meta_key`.
 * }
 * @return array|int|WP_Error List of WP_Term instances and their children. Will return WP_Error, if any of $taxonomies
 *                            do not exist.
 */
function get_terms($taxonomies, $args = '')
{
    global $wpdb;
    $empty_array = array();
    $single_taxonomy = !is_array($taxonomies) || 1 === count($taxonomies);
    if (!is_array($taxonomies)) {
        $taxonomies = array($taxonomies);
    }
    foreach ($taxonomies as $taxonomy) {
        if (!taxonomy_exists($taxonomy)) {
            return new WP_Error('invalid_taxonomy', __('Invalid taxonomy'));
        }
    }
    $defaults = array('orderby' => 'name', 'order' => 'ASC', 'hide_empty' => true, 'include' => array(), 'exclude' => array(), 'exclude_tree' => array(), 'number' => '', 'offset' => '', 'fields' => 'all', 'name' => '', 'slug' => '', 'hierarchical' => true, 'search' => '', 'name__like' => '', 'description__like' => '', 'pad_counts' => false, 'get' => '', 'child_of' => 0, 'parent' => '', 'childless' => false, 'cache_domain' => 'core', 'update_term_meta_cache' => true, 'meta_query' => '');
    /**
     * Filter the terms query default arguments.
     *
     * Use 'get_terms_args' to filter the passed arguments.
     *
     * @since 4.4.0
     *
     * @param array $defaults   An array of default get_terms() arguments.
     * @param array $taxonomies An array of taxonomies.
     */
    $args = wp_parse_args($args, apply_filters('get_terms_defaults', $defaults, $taxonomies));
    $args['number'] = absint($args['number']);
    $args['offset'] = absint($args['offset']);
    // Save queries by not crawling the tree in the case of multiple taxes or a flat tax.
    $has_hierarchical_tax = false;
    foreach ($taxonomies as $_tax) {
        if (is_taxonomy_hierarchical($_tax)) {
            $has_hierarchical_tax = true;
        }
    }
    if (!$has_hierarchical_tax) {
        $args['hierarchical'] = false;
        $args['pad_counts'] = false;
    }
    // 'parent' overrides 'child_of'.
    if (0 < intval($args['parent'])) {
        $args['child_of'] = false;
    }
    if ('all' == $args['get']) {
        $args['childless'] = false;
        $args['child_of'] = 0;
        $args['hide_empty'] = 0;
        $args['hierarchical'] = false;
        $args['pad_counts'] = false;
    }
    /**
     * Filter the terms query arguments.
     *
     * @since 3.1.0
     *
     * @param array $args       An array of get_terms() arguments.
     * @param array $taxonomies An array of taxonomies.
     */
    $args = apply_filters('get_terms_args', $args, $taxonomies);
    // Avoid the query if the queried parent/child_of term has no descendants.
    $child_of = $args['child_of'];
    $parent = $args['parent'];
    if ($child_of) {
        $_parent = $child_of;
    } elseif ($parent) {
        $_parent = $parent;
    } else {
        $_parent = false;
    }
    if ($_parent) {
        $in_hierarchy = false;
        foreach ($taxonomies as $_tax) {
            $hierarchy = _get_term_hierarchy($_tax);
            if (isset($hierarchy[$_parent])) {
                $in_hierarchy = true;
            }
        }
        if (!$in_hierarchy) {
            return $empty_array;
        }
    }
    $_orderby = strtolower($args['orderby']);
    if ('count' == $_orderby) {
        $orderby = 'tt.count';
    } elseif ('name' == $_orderby) {
        $orderby = 't.name';
    } elseif ('slug' == $_orderby) {
        $orderby = 't.slug';
    } elseif ('include' == $_orderby && !empty($args['include'])) {
        $include = implode(',', array_map('absint', $args['include']));
        $orderby = "FIELD( t.term_id, {$include} )";
    } elseif ('term_group' == $_orderby) {
        $orderby = 't.term_group';
    } elseif ('description' == $_orderby) {
        $orderby = 'tt.description';
    } elseif ('none' == $_orderby) {
        $orderby = '';
    } elseif (empty($_orderby) || 'id' == $_orderby || 'term_id' === $_orderby) {
        $orderby = 't.term_id';
    } else {
        $orderby = 't.name';
    }
    /**
     * Filter the ORDERBY clause of the terms query.
     *
     * @since 2.8.0
     *
     * @param string $orderby    `ORDERBY` clause of the terms query.
     * @param array  $args       An array of terms query arguments.
     * @param array  $taxonomies An array of taxonomies.
     */
    $orderby = apply_filters('get_terms_orderby', $orderby, $args, $taxonomies);
    $order = strtoupper($args['order']);
    if (!empty($orderby)) {
        $orderby = "ORDER BY {$orderby}";
    } else {
        $order = '';
    }
    if ('' !== $order && !in_array($order, array('ASC', 'DESC'))) {
        $order = 'ASC';
    }
    $where = "tt.taxonomy IN ('" . implode("', '", $taxonomies) . "')";
    $exclude = $args['exclude'];
    $exclude_tree = $args['exclude_tree'];
    $include = $args['include'];
    $inclusions = '';
    if (!empty($include)) {
        $exclude = '';
        $exclude_tree = '';
        $inclusions = implode(',', wp_parse_id_list($include));
    }
    if (!empty($inclusions)) {
        $inclusions = ' AND t.term_id IN ( ' . $inclusions . ' )';
        $where .= $inclusions;
    }
    $exclusions = array();
    if (!empty($exclude_tree)) {
        $exclude_tree = wp_parse_id_list($exclude_tree);
        $excluded_children = $exclude_tree;
        foreach ($exclude_tree as $extrunk) {
            $excluded_children = array_merge($excluded_children, (array) get_terms($taxonomies[0], array('child_of' => intval($extrunk), 'fields' => 'ids', 'hide_empty' => 0)));
        }
        $exclusions = array_merge($excluded_children, $exclusions);
    }
    if (!empty($exclude)) {
        $exclusions = array_merge(wp_parse_id_list($exclude), $exclusions);
    }
    // 'childless' terms are those without an entry in the flattened term hierarchy.
    $childless = (bool) $args['childless'];
    if ($childless) {
        foreach ($taxonomies as $_tax) {
            $term_hierarchy = _get_term_hierarchy($_tax);
            $exclusions = array_merge(array_keys($term_hierarchy), $exclusions);
        }
    }
    if (!empty($exclusions)) {
        $exclusions = ' AND t.term_id NOT IN (' . implode(',', array_map('intval', $exclusions)) . ')';
    } else {
        $exclusions = '';
    }
    /**
     * Filter the terms to exclude from the terms query.
     *
     * @since 2.3.0
     *
     * @param string $exclusions `NOT IN` clause of the terms query.
     * @param array  $args       An array of terms query arguments.
     * @param array  $taxonomies An array of taxonomies.
     */
    $exclusions = apply_filters('list_terms_exclusions', $exclusions, $args, $taxonomies);
    if (!empty($exclusions)) {
        $where .= $exclusions;
    }
    if (!empty($args['name'])) {
        $names = (array) $args['name'];
        foreach ($names as &$_name) {
            // `sanitize_term_field()` returns slashed data.
            $_name = stripslashes(sanitize_term_field('name', $_name, 0, reset($taxonomies), 'db'));
        }
        $where .= " AND t.name IN ('" . implode("', '", array_map('esc_sql', $names)) . "')";
    }
    if (!empty($args['slug'])) {
        if (is_array($args['slug'])) {
            $slug = array_map('sanitize_title', $args['slug']);
            $where .= " AND t.slug IN ('" . implode("', '", $slug) . "')";
        } else {
            $slug = sanitize_title($args['slug']);
            $where .= " AND t.slug = '{$slug}'";
        }
    }
    if (!empty($args['name__like'])) {
        $where .= $wpdb->prepare(" AND t.name LIKE %s", '%' . $wpdb->esc_like($args['name__like']) . '%');
    }
    if (!empty($args['description__like'])) {
        $where .= $wpdb->prepare(" AND tt.description LIKE %s", '%' . $wpdb->esc_like($args['description__like']) . '%');
    }
    if ('' !== $parent) {
        $parent = (int) $parent;
        $where .= " AND tt.parent = '{$parent}'";
    }
    $hierarchical = $args['hierarchical'];
    if ('count' == $args['fields']) {
        $hierarchical = false;
    }
    if ($args['hide_empty'] && !$hierarchical) {
        $where .= ' AND tt.count > 0';
    }
    $number = $args['number'];
    $offset = $args['offset'];
    // Don't limit the query results when we have to descend the family tree.
    if ($number && !$hierarchical && !$child_of && '' === $parent) {
        if ($offset) {
            $limits = 'LIMIT ' . $offset . ',' . $number;
        } else {
            $limits = 'LIMIT ' . $number;
        }
    } else {
        $limits = '';
    }
    if (!empty($args['search'])) {
        $like = '%' . $wpdb->esc_like($args['search']) . '%';
        $where .= $wpdb->prepare(' AND ((t.name LIKE %s) OR (t.slug LIKE %s))', $like, $like);
    }
    // Meta query support.
    $join = '';
    $distinct = '';
    $mquery = new WP_Meta_Query();
    $mquery->parse_query_vars($args);
    $mq_sql = $mquery->get_sql('term', 't', 'term_id');
    $meta_clauses = $mquery->get_clauses();
    if (!empty($meta_clauses)) {
        $join .= $mq_sql['join'];
        $where .= $mq_sql['where'];
        $distinct .= "DISTINCT";
        // 'orderby' support.
        $allowed_keys = array();
        $primary_meta_key = null;
        $primary_meta_query = reset($meta_clauses);
        if (!empty($primary_meta_query['key'])) {
            $primary_meta_key = $primary_meta_query['key'];
            $allowed_keys[] = $primary_meta_key;
        }
        $allowed_keys[] = 'meta_value';
        $allowed_keys[] = 'meta_value_num';
        $allowed_keys = array_merge($allowed_keys, array_keys($meta_clauses));
        if (!empty($args['orderby']) && in_array($args['orderby'], $allowed_keys)) {
            switch ($args['orderby']) {
                case $primary_meta_key:
                case 'meta_value':
                    if (!empty($primary_meta_query['type'])) {
                        $orderby = "ORDER BY CAST({$primary_meta_query['alias']}.meta_value AS {$primary_meta_query['cast']})";
                    } else {
                        $orderby = "ORDER BY {$primary_meta_query['alias']}.meta_value";
                    }
                    break;
                case 'meta_value_num':
                    $orderby = "ORDER BY {$primary_meta_query['alias']}.meta_value+0";
                    break;
                default:
                    if (array_key_exists($args['orderby'], $meta_clauses)) {
                        // $orderby corresponds to a meta_query clause.
                        $meta_clause = $meta_clauses[$args['orderby']];
                        $orderby = "ORDER BY CAST({$meta_clause['alias']}.meta_value AS {$meta_clause['cast']})";
                    }
                    break;
            }
        }
    }
    $selects = array();
    switch ($args['fields']) {
        case 'all':
            $selects = array('t.*', 'tt.*');
            break;
        case 'ids':
        case 'id=>parent':
            $selects = array('t.term_id', 'tt.parent', 'tt.count', 'tt.taxonomy');
            break;
        case 'names':
            $selects = array('t.term_id', 'tt.parent', 'tt.count', 't.name', 'tt.taxonomy');
            break;
        case 'count':
            $orderby = '';
            $order = '';
            $selects = array('COUNT(*)');
            break;
        case 'id=>name':
            $selects = array('t.term_id', 't.name', 'tt.count', 'tt.taxonomy');
            break;
        case 'id=>slug':
            $selects = array('t.term_id', 't.slug', 'tt.count', 'tt.taxonomy');
            break;
    }
    $_fields = $args['fields'];
    /**
     * Filter the fields to select in the terms query.
     *
     * Field lists modified using this filter will only modify the term fields returned
     * by the function when the `$fields` parameter set to 'count' or 'all'. In all other
     * cases, the term fields in the results array will be determined by the `$fields`
     * parameter alone.
     *
     * Use of this filter can result in unpredictable behavior, and is not recommended.
     *
     * @since 2.8.0
     *
     * @param array $selects    An array of fields to select for the terms query.
     * @param array $args       An array of term query arguments.
     * @param array $taxonomies An array of taxonomies.
     */
    $fields = implode(', ', apply_filters('get_terms_fields', $selects, $args, $taxonomies));
    $join .= " INNER JOIN {$wpdb->term_taxonomy} AS tt ON t.term_id = tt.term_id";
    $pieces = array('fields', 'join', 'where', 'distinct', 'orderby', 'order', 'limits');
    /**
     * Filter the terms query SQL clauses.
     *
     * @since 3.1.0
     *
     * @param array $pieces     Terms query SQL clauses.
     * @param array $taxonomies An array of taxonomies.
     * @param array $args       An array of terms query arguments.
     */
    $clauses = apply_filters('terms_clauses', compact($pieces), $taxonomies, $args);
    $fields = isset($clauses['fields']) ? $clauses['fields'] : '';
    $join = isset($clauses['join']) ? $clauses['join'] : '';
    $where = isset($clauses['where']) ? $clauses['where'] : '';
    $distinct = isset($clauses['distinct']) ? $clauses['distinct'] : '';
    $orderby = isset($clauses['orderby']) ? $clauses['orderby'] : '';
    $order = isset($clauses['order']) ? $clauses['order'] : '';
    $limits = isset($clauses['limits']) ? $clauses['limits'] : '';
    $query = "SELECT {$distinct} {$fields} FROM {$wpdb->terms} AS t {$join} WHERE {$where} {$orderby} {$order} {$limits}";
    // $args can be anything. Only use the args defined in defaults to compute the key.
    $key = md5(serialize(wp_array_slice_assoc($args, array_keys($defaults))) . serialize($taxonomies) . $query);
    $last_changed = wp_cache_get('last_changed', 'terms');
    if (!$last_changed) {
        $last_changed = microtime();
        wp_cache_set('last_changed', $last_changed, 'terms');
    }
    $cache_key = "get_terms:{$key}:{$last_changed}";
    $cache = wp_cache_get($cache_key, 'terms');
    if (false !== $cache) {
        if ('all' === $_fields) {
            $cache = array_map('get_term', $cache);
        }
        /**
         * Filter the given taxonomy's terms cache.
         *
         * @since 2.3.0
         *
         * @param array $cache      Cached array of terms for the given taxonomy.
         * @param array $taxonomies An array of taxonomies.
         * @param array $args       An array of get_terms() arguments.
         */
        return apply_filters('get_terms', $cache, $taxonomies, $args);
    }
    if ('count' == $_fields) {
        return $wpdb->get_var($query);
    }
    $terms = $wpdb->get_results($query);
    if ('all' == $_fields) {
        update_term_cache($terms);
    }
    // Prime termmeta cache.
    if ($args['update_term_meta_cache']) {
        $term_ids = wp_list_pluck($terms, 'term_id');
        update_termmeta_cache($term_ids);
    }
    if (empty($terms)) {
        wp_cache_add($cache_key, array(), 'terms', DAY_IN_SECONDS);
        /** This filter is documented in wp-includes/taxonomy.php */
        return apply_filters('get_terms', array(), $taxonomies, $args);
    }
    if ($child_of) {
        foreach ($taxonomies as $_tax) {
            $children = _get_term_hierarchy($_tax);
            if (!empty($children)) {
                $terms = _get_term_children($child_of, $terms, $_tax);
            }
        }
    }
    // Update term counts to include children.
    if ($args['pad_counts'] && 'all' == $_fields) {
        foreach ($taxonomies as $_tax) {
            _pad_term_counts($terms, $_tax);
        }
    }
    // Make sure we show empty categories that have children.
    if ($hierarchical && $args['hide_empty'] && is_array($terms)) {
        foreach ($terms as $k => $term) {
            if (!$term->count) {
                $children = get_term_children($term->term_id, $term->taxonomy);
                if (is_array($children)) {
                    foreach ($children as $child_id) {
                        $child = get_term($child_id, $term->taxonomy);
                        if ($child->count) {
                            continue 2;
                        }
                    }
                }
                // It really is empty.
                unset($terms[$k]);
            }
        }
    }
    $_terms = array();
    if ('id=>parent' == $_fields) {
        foreach ($terms as $term) {
            $_terms[$term->term_id] = $term->parent;
        }
    } elseif ('ids' == $_fields) {
        foreach ($terms as $term) {
            $_terms[] = $term->term_id;
        }
    } elseif ('names' == $_fields) {
        foreach ($terms as $term) {
            $_terms[] = $term->name;
        }
    } elseif ('id=>name' == $_fields) {
        foreach ($terms as $term) {
            $_terms[$term->term_id] = $term->name;
        }
    } elseif ('id=>slug' == $_fields) {
        foreach ($terms as $term) {
            $_terms[$term->term_id] = $term->slug;
        }
    }
    if (!empty($_terms)) {
        $terms = $_terms;
    }
    if ($number && is_array($terms) && count($terms) > $number) {
        $terms = array_slice($terms, $offset, $number, true);
    }
    wp_cache_add($cache_key, $terms, 'terms', DAY_IN_SECONDS);
    if ('all' === $_fields) {
        $terms = array_map('get_term', $terms);
    }
    /** This filter is documented in wp-includes/taxonomy.php */
    return apply_filters('get_terms', $terms, $taxonomies, $args);
}