get_terms

The timeline below displays how wordpress function get_terms has changed across different WordPress versions. If a version is not listed, refer to the next available version below.

WordPress Version: 6.2

/**
 * Retrieves 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 return type varies depending on the value passed to `$args['fields']`. See
 * WP_Term_Query::get_terms() for details. In all cases, a `WP_Error` object will
 * be returned if an invalid taxonomy is requested.
 *
 * 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.
 *
 * Taxonomy or an array of taxonomies should be passed via the 'taxonomy' argument
 * in the `$args` array:
 *
 *     $terms = get_terms( array(
 *         'taxonomy'   => 'post_tag',
 *         'hide_empty' => false,
 *     ) );
 *
 * Prior to 4.5.0, taxonomy was passed as the first parameter of `get_terms()`.
 *
 * @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 Changed the function signature so that the `$args` array can be provided as the first parameter.
 *              Introduced 'meta_key' and 'meta_value' parameters. Introduced the ability to order results by metadata.
 * @since 4.8.0 Introduced 'suppress_filter' parameter.
 *
 * @internal The `$deprecated` parameter is parsed for backward compatibility only.
 *
 * @param array|string $args       Optional. Array or string of arguments. See WP_Term_Query::__construct()
 *                                 for information on accepted arguments. Default empty array.
 * @param array|string $deprecated Optional. Argument array, when using the legacy function parameter format.
 *                                 If present, this parameter will be interpreted as `$args`, and the first
 *                                 function parameter will be parsed as a taxonomy or array of taxonomies.
 *                                 Default empty.
 * @return WP_Term[]|int[]|string[]|string|WP_Error Array of terms, a count thereof as a numeric string,
 *                                                  or WP_Error if any of the taxonomies do not exist.
 *                                                  See the function description for more information.
 */
function get_terms($args = array(), $deprecated = '')
{
    $term_query = new WP_Term_Query();
    $defaults = array('suppress_filter' => false);
    /*
     * Legacy argument format ($taxonomy, $args) takes precedence.
     *
     * We detect legacy argument format by checking if
     * (a) a second non-empty parameter is passed, or
     * (b) the first parameter shares no keys with the default array (ie, it's a list of taxonomies)
     */
    $_args = wp_parse_args($args);
    $key_intersect = array_intersect_key($term_query->query_var_defaults, (array) $_args);
    $do_legacy_args = $deprecated || empty($key_intersect);
    if ($do_legacy_args) {
        $taxonomies = (array) $args;
        $args = wp_parse_args($deprecated, $defaults);
        $args['taxonomy'] = $taxonomies;
    } else {
        $args = wp_parse_args($args, $defaults);
        if (isset($args['taxonomy']) && null !== $args['taxonomy']) {
            $args['taxonomy'] = (array) $args['taxonomy'];
        }
    }
    if (!empty($args['taxonomy'])) {
        foreach ($args['taxonomy'] as $taxonomy) {
            if (!taxonomy_exists($taxonomy)) {
                return new WP_Error('invalid_taxonomy', __('Invalid taxonomy.'));
            }
        }
    }
    // Don't pass suppress_filter to WP_Term_Query.
    $suppress_filter = $args['suppress_filter'];
    unset($args['suppress_filter']);
    $terms = $term_query->query($args);
    // Count queries are not filtered, for legacy reasons.
    if (!is_array($terms)) {
        return $terms;
    }
    if ($suppress_filter) {
        return $terms;
    }
    /**
     * Filters the found terms.
     *
     * @since 2.3.0
     * @since 4.6.0 Added the `$term_query` parameter.
     *
     * @param array         $terms      Array of found terms.
     * @param array|null    $taxonomies An array of taxonomies if known.
     * @param array         $args       An array of get_terms() arguments.
     * @param WP_Term_Query $term_query The WP_Term_Query object.
     */
    return apply_filters('get_terms', $terms, $term_query->query_vars['taxonomy'], $term_query->query_vars, $term_query);
}

WordPress Version: 5.9

/**
 * Retrieves 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 return type varies depending on the value passed to `$args['fields']`. See
 * WP_Term_Query::get_terms() for details. In all cases, a `WP_Error` object will
 * be returned if an invalid taxonomy is requested.
 *
 * 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.
 *
 * Prior to 4.5.0, the first parameter of `get_terms()` was a taxonomy or list of taxonomies:
 *
 *     $terms = get_terms( 'post_tag', array(
 *         'hide_empty' => false,
 *     ) );
 *
 * Since 4.5.0, taxonomies should be passed via the 'taxonomy' argument in the `$args` array:
 *
 *     $terms = get_terms( array(
 *         'taxonomy' => 'post_tag',
 *         'hide_empty' => false,
 *     ) );
 *
 * @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 Changed the function signature so that the `$args` array can be provided as the first parameter.
 *              Introduced 'meta_key' and 'meta_value' parameters. Introduced the ability to order results by metadata.
 * @since 4.8.0 Introduced 'suppress_filter' parameter.
 *
 * @internal The `$deprecated` parameter is parsed for backward compatibility only.
 *
 * @param array|string $args       Optional. Array or string of arguments. See WP_Term_Query::__construct()
 *                                 for information on accepted arguments. Default empty array.
 * @param array|string $deprecated Optional. Argument array, when using the legacy function parameter format.
 *                                 If present, this parameter will be interpreted as `$args`, and the first
 *                                 function parameter will be parsed as a taxonomy or array of taxonomies.
 *                                 Default empty.
 * @return WP_Term[]|int[]|string[]|string|WP_Error Array of terms, a count thereof as a numeric string,
 *                                                  or WP_Error if any of the taxonomies do not exist.
 *                                                  See the function description for more information.
 */
function get_terms($args = array(), $deprecated = '')
{
    $term_query = new WP_Term_Query();
    $defaults = array('suppress_filter' => false);
    /*
     * Legacy argument format ($taxonomy, $args) takes precedence.
     *
     * We detect legacy argument format by checking if
     * (a) a second non-empty parameter is passed, or
     * (b) the first parameter shares no keys with the default array (ie, it's a list of taxonomies)
     */
    $_args = wp_parse_args($args);
    $key_intersect = array_intersect_key($term_query->query_var_defaults, (array) $_args);
    $do_legacy_args = $deprecated || empty($key_intersect);
    if ($do_legacy_args) {
        $taxonomies = (array) $args;
        $args = wp_parse_args($deprecated, $defaults);
        $args['taxonomy'] = $taxonomies;
    } else {
        $args = wp_parse_args($args, $defaults);
        if (isset($args['taxonomy']) && null !== $args['taxonomy']) {
            $args['taxonomy'] = (array) $args['taxonomy'];
        }
    }
    if (!empty($args['taxonomy'])) {
        foreach ($args['taxonomy'] as $taxonomy) {
            if (!taxonomy_exists($taxonomy)) {
                return new WP_Error('invalid_taxonomy', __('Invalid taxonomy.'));
            }
        }
    }
    // Don't pass suppress_filter to WP_Term_Query.
    $suppress_filter = $args['suppress_filter'];
    unset($args['suppress_filter']);
    $terms = $term_query->query($args);
    // Count queries are not filtered, for legacy reasons.
    if (!is_array($terms)) {
        return $terms;
    }
    if ($suppress_filter) {
        return $terms;
    }
    /**
     * Filters the found terms.
     *
     * @since 2.3.0
     * @since 4.6.0 Added the `$term_query` parameter.
     *
     * @param array         $terms      Array of found terms.
     * @param array|null    $taxonomies An array of taxonomies if known.
     * @param array         $args       An array of get_terms() arguments.
     * @param WP_Term_Query $term_query The WP_Term_Query object.
     */
    return apply_filters('get_terms', $terms, $term_query->query_vars['taxonomy'], $term_query->query_vars, $term_query);
}

WordPress Version: 5.7

/**
 * Retrieves 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 return type varies depending on the value passed to `$args['fields']`. See
 * WP_Term_Query::get_terms() for details. In all cases, a `WP_Error` object will
 * be returned if an invalid taxonomy is requested.
 *
 * 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.
 *
 * Prior to 4.5.0, the first parameter of `get_terms()` was a taxonomy or list of taxonomies:
 *
 *     $terms = get_terms( 'post_tag', array(
 *         'hide_empty' => false,
 *     ) );
 *
 * Since 4.5.0, taxonomies should be passed via the 'taxonomy' argument in the `$args` array:
 *
 *     $terms = get_terms( array(
 *         'taxonomy' => 'post_tag',
 *         'hide_empty' => false,
 *     ) );
 *
 * @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 Changed the function signature so that the `$args` array can be provided as the first parameter.
 *              Introduced 'meta_key' and 'meta_value' parameters. Introduced the ability to order results by metadata.
 * @since 4.8.0 Introduced 'suppress_filter' parameter.
 *
 * @internal The `$deprecated` parameter is parsed for backward compatibility only.
 *
 * @param array|string $args       Optional. Array or string of arguments. See WP_Term_Query::__construct()
 *                                 for information on accepted arguments. Default empty array.
 * @param array|string $deprecated Optional. Argument array, when using the legacy function parameter format.
 *                                 If present, this parameter will be interpreted as `$args`, and the first
 *                                 function parameter will be parsed as a taxonomy or array of taxonomies.
 *                                 Default empty.
 * @return WP_Term[]|int[]|string[]|string|WP_Error Array of terms, a count thereof as a numeric string,
 *                                                  or WP_Error if any of the taxonomies do not exist.
 *                                                  See the function description for more information.
 */
function get_terms($args = array(), $deprecated = '')
{
    $term_query = new WP_Term_Query();
    $defaults = array('suppress_filter' => false);
    /*
     * Legacy argument format ($taxonomy, $args) takes precedence.
     *
     * We detect legacy argument format by checking if
     * (a) a second non-empty parameter is passed, or
     * (b) the first parameter shares no keys with the default array (ie, it's a list of taxonomies)
     */
    $_args = wp_parse_args($args);
    $key_intersect = array_intersect_key($term_query->query_var_defaults, (array) $_args);
    $do_legacy_args = $deprecated || empty($key_intersect);
    if ($do_legacy_args) {
        $taxonomies = (array) $args;
        $args = wp_parse_args($deprecated, $defaults);
        $args['taxonomy'] = $taxonomies;
    } else {
        $args = wp_parse_args($args, $defaults);
        if (isset($args['taxonomy']) && null !== $args['taxonomy']) {
            $args['taxonomy'] = (array) $args['taxonomy'];
        }
    }
    if (!empty($args['taxonomy'])) {
        foreach ($args['taxonomy'] as $taxonomy) {
            if (!taxonomy_exists($taxonomy)) {
                return new WP_Error('invalid_taxonomy', __('Invalid taxonomy.'));
            }
        }
    }
    // Don't pass suppress_filter to WP_Term_Query.
    $suppress_filter = $args['suppress_filter'];
    unset($args['suppress_filter']);
    $terms = $term_query->query($args);
    // Count queries are not filtered, for legacy reasons.
    if (!is_array($terms)) {
        return $terms;
    }
    if ($suppress_filter) {
        return $terms;
    }
    /**
     * Filters the found terms.
     *
     * @since 2.3.0
     * @since 4.6.0 Added the `$term_query` parameter.
     *
     * @param array         $terms      Array of found terms.
     * @param array         $taxonomies An array of taxonomies.
     * @param array         $args       An array of get_terms() arguments.
     * @param WP_Term_Query $term_query The WP_Term_Query object.
     */
    return apply_filters('get_terms', $terms, $term_query->query_vars['taxonomy'], $term_query->query_vars, $term_query);
}

WordPress Version: 5.6

/**
 * 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.
 *
 * Prior to 4.5.0, the first parameter of `get_terms()` was a taxonomy or list of taxonomies:
 *
 *     $terms = get_terms( 'post_tag', array(
 *         'hide_empty' => false,
 *     ) );
 *
 * Since 4.5.0, taxonomies should be passed via the 'taxonomy' argument in the `$args` array:
 *
 *     $terms = get_terms( array(
 *         'taxonomy' => 'post_tag',
 *         'hide_empty' => false,
 *     ) );
 *
 * @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 Changed the function signature so that the `$args` array can be provided as the first parameter.
 *              Introduced 'meta_key' and 'meta_value' parameters. Introduced the ability to order results by metadata.
 * @since 4.8.0 Introduced 'suppress_filter' parameter.
 *
 * @internal The `$deprecated` parameter is parsed for backward compatibility only.
 *
 * @param array|string $args       Optional. Array or string of arguments. See WP_Term_Query::__construct()
 *                                 for information on accepted arguments. Default empty array.
 * @param array|string $deprecated Optional. Argument array, when using the legacy function parameter format.
 *                                 If present, this parameter will be interpreted as `$args`, and the first
 *                                 function parameter will be parsed as a taxonomy or array of taxonomies.
 *                                 Default empty.
 * @return WP_Term[]|int|WP_Error Array of WP_Term instances, a count thereof,
 *                                or WP_Error if any of the taxonomies do not exist.
 */
function get_terms($args = array(), $deprecated = '')
{
    $term_query = new WP_Term_Query();
    $defaults = array('suppress_filter' => false);
    /*
     * Legacy argument format ($taxonomy, $args) takes precedence.
     *
     * We detect legacy argument format by checking if
     * (a) a second non-empty parameter is passed, or
     * (b) the first parameter shares no keys with the default array (ie, it's a list of taxonomies)
     */
    $_args = wp_parse_args($args);
    $key_intersect = array_intersect_key($term_query->query_var_defaults, (array) $_args);
    $do_legacy_args = $deprecated || empty($key_intersect);
    if ($do_legacy_args) {
        $taxonomies = (array) $args;
        $args = wp_parse_args($deprecated, $defaults);
        $args['taxonomy'] = $taxonomies;
    } else {
        $args = wp_parse_args($args, $defaults);
        if (isset($args['taxonomy']) && null !== $args['taxonomy']) {
            $args['taxonomy'] = (array) $args['taxonomy'];
        }
    }
    if (!empty($args['taxonomy'])) {
        foreach ($args['taxonomy'] as $taxonomy) {
            if (!taxonomy_exists($taxonomy)) {
                return new WP_Error('invalid_taxonomy', __('Invalid taxonomy.'));
            }
        }
    }
    // Don't pass suppress_filter to WP_Term_Query.
    $suppress_filter = $args['suppress_filter'];
    unset($args['suppress_filter']);
    $terms = $term_query->query($args);
    // Count queries are not filtered, for legacy reasons.
    if (!is_array($terms)) {
        return $terms;
    }
    if ($suppress_filter) {
        return $terms;
    }
    /**
     * Filters the found terms.
     *
     * @since 2.3.0
     * @since 4.6.0 Added the `$term_query` parameter.
     *
     * @param array         $terms      Array of found terms.
     * @param array         $taxonomies An array of taxonomies.
     * @param array         $args       An array of get_terms() arguments.
     * @param WP_Term_Query $term_query The WP_Term_Query object.
     */
    return apply_filters('get_terms', $terms, $term_query->query_vars['taxonomy'], $term_query->query_vars, $term_query);
}

WordPress Version: 5.5

/**
 * 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.
 *
 * Prior to 4.5.0, the first parameter of `get_terms()` was a taxonomy or list of taxonomies:
 *
 *     $terms = get_terms( 'post_tag', array(
 *         'hide_empty' => false,
 *     ) );
 *
 * Since 4.5.0, taxonomies should be passed via the 'taxonomy' argument in the `$args` array:
 *
 *     $terms = get_terms( array(
 *         'taxonomy' => 'post_tag',
 *         'hide_empty' => false,
 *     ) );
 *
 * @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 Changed the function signature so that the `$args` array can be provided as the first parameter.
 *              Introduced 'meta_key' and 'meta_value' parameters. Introduced the ability to order results by metadata.
 * @since 4.8.0 Introduced 'suppress_filter' parameter.
 *
 * @internal The `$deprecated` parameter is parsed for backward compatibility only.
 *
 * @param array|string $args       Optional. Array or string of arguments. See WP_Term_Query::__construct()
 *                                 for information on accepted arguments. Default empty.
 * @param array|string $deprecated Argument array, when using the legacy function parameter format. If present,
 *                                 this parameter will be interpreted as `$args`, and the first function parameter
 *                                 will be parsed as a taxonomy or array of taxonomies.
 * @return WP_Term[]|int|WP_Error Array of WP_Term instances, a count thereof,
 *                                or WP_Error if any of the taxonomies do not exist.
 */
function get_terms($args = array(), $deprecated = '')
{
    $term_query = new WP_Term_Query();
    $defaults = array('suppress_filter' => false);
    /*
     * Legacy argument format ($taxonomy, $args) takes precedence.
     *
     * We detect legacy argument format by checking if
     * (a) a second non-empty parameter is passed, or
     * (b) the first parameter shares no keys with the default array (ie, it's a list of taxonomies)
     */
    $_args = wp_parse_args($args);
    $key_intersect = array_intersect_key($term_query->query_var_defaults, (array) $_args);
    $do_legacy_args = $deprecated || empty($key_intersect);
    if ($do_legacy_args) {
        $taxonomies = (array) $args;
        $args = wp_parse_args($deprecated, $defaults);
        $args['taxonomy'] = $taxonomies;
    } else {
        $args = wp_parse_args($args, $defaults);
        if (isset($args['taxonomy']) && null !== $args['taxonomy']) {
            $args['taxonomy'] = (array) $args['taxonomy'];
        }
    }
    if (!empty($args['taxonomy'])) {
        foreach ($args['taxonomy'] as $taxonomy) {
            if (!taxonomy_exists($taxonomy)) {
                return new WP_Error('invalid_taxonomy', __('Invalid taxonomy.'));
            }
        }
    }
    // Don't pass suppress_filter to WP_Term_Query.
    $suppress_filter = $args['suppress_filter'];
    unset($args['suppress_filter']);
    $terms = $term_query->query($args);
    // Count queries are not filtered, for legacy reasons.
    if (!is_array($terms)) {
        return $terms;
    }
    if ($suppress_filter) {
        return $terms;
    }
    /**
     * Filters the found terms.
     *
     * @since 2.3.0
     * @since 4.6.0 Added the `$term_query` parameter.
     *
     * @param array         $terms      Array of found terms.
     * @param array         $taxonomies An array of taxonomies.
     * @param array         $args       An array of get_terms() arguments.
     * @param WP_Term_Query $term_query The WP_Term_Query object.
     */
    return apply_filters('get_terms', $terms, $term_query->query_vars['taxonomy'], $term_query->query_vars, $term_query);
}

WordPress Version: 5.4

/**
 * 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.
 *
 * Prior to 4.5.0, the first parameter of `get_terms()` was a taxonomy or list of taxonomies:
 *
 *     $terms = get_terms( 'post_tag', array(
 *         'hide_empty' => false,
 *     ) );
 *
 * Since 4.5.0, taxonomies should be passed via the 'taxonomy' argument in the `$args` array:
 *
 *     $terms = get_terms( array(
 *         'taxonomy' => 'post_tag',
 *         'hide_empty' => false,
 *     ) );
 *
 * @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 Changed the function signature so that the `$args` array can be provided as the first parameter.
 *              Introduced 'meta_key' and 'meta_value' parameters. Introduced the ability to order results by metadata.
 * @since 4.8.0 Introduced 'suppress_filter' parameter.
 *
 * @internal The `$deprecated` parameter is parsed for backward compatibility only.
 *
 * @param array|string $args       Optional. Array or string of arguments. See WP_Term_Query::__construct()
 *                                 for information on accepted arguments. Default empty.
 * @param array|string $deprecated Argument array, when using the legacy function parameter format. If present, this
 *                                 parameter will be interpreted as `$args`, and the first function parameter will
 *                                 be parsed as a taxonomy or array of taxonomies.
 * @return WP_Term[]|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($args = array(), $deprecated = '')
{
    $term_query = new WP_Term_Query();
    $defaults = array('suppress_filter' => false);
    /*
     * Legacy argument format ($taxonomy, $args) takes precedence.
     *
     * We detect legacy argument format by checking if
     * (a) a second non-empty parameter is passed, or
     * (b) the first parameter shares no keys with the default array (ie, it's a list of taxonomies)
     */
    $_args = wp_parse_args($args);
    $key_intersect = array_intersect_key($term_query->query_var_defaults, (array) $_args);
    $do_legacy_args = $deprecated || empty($key_intersect);
    if ($do_legacy_args) {
        $taxonomies = (array) $args;
        $args = wp_parse_args($deprecated, $defaults);
        $args['taxonomy'] = $taxonomies;
    } else {
        $args = wp_parse_args($args, $defaults);
        if (isset($args['taxonomy']) && null !== $args['taxonomy']) {
            $args['taxonomy'] = (array) $args['taxonomy'];
        }
    }
    if (!empty($args['taxonomy'])) {
        foreach ($args['taxonomy'] as $taxonomy) {
            if (!taxonomy_exists($taxonomy)) {
                return new WP_Error('invalid_taxonomy', __('Invalid taxonomy.'));
            }
        }
    }
    // Don't pass suppress_filter to WP_Term_Query.
    $suppress_filter = $args['suppress_filter'];
    unset($args['suppress_filter']);
    $terms = $term_query->query($args);
    // Count queries are not filtered, for legacy reasons.
    if (!is_array($terms)) {
        return $terms;
    }
    if ($suppress_filter) {
        return $terms;
    }
    /**
     * Filters the found terms.
     *
     * @since 2.3.0
     * @since 4.6.0 Added the `$term_query` parameter.
     *
     * @param array         $terms      Array of found terms.
     * @param array         $taxonomies An array of taxonomies.
     * @param array         $args       An array of get_terms() arguments.
     * @param WP_Term_Query $term_query The WP_Term_Query object.
     */
    return apply_filters('get_terms', $terms, $term_query->query_vars['taxonomy'], $term_query->query_vars, $term_query);
}

WordPress Version: 5.3

/**
 * 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.
 *
 * Prior to 4.5.0, the first parameter of `get_terms()` was a taxonomy or list of taxonomies:
 *
 *     $terms = get_terms( 'post_tag', array(
 *         'hide_empty' => false,
 *     ) );
 *
 * Since 4.5.0, taxonomies should be passed via the 'taxonomy' argument in the `$args` array:
 *
 *     $terms = get_terms( array(
 *         'taxonomy' => 'post_tag',
 *         'hide_empty' => false,
 *     ) );
 *
 * @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 Changed the function signature so that the `$args` array can be provided as the first parameter.
 *              Introduced 'meta_key' and 'meta_value' parameters. Introduced the ability to order results by metadata.
 * @since 4.8.0 Introduced 'suppress_filter' parameter.
 *
 * @internal The `$deprecated` parameter is parsed for backward compatibility only.
 *
 * @param array|string $args       Optional. Array or string of arguments. See WP_Term_Query::__construct()
 *                                 for information on accepted arguments. Default empty.
 * @param array|string $deprecated Argument array, when using the legacy function parameter format. If present, this
 *                                 parameter will be interpreted as `$args`, and the first function parameter will
 *                                 be parsed as a taxonomy or array of taxonomies.
 * @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($args = array(), $deprecated = '')
{
    $term_query = new WP_Term_Query();
    $defaults = array('suppress_filter' => false);
    /*
     * Legacy argument format ($taxonomy, $args) takes precedence.
     *
     * We detect legacy argument format by checking if
     * (a) a second non-empty parameter is passed, or
     * (b) the first parameter shares no keys with the default array (ie, it's a list of taxonomies)
     */
    $_args = wp_parse_args($args);
    $key_intersect = array_intersect_key($term_query->query_var_defaults, (array) $_args);
    $do_legacy_args = $deprecated || empty($key_intersect);
    if ($do_legacy_args) {
        $taxonomies = (array) $args;
        $args = wp_parse_args($deprecated, $defaults);
        $args['taxonomy'] = $taxonomies;
    } else {
        $args = wp_parse_args($args, $defaults);
        if (isset($args['taxonomy']) && null !== $args['taxonomy']) {
            $args['taxonomy'] = (array) $args['taxonomy'];
        }
    }
    if (!empty($args['taxonomy'])) {
        foreach ($args['taxonomy'] as $taxonomy) {
            if (!taxonomy_exists($taxonomy)) {
                return new WP_Error('invalid_taxonomy', __('Invalid taxonomy.'));
            }
        }
    }
    // Don't pass suppress_filter to WP_Term_Query.
    $suppress_filter = $args['suppress_filter'];
    unset($args['suppress_filter']);
    $terms = $term_query->query($args);
    // Count queries are not filtered, for legacy reasons.
    if (!is_array($terms)) {
        return $terms;
    }
    if ($suppress_filter) {
        return $terms;
    }
    /**
     * Filters the found terms.
     *
     * @since 2.3.0
     * @since 4.6.0 Added the `$term_query` parameter.
     *
     * @param array         $terms      Array of found terms.
     * @param array         $taxonomies An array of taxonomies.
     * @param array         $args       An array of get_terms() arguments.
     * @param WP_Term_Query $term_query The WP_Term_Query object.
     */
    return apply_filters('get_terms', $terms, $term_query->query_vars['taxonomy'], $term_query->query_vars, $term_query);
}

WordPress Version: 4.9

/**
 * 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.
 *
 * Prior to 4.5.0, the first parameter of `get_terms()` was a taxonomy or list of taxonomies:
 *
 *     $terms = get_terms( 'post_tag', array(
 *         'hide_empty' => false,
 *     ) );
 *
 * Since 4.5.0, taxonomies should be passed via the 'taxonomy' argument in the `$args` array:
 *
 *     $terms = get_terms( array(
 *         'taxonomy' => 'post_tag',
 *         'hide_empty' => false,
 *     ) );
 *
 * @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 Changed the function signature so that the `$args` array can be provided as the first parameter.
 *              Introduced 'meta_key' and 'meta_value' parameters. Introduced the ability to order results by metadata.
 * @since 4.8.0 Introduced 'suppress_filter' parameter.
 *
 * @internal The `$deprecated` parameter is parsed for backward compatibility only.
 *
 * @param string|array $args       Optional. Array or string of arguments. See WP_Term_Query::__construct()
 *                                 for information on accepted arguments. Default empty.
 * @param array        $deprecated Argument array, when using the legacy function parameter format. If present, this
 *                                 parameter will be interpreted as `$args`, and the first function parameter will
 *                                 be parsed as a taxonomy or array of taxonomies.
 * @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($args = array(), $deprecated = '')
{
    $term_query = new WP_Term_Query();
    $defaults = array('suppress_filter' => false);
    /*
     * Legacy argument format ($taxonomy, $args) takes precedence.
     *
     * We detect legacy argument format by checking if
     * (a) a second non-empty parameter is passed, or
     * (b) the first parameter shares no keys with the default array (ie, it's a list of taxonomies)
     */
    $_args = wp_parse_args($args);
    $key_intersect = array_intersect_key($term_query->query_var_defaults, (array) $_args);
    $do_legacy_args = $deprecated || empty($key_intersect);
    if ($do_legacy_args) {
        $taxonomies = (array) $args;
        $args = wp_parse_args($deprecated, $defaults);
        $args['taxonomy'] = $taxonomies;
    } else {
        $args = wp_parse_args($args, $defaults);
        if (isset($args['taxonomy']) && null !== $args['taxonomy']) {
            $args['taxonomy'] = (array) $args['taxonomy'];
        }
    }
    if (!empty($args['taxonomy'])) {
        foreach ($args['taxonomy'] as $taxonomy) {
            if (!taxonomy_exists($taxonomy)) {
                return new WP_Error('invalid_taxonomy', __('Invalid taxonomy.'));
            }
        }
    }
    // Don't pass suppress_filter to WP_Term_Query.
    $suppress_filter = $args['suppress_filter'];
    unset($args['suppress_filter']);
    $terms = $term_query->query($args);
    // Count queries are not filtered, for legacy reasons.
    if (!is_array($terms)) {
        return $terms;
    }
    if ($suppress_filter) {
        return $terms;
    }
    /**
     * Filters the found terms.
     *
     * @since 2.3.0
     * @since 4.6.0 Added the `$term_query` parameter.
     *
     * @param array         $terms      Array of found terms.
     * @param array         $taxonomies An array of taxonomies.
     * @param array         $args       An array of get_terms() arguments.
     * @param WP_Term_Query $term_query The WP_Term_Query object.
     */
    return apply_filters('get_terms', $terms, $term_query->query_vars['taxonomy'], $term_query->query_vars, $term_query);
}

WordPress Version: 4.8

/**
 * 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.
 *
 * Prior to 4.5.0, the first parameter of `get_terms()` was a taxonomy or list of taxonomies:
 *
 *     $terms = get_terms( 'post_tag', array(
 *         'hide_empty' => false,
 *     ) );
 *
 * Since 4.5.0, taxonomies should be passed via the 'taxonomy' argument in the `$args` array:
 *
 *     $terms = get_terms( array(
 *         'taxonomy' => 'post_tag',
 *         'hide_empty' => false,
 *     ) );
 *
 * @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 Changed the function signature so that the `$args` array can be provided as the first parameter.
 *              Introduced 'meta_key' and 'meta_value' parameters. Introduced the ability to order results by metadata.
 * @since 4.8.0 Introduced 'suppress_filter' parameter.
 *
 * @internal The `$deprecated` parameter is parsed for backward compatibility only.
 *
 * @global wpdb  $wpdb WordPress database abstraction object.
 * @global array $wp_filter
 *
 * @param string|array $args       Optional. Array or string of arguments. See WP_Term_Query::__construct()
 *                                 for information on accepted arguments. Default empty.
 * @param array        $deprecated Argument array, when using the legacy function parameter format. If present, this
 *                                 parameter will be interpreted as `$args`, and the first function parameter will
 *                                 be parsed as a taxonomy or array of taxonomies.
 * @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($args = array(), $deprecated = '')
{
    global $wpdb;
    $term_query = new WP_Term_Query();
    $defaults = array('suppress_filter' => false);
    /*
     * Legacy argument format ($taxonomy, $args) takes precedence.
     *
     * We detect legacy argument format by checking if
     * (a) a second non-empty parameter is passed, or
     * (b) the first parameter shares no keys with the default array (ie, it's a list of taxonomies)
     */
    $_args = wp_parse_args($args);
    $key_intersect = array_intersect_key($term_query->query_var_defaults, (array) $_args);
    $do_legacy_args = $deprecated || empty($key_intersect);
    if ($do_legacy_args) {
        $taxonomies = (array) $args;
        $args = wp_parse_args($deprecated, $defaults);
        $args['taxonomy'] = $taxonomies;
    } else {
        $args = wp_parse_args($args, $defaults);
        if (isset($args['taxonomy']) && null !== $args['taxonomy']) {
            $args['taxonomy'] = (array) $args['taxonomy'];
        }
    }
    if (!empty($args['taxonomy'])) {
        foreach ($args['taxonomy'] as $taxonomy) {
            if (!taxonomy_exists($taxonomy)) {
                return new WP_Error('invalid_taxonomy', __('Invalid taxonomy.'));
            }
        }
    }
    // Don't pass suppress_filter to WP_Term_Query.
    $suppress_filter = $args['suppress_filter'];
    unset($args['suppress_filter']);
    $terms = $term_query->query($args);
    // Count queries are not filtered, for legacy reasons.
    if (!is_array($terms)) {
        return $terms;
    }
    if ($suppress_filter) {
        return $terms;
    }
    /**
     * Filters the found terms.
     *
     * @since 2.3.0
     * @since 4.6.0 Added the `$term_query` parameter.
     *
     * @param array         $terms      Array of found terms.
     * @param array         $taxonomies An array of taxonomies.
     * @param array         $args       An array of get_terms() arguments.
     * @param WP_Term_Query $term_query The WP_Term_Query object.
     */
    return apply_filters('get_terms', $terms, $term_query->query_vars['taxonomy'], $term_query->query_vars, $term_query);
}

WordPress Version: 4.6

/**
 * 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.
 *
 * Prior to 4.5.0, the first parameter of `get_terms()` was a taxonomy or list of taxonomies:
 *
 *     $terms = get_terms( 'post_tag', array(
 *         'hide_empty' => false,
 *     ) );
 *
 * Since 4.5.0, taxonomies should be passed via the 'taxonomy' argument in the `$args` array:
 *
 *     $terms = get_terms( array(
 *         'taxonomy' => 'post_tag',
 *         'hide_empty' => false,
 *     ) );
 *
 * @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 Changed the function signature so that the `$args` array can be provided as the first parameter.
 *              Introduced 'meta_key' and 'meta_value' parameters. Introduced the ability to order results by metadata.
 *
 * @internal The `$deprecated` parameter is parsed for backward compatibility only.
 *
 * @global wpdb  $wpdb WordPress database abstraction object.
 * @global array $wp_filter
 *
 * @param array|string $args {
 *     Optional. Array or string of arguments to get terms.
 *
 *     @type string|array $taxonomy               Taxonomy name, or array of taxonomies, to which results should
 *                                                be limited.
 *     @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`.
 * }
 * @param array $deprecated Argument array, when using the legacy function parameter format. If present, this
 *                          parameter will be interpreted as `$args`, and the first function parameter will
 *                          be parsed as a taxonomy or array of taxonomies.
 * @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($args = array(), $deprecated = '')
{
    global $wpdb;
    $term_query = new WP_Term_Query();
    /*
     * Legacy argument format ($taxonomy, $args) takes precedence.
     *
     * We detect legacy argument format by checking if
     * (a) a second non-empty parameter is passed, or
     * (b) the first parameter shares no keys with the default array (ie, it's a list of taxonomies)
     */
    $_args = wp_parse_args($args);
    $key_intersect = array_intersect_key($term_query->query_var_defaults, (array) $_args);
    $do_legacy_args = $deprecated || empty($key_intersect);
    if ($do_legacy_args) {
        $taxonomies = (array) $args;
        $args = wp_parse_args($deprecated);
        $args['taxonomy'] = $taxonomies;
    } else {
        $args = wp_parse_args($args);
        if (isset($args['taxonomy']) && null !== $args['taxonomy']) {
            $args['taxonomy'] = (array) $args['taxonomy'];
        }
    }
    if (!empty($args['taxonomy'])) {
        foreach ($args['taxonomy'] as $taxonomy) {
            if (!taxonomy_exists($taxonomy)) {
                return new WP_Error('invalid_taxonomy', __('Invalid taxonomy.'));
            }
        }
    }
    $terms = $term_query->query($args);
    // Count queries are not filtered, for legacy reasons.
    if (!is_array($terms)) {
        return $terms;
    }
    /**
     * Filters the found terms.
     *
     * @since 2.3.0
     * @since 4.6.0 Added the `$term_query` parameter.
     *
     * @param array         $terms      Array of found terms.
     * @param array         $taxonomies An array of taxonomies.
     * @param array         $args       An array of get_terms() arguments.
     * @param WP_Term_Query $term_query The WP_Term_Query object.
     */
    return apply_filters('get_terms', $terms, $term_query->query_vars['taxonomy'], $term_query->query_vars, $term_query);
}

WordPress Version: 4.5

/**
 * 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.
 *
 * Prior to 4.5.0, the first parameter of `get_terms()` was a taxonomy or list of taxonomies:
 *
 *     $terms = get_terms( 'post_tag', array(
 *         'hide_empty' => false,
 *     ) );
 *
 * Since 4.5.0, taxonomies should be passed via the 'taxonomy' argument in the `$args` array:
 *
 *     $terms = get_terms( array(
 *         'taxonomy' => 'post_tag',
 *         'hide_empty' => false,
 *     ) );
 *
 * @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 Changed the function signature so that the `$args` array can be provided as the first parameter.
 *              Introduced 'meta_key' and 'meta_value' parameters. Introduced the ability to order results by metadata.
 *
 * @internal The `$deprecated` parameter is parsed for backward compatibility only.
 *
 * @global wpdb  $wpdb WordPress database abstraction object.
 * @global array $wp_filter
 *
 * @param array|string $args {
 *     Optional. Array or string of arguments to get terms.
 *
 *     @type string|array $taxonomy               Taxonomy name, or array of taxonomies, to which results should
 *                                                be limited.
 *     @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`.
 * }
 * @param array $deprecated Argument array, when using the legacy function parameter format. If present, this
 *                          parameter will be interpreted as `$args`, and the first function parameter will
 *                          be parsed as a taxonomy or array of taxonomies.
 * @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($args = array(), $deprecated = '')
{
    global $wpdb;
    $defaults = array('taxonomy' => null, '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' => '');
    /*
     * Legacy argument format ($taxonomy, $args) takes precedence.
     *
     * We detect legacy argument format by checking if
     * (a) a second non-empty parameter is passed, or
     * (b) the first parameter shares no keys with the default array (ie, it's a list of taxonomies)
     */
    $key_intersect = array_intersect_key($defaults, (array) $args);
    $do_legacy_args = $deprecated || empty($key_intersect);
    $taxonomies = null;
    if ($do_legacy_args) {
        $taxonomies = (array) $args;
        $args = $deprecated;
    } elseif (isset($args['taxonomy']) && null !== $args['taxonomy']) {
        $taxonomies = (array) $args['taxonomy'];
        unset($args['taxonomy']);
    }
    $empty_array = array();
    if ($taxonomies) {
        foreach ($taxonomies as $taxonomy) {
            if (!taxonomy_exists($taxonomy)) {
                return new WP_Error('invalid_taxonomy', __('Invalid taxonomy'));
            }
        }
    }
    /**
     * 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;
    if ($taxonomies) {
        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_conditions = array();
    if ($taxonomies) {
        $where_conditions[] = "tt.taxonomy IN ('" . implode("', '", array_map('esc_sql', $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)) {
        $where_conditions[] = 't.term_id IN ( ' . $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 = '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)) {
        // Must do string manipulation here for backward compatibility with filter.
        $where_conditions[] = preg_replace('/^\s*AND\s*/', '', $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_conditions[] = "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_conditions[] = "t.slug IN ('" . implode("', '", $slug) . "')";
        } else {
            $slug = sanitize_title($args['slug']);
            $where_conditions[] = "t.slug = '{$slug}'";
        }
    }
    if (!empty($args['name__like'])) {
        $where_conditions[] = $wpdb->prepare("t.name LIKE %s", '%' . $wpdb->esc_like($args['name__like']) . '%');
    }
    if (!empty($args['description__like'])) {
        $where_conditions[] = $wpdb->prepare("tt.description LIKE %s", '%' . $wpdb->esc_like($args['description__like']) . '%');
    }
    if ('' !== $parent) {
        $parent = (int) $parent;
        $where_conditions[] = "tt.parent = '{$parent}'";
    }
    $hierarchical = $args['hierarchical'];
    if ('count' == $args['fields']) {
        $hierarchical = false;
    }
    if ($args['hide_empty'] && !$hierarchical) {
        $where_conditions[] = '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_conditions[] = $wpdb->prepare('((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_conditions[] = preg_replace('/^\s*AND\s*/', '', $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";
    $where = implode(' AND ', $where_conditions);
    $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'] : '';
    if ($where) {
        $where = "WHERE {$where}";
    }
    $query = "SELECT {$distinct} {$fields} FROM {$wpdb->terms} AS t {$join} {$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;
    }
    // Hierarchical queries are not limited, so 'offset' and 'number' must be handled now.
    if ($hierarchical && $number && is_array($terms)) {
        if ($offset >= count($terms)) {
            $terms = array();
        } else {
            $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);
}

WordPress Version: .20

/**
 * 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.
 *
 * @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,
 *                                                or 'none' to skip ORDER BY. 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.
 * }
 * @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("', '", array_map('esc_sql', $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) {
            $_name = 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 = '';
    if (!empty($args['meta_query'])) {
        $mquery = new WP_Meta_Query($args['meta_query']);
        $mq_sql = $mquery->get_sql('term', 't', 'term_id');
        $join .= $mq_sql['join'];
        $where .= $mq_sql['where'];
        $distinct .= "DISTINCT";
    }
    $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);
    }
    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);
}

WordPress Version: 4.2

/**
 * 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.
 *
 * @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,
 *                                                or 'none' to skip ORDER BY. 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.
 * }
 * @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) {
            $_name = 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 = '';
    if (!empty($args['meta_query'])) {
        $mquery = new WP_Meta_Query($args['meta_query']);
        $mq_sql = $mquery->get_sql('term', 't', 'term_id');
        $join .= $mq_sql['join'];
        $where .= $mq_sql['where'];
        $distinct .= "DISTINCT";
    }
    $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);
    }
    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);
}

WordPress Version: .10

/**
 * 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.
 *
 * @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,
 *                                                or 'none' to skip ORDER BY. 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.
 * }
 * @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("', '", array_map('esc_sql', $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) {
            $_name = 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 = '';
    if (!empty($args['meta_query'])) {
        $mquery = new WP_Meta_Query($args['meta_query']);
        $mq_sql = $mquery->get_sql('term', 't', 'term_id');
        $join .= $mq_sql['join'];
        $where .= $mq_sql['where'];
        $distinct .= "DISTINCT";
    }
    $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);
    }
    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);
}

WordPress Version: 4.1

/**
 * 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.
 *
 * @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,
 *                                                or 'none' to skip ORDER BY. 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.
 * }
 * @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) {
            $_name = 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 = '';
    if (!empty($args['meta_query'])) {
        $mquery = new WP_Meta_Query($args['meta_query']);
        $mq_sql = $mquery->get_sql('term', 't', 'term_id');
        $join .= $mq_sql['join'];
        $where .= $mq_sql['where'];
        $distinct .= "DISTINCT";
    }
    $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);
    }
    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);
}

WordPress Version: 4.4

/**
 * 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.
 *
 * @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,
 *                                                or 'none' to skip ORDER BY. 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.
 * }
 * @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) {
            $_name = 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 = '';
    if (!empty($args['meta_query'])) {
        $mquery = new WP_Meta_Query($args['meta_query']);
        $mq_sql = $mquery->get_sql('term', 't', 'term_id');
        $join .= $mq_sql['join'];
        $where .= $mq_sql['where'];
    }
    $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', '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'] : '';
    $orderby = isset($clauses['orderby']) ? $clauses['orderby'] : '';
    $order = isset($clauses['order']) ? $clauses['order'] : '';
    $limits = isset($clauses['limits']) ? $clauses['limits'] : '';
    $query = "SELECT {$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);
    }
    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);
}

WordPress Version: .30

/**
 * 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.
 *
 * @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,
 *                                           or 'none' to skip ORDER BY. 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
 *                                           term objects), 'ids' or 'names' (returns an array of integers
 *                                           or strings, respectively. 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'.
 * }
 * @return array|int|WP_Error List of Term Objects 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, 'exclude' => array(), 'exclude_tree' => array(), 'include' => array(), 'number' => '', 'fields' => 'all', 'name' => '', 'slug' => '', 'parent' => '', 'childless' => false, 'hierarchical' => true, 'child_of' => 0, 'get' => '', 'name__like' => '', 'description__like' => '', 'pad_counts' => false, 'offset' => '', 'search' => '', 'cache_domain' => 'core');
    $args = wp_parse_args($args, $defaults);
    $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_term() 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;
        }
    }
    // $args can be whatever, only use the args defined in defaults to compute the key.
    $filter_key = has_filter('list_terms_exclusions') ? serialize($GLOBALS['wp_filter']['list_terms_exclusions']) : '';
    $key = md5(serialize(wp_array_slice_assoc($args, array_keys($defaults))) . serialize($taxonomies) . $filter_key);
    $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) {
        /**
         * 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);
    }
    $_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) {
        $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("', '", array_map('esc_sql', $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) {
            $_name = 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);
    }
    $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', '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'] : '';
    $orderby = isset($clauses['orderby']) ? $clauses['orderby'] : '';
    $order = isset($clauses['order']) ? $clauses['order'] : '';
    $limits = isset($clauses['limits']) ? $clauses['limits'] : '';
    $query = "SELECT {$fields} FROM {$wpdb->terms} AS t {$join} WHERE {$where} {$orderby} {$order} {$limits}";
    if ('count' == $_fields) {
        return $wpdb->get_var($query);
    }
    $terms = $wpdb->get_results($query);
    if ('all' == $_fields) {
        update_term_cache($terms);
    }
    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);
    }
    wp_cache_add($cache_key, $terms, 'terms', DAY_IN_SECONDS);
    /** This filter is documented in wp-includes/taxonomy */
    return apply_filters('get_terms', $terms, $taxonomies, $args);
}

WordPress Version: 3.3

/**
 * 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.
 *
 * @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,
 *                                           or 'none' to skip ORDER BY. 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
 *                                           term objects), 'ids' or 'names' (returns an array of integers
 *                                           or strings, respectively. 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'.
 * }
 * @return array|int|WP_Error List of Term Objects 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, 'exclude' => array(), 'exclude_tree' => array(), 'include' => array(), 'number' => '', 'fields' => 'all', 'name' => '', 'slug' => '', 'parent' => '', 'childless' => false, 'hierarchical' => true, 'child_of' => 0, 'get' => '', 'name__like' => '', 'description__like' => '', 'pad_counts' => false, 'offset' => '', 'search' => '', 'cache_domain' => 'core');
    $args = wp_parse_args($args, $defaults);
    $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_term() 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;
        }
    }
    // $args can be whatever, only use the args defined in defaults to compute the key.
    $filter_key = has_filter('list_terms_exclusions') ? serialize($GLOBALS['wp_filter']['list_terms_exclusions']) : '';
    $key = md5(serialize(wp_array_slice_assoc($args, array_keys($defaults))) . serialize($taxonomies) . $filter_key);
    $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) {
        /**
         * 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);
    }
    $_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) {
        $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) {
            $_name = 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);
    }
    $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', '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'] : '';
    $orderby = isset($clauses['orderby']) ? $clauses['orderby'] : '';
    $order = isset($clauses['order']) ? $clauses['order'] : '';
    $limits = isset($clauses['limits']) ? $clauses['limits'] : '';
    $query = "SELECT {$fields} FROM {$wpdb->terms} AS t {$join} WHERE {$where} {$orderby} {$order} {$limits}";
    if ('count' == $_fields) {
        return $wpdb->get_var($query);
    }
    $terms = $wpdb->get_results($query);
    if ('all' == $_fields) {
        update_term_cache($terms);
    }
    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);
    }
    wp_cache_add($cache_key, $terms, 'terms', DAY_IN_SECONDS);
    /** This filter is documented in wp-includes/taxonomy */
    return apply_filters('get_terms', $terms, $taxonomies, $args);
}

WordPress Version: .20

/**
 * 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.
 *
 * @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,
 *                                           or 'none' to skip ORDER BY. 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
 *                                           term objects), 'ids' or 'names' (returns an array of integers
 *                                           or strings, respectively. 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'.
 * }
 * @return array|int|WP_Error List of Term Objects 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, 'exclude' => array(), 'exclude_tree' => array(), 'include' => array(), 'number' => '', 'fields' => 'all', 'name' => '', 'slug' => '', 'parent' => '', 'childless' => false, 'hierarchical' => true, 'child_of' => 0, 'get' => '', 'name__like' => '', 'description__like' => '', 'pad_counts' => false, 'offset' => '', 'search' => '', 'cache_domain' => 'core');
    $args = wp_parse_args($args, $defaults);
    $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_term() 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;
        }
    }
    // $args can be whatever, only use the args defined in defaults to compute the key.
    $filter_key = has_filter('list_terms_exclusions') ? serialize($GLOBALS['wp_filter']['list_terms_exclusions']) : '';
    $key = md5(serialize(wp_array_slice_assoc($args, array_keys($defaults))) . serialize($taxonomies) . $filter_key);
    $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) {
        /**
         * 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);
    }
    $_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) {
        $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("', '", array_map('esc_sql', $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) {
            $_name = 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);
    }
    $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', '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'] : '';
    $orderby = isset($clauses['orderby']) ? $clauses['orderby'] : '';
    $order = isset($clauses['order']) ? $clauses['order'] : '';
    $limits = isset($clauses['limits']) ? $clauses['limits'] : '';
    $query = "SELECT {$fields} FROM {$wpdb->terms} AS t {$join} WHERE {$where} {$orderby} {$order} {$limits}";
    if ('count' == $_fields) {
        return $wpdb->get_var($query);
    }
    $terms = $wpdb->get_results($query);
    if ('all' == $_fields) {
        update_term_cache($terms);
    }
    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);
    }
    wp_cache_add($cache_key, $terms, 'terms', DAY_IN_SECONDS);
    /** This filter is documented in wp-includes/taxonomy */
    return apply_filters('get_terms', $terms, $taxonomies, $args);
}

WordPress Version: 3.2

/**
 * 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.
 *
 * @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,
 *                                           or 'none' to skip ORDER BY. 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
 *                                           term objects), 'ids' or 'names' (returns an array of integers
 *                                           or strings, respectively. 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'.
 * }
 * @return array|int|WP_Error List of Term Objects 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, 'exclude' => array(), 'exclude_tree' => array(), 'include' => array(), 'number' => '', 'fields' => 'all', 'name' => '', 'slug' => '', 'parent' => '', 'childless' => false, 'hierarchical' => true, 'child_of' => 0, 'get' => '', 'name__like' => '', 'description__like' => '', 'pad_counts' => false, 'offset' => '', 'search' => '', 'cache_domain' => 'core');
    $args = wp_parse_args($args, $defaults);
    $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_term() 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;
        }
    }
    // $args can be whatever, only use the args defined in defaults to compute the key.
    $filter_key = has_filter('list_terms_exclusions') ? serialize($GLOBALS['wp_filter']['list_terms_exclusions']) : '';
    $key = md5(serialize(wp_array_slice_assoc($args, array_keys($defaults))) . serialize($taxonomies) . $filter_key);
    $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) {
        /**
         * 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);
    }
    $_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) {
        $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) {
            $_name = 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);
    }
    $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', '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'] : '';
    $orderby = isset($clauses['orderby']) ? $clauses['orderby'] : '';
    $order = isset($clauses['order']) ? $clauses['order'] : '';
    $limits = isset($clauses['limits']) ? $clauses['limits'] : '';
    $query = "SELECT {$fields} FROM {$wpdb->terms} AS t {$join} WHERE {$where} {$orderby} {$order} {$limits}";
    if ('count' == $_fields) {
        return $wpdb->get_var($query);
    }
    $terms = $wpdb->get_results($query);
    if ('all' == $_fields) {
        update_term_cache($terms);
    }
    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);
    }
    wp_cache_add($cache_key, $terms, 'terms', DAY_IN_SECONDS);
    /** This filter is documented in wp-includes/taxonomy */
    return apply_filters('get_terms', $terms, $taxonomies, $args);
}

WordPress Version: .10

/**
 * 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.
 *
 * @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,
 *                                           or 'none' to skip ORDER BY. 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
 *                                           term objects), 'ids' or 'names' (returns an array of integers
 *                                           or strings, respectively. 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'.
 * }
 * @return array|int|WP_Error List of Term Objects 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, 'exclude' => array(), 'exclude_tree' => array(), 'include' => array(), 'number' => '', 'fields' => 'all', 'name' => '', 'slug' => '', 'parent' => '', 'childless' => false, 'hierarchical' => true, 'child_of' => 0, 'get' => '', 'name__like' => '', 'description__like' => '', 'pad_counts' => false, 'offset' => '', 'search' => '', 'cache_domain' => 'core');
    $args = wp_parse_args($args, $defaults);
    $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_term() 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;
        }
    }
    // $args can be whatever, only use the args defined in defaults to compute the key.
    $filter_key = has_filter('list_terms_exclusions') ? serialize($GLOBALS['wp_filter']['list_terms_exclusions']) : '';
    $key = md5(serialize(wp_array_slice_assoc($args, array_keys($defaults))) . serialize($taxonomies) . $filter_key);
    $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) {
        /**
         * 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);
    }
    $_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) {
        $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("', '", array_map('esc_sql', $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) {
            $_name = 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);
    }
    $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', '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'] : '';
    $orderby = isset($clauses['orderby']) ? $clauses['orderby'] : '';
    $order = isset($clauses['order']) ? $clauses['order'] : '';
    $limits = isset($clauses['limits']) ? $clauses['limits'] : '';
    $query = "SELECT {$fields} FROM {$wpdb->terms} AS t {$join} WHERE {$where} {$orderby} {$order} {$limits}";
    if ('count' == $_fields) {
        return $wpdb->get_var($query);
    }
    $terms = $wpdb->get_results($query);
    if ('all' == $_fields) {
        update_term_cache($terms);
    }
    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);
    }
    wp_cache_add($cache_key, $terms, 'terms', DAY_IN_SECONDS);
    /** This filter is documented in wp-includes/taxonomy */
    return apply_filters('get_terms', $terms, $taxonomies, $args);
}

WordPress Version: 4.3

/**
 * 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.
 *
 * @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,
 *                                           or 'none' to skip ORDER BY. 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
 *                                           term objects), 'ids' or 'names' (returns an array of integers
 *                                           or strings, respectively. 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'.
 * }
 * @return array|int|WP_Error List of Term Objects 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, 'exclude' => array(), 'exclude_tree' => array(), 'include' => array(), 'number' => '', 'fields' => 'all', 'name' => '', 'slug' => '', 'parent' => '', 'childless' => false, 'hierarchical' => true, 'child_of' => 0, 'get' => '', 'name__like' => '', 'description__like' => '', 'pad_counts' => false, 'offset' => '', 'search' => '', 'cache_domain' => 'core');
    $args = wp_parse_args($args, $defaults);
    $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_term() 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;
        }
    }
    // $args can be whatever, only use the args defined in defaults to compute the key.
    $filter_key = has_filter('list_terms_exclusions') ? serialize($GLOBALS['wp_filter']['list_terms_exclusions']) : '';
    $key = md5(serialize(wp_array_slice_assoc($args, array_keys($defaults))) . serialize($taxonomies) . $filter_key);
    $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) {
        /**
         * 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);
    }
    $_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) {
        $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) {
            $_name = 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);
    }
    $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', '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'] : '';
    $orderby = isset($clauses['orderby']) ? $clauses['orderby'] : '';
    $order = isset($clauses['order']) ? $clauses['order'] : '';
    $limits = isset($clauses['limits']) ? $clauses['limits'] : '';
    $query = "SELECT {$fields} FROM {$wpdb->terms} AS t {$join} WHERE {$where} {$orderby} {$order} {$limits}";
    if ('count' == $_fields) {
        return $wpdb->get_var($query);
    }
    $terms = $wpdb->get_results($query);
    if ('all' == $_fields) {
        update_term_cache($terms);
    }
    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);
    }
    wp_cache_add($cache_key, $terms, 'terms', DAY_IN_SECONDS);
    /** This filter is documented in wp-includes/taxonomy */
    return apply_filters('get_terms', $terms, $taxonomies, $args);
}

WordPress Version: 2.8

/**
 * 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 '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 'list_terms_exclusions' filter passes the compiled exclusions along with
 * the $args.
 *
 * The '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.
 *
 * @global wpdb $wpdb WordPress database abstraction object.
 *
 * @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,
 *                                           or 'none' to skip ORDER BY. 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
 *                                           term objects), 'ids' or 'names' (returns an array of integers
 *                                           or strings, respectively. 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'.
 * }
 * @return array|WP_Error List of Term Objects 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)) {
            $error = new WP_Error('invalid_taxonomy', __('Invalid taxonomy'));
            return $error;
        }
    }
    $defaults = array('orderby' => 'name', 'order' => 'ASC', 'hide_empty' => true, 'exclude' => array(), 'exclude_tree' => array(), 'include' => array(), 'number' => '', 'fields' => 'all', 'name' => '', 'slug' => '', 'parent' => '', 'childless' => false, 'hierarchical' => true, 'child_of' => 0, 'get' => '', 'name__like' => '', 'description__like' => '', 'pad_counts' => false, 'offset' => '', 'search' => '', 'cache_domain' => 'core');
    $args = wp_parse_args($args, $defaults);
    $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 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;
        }
    }
    // $args can be whatever, only use the args defined in defaults to compute the key
    $filter_key = has_filter('list_terms_exclusions') ? serialize($GLOBALS['wp_filter']['list_terms_exclusions']) : '';
    $key = md5(serialize(wp_array_slice_assoc($args, array_keys($defaults))) . serialize($taxonomies) . $filter_key);
    $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) {
        /**
         * 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 arguments to get terms.
         */
        $cache = apply_filters('get_terms', $cache, $taxonomies, $args);
        return $cache;
    }
    $_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) {
        $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("', '", array_map('esc_sql', $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'])) {
        if (is_array($args['name'])) {
            $name = array_map('sanitize_text_field', $args['name']);
            $where .= " AND t.name IN ('" . implode("', '", array_map('esc_sql', $name)) . "')";
        } else {
            $name = sanitize_text_field($args['name']);
            $where .= $wpdb->prepare(" AND t.name = %s", $name);
        }
    }
    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);
    }
    $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', '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'] : '';
    $orderby = isset($clauses['orderby']) ? $clauses['orderby'] : '';
    $order = isset($clauses['order']) ? $clauses['order'] : '';
    $limits = isset($clauses['limits']) ? $clauses['limits'] : '';
    $query = "SELECT {$fields} FROM {$wpdb->terms} AS t {$join} WHERE {$where} {$orderby} {$order} {$limits}";
    if ('count' == $_fields) {
        $term_count = $wpdb->get_var($query);
        return $term_count;
    }
    $terms = $wpdb->get_results($query);
    if ('all' == $_fields) {
        update_term_cache($terms);
    }
    if (empty($terms)) {
        wp_cache_add($cache_key, array(), 'terms', DAY_IN_SECONDS);
        /** This filter is documented in wp-includes/taxonomy.php */
        $terms = apply_filters('get_terms', array(), $taxonomies, $args);
        return $terms;
    }
    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);
    }
    wp_cache_add($cache_key, $terms, 'terms', DAY_IN_SECONDS);
    /** This filter is documented in wp-includes/taxonomy */
    $terms = apply_filters('get_terms', $terms, $taxonomies, $args);
    return $terms;
}

WordPress Version: 2.4

/**
 * 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 '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 'list_terms_exclusions' filter passes the compiled exclusions along with
 * the $args.
 *
 * The '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.
 *
 * @global wpdb $wpdb WordPress database abstraction object.
 *
 * @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,
 *                                           or 'none' to skip ORDER BY. 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
 *                                           term objects), 'ids' or 'names' (returns an array of integers
 *                                           or strings, respectively. 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'.
 * }
 * @return array|WP_Error List of Term Objects 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)) {
            $error = new WP_Error('invalid_taxonomy', __('Invalid taxonomy'));
            return $error;
        }
    }
    $defaults = array('orderby' => 'name', 'order' => 'ASC', 'hide_empty' => true, 'exclude' => array(), 'exclude_tree' => array(), 'include' => array(), 'number' => '', 'fields' => 'all', 'name' => '', 'slug' => '', 'parent' => '', 'childless' => false, 'hierarchical' => true, 'child_of' => 0, 'get' => '', 'name__like' => '', 'description__like' => '', 'pad_counts' => false, 'offset' => '', 'search' => '', 'cache_domain' => 'core');
    $args = wp_parse_args($args, $defaults);
    $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 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;
        }
    }
    // $args can be whatever, only use the args defined in defaults to compute the key
    $filter_key = has_filter('list_terms_exclusions') ? serialize($GLOBALS['wp_filter']['list_terms_exclusions']) : '';
    $key = md5(serialize(wp_array_slice_assoc($args, array_keys($defaults))) . serialize($taxonomies) . $filter_key);
    $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) {
        /**
         * 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 arguments to get terms.
         */
        $cache = apply_filters('get_terms', $cache, $taxonomies, $args);
        return $cache;
    }
    $_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) {
        $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'])) {
        if (is_array($args['name'])) {
            $name = array_map('sanitize_text_field', $args['name']);
            $where .= " AND t.name IN ('" . implode("', '", array_map('esc_sql', $name)) . "')";
        } else {
            $name = sanitize_text_field($args['name']);
            $where .= $wpdb->prepare(" AND t.name = %s", $name);
        }
    }
    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);
    }
    $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', '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'] : '';
    $orderby = isset($clauses['orderby']) ? $clauses['orderby'] : '';
    $order = isset($clauses['order']) ? $clauses['order'] : '';
    $limits = isset($clauses['limits']) ? $clauses['limits'] : '';
    $query = "SELECT {$fields} FROM {$wpdb->terms} AS t {$join} WHERE {$where} {$orderby} {$order} {$limits}";
    if ('count' == $_fields) {
        $term_count = $wpdb->get_var($query);
        return $term_count;
    }
    $terms = $wpdb->get_results($query);
    if ('all' == $_fields) {
        update_term_cache($terms);
    }
    if (empty($terms)) {
        wp_cache_add($cache_key, array(), 'terms', DAY_IN_SECONDS);
        /** This filter is documented in wp-includes/taxonomy.php */
        $terms = apply_filters('get_terms', array(), $taxonomies, $args);
        return $terms;
    }
    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);
    }
    wp_cache_add($cache_key, $terms, 'terms', DAY_IN_SECONDS);
    /** This filter is documented in wp-includes/taxonomy */
    $terms = apply_filters('get_terms', $terms, $taxonomies, $args);
    return $terms;
}

WordPress Version: .30

/**
 * 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 '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 'list_terms_exclusions' filter passes the compiled exclusions along with
 * the $args.
 *
 * The '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.
 *
 * @global wpdb $wpdb WordPress database abstraction object.
 *
 * @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,
 *                                           or 'none' to skip ORDER BY. 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
 *                                           term objects), 'ids' or 'names' (returns an array of integers
 *                                           or strings, respectively. 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'.
 * }
 * @return array|WP_Error List of Term Objects 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)) {
            $error = new WP_Error('invalid_taxonomy', __('Invalid taxonomy'));
            return $error;
        }
    }
    $defaults = array('orderby' => 'name', 'order' => 'ASC', 'hide_empty' => true, 'exclude' => array(), 'exclude_tree' => array(), 'include' => array(), 'number' => '', 'fields' => 'all', 'name' => '', 'slug' => '', 'parent' => '', 'childless' => false, 'hierarchical' => true, 'child_of' => 0, 'get' => '', 'name__like' => '', 'description__like' => '', 'pad_counts' => false, 'offset' => '', 'search' => '', 'cache_domain' => 'core');
    $args = wp_parse_args($args, $defaults);
    $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 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;
        }
    }
    // $args can be whatever, only use the args defined in defaults to compute the key
    $filter_key = has_filter('list_terms_exclusions') ? serialize($GLOBALS['wp_filter']['list_terms_exclusions']) : '';
    $key = md5(serialize(wp_array_slice_assoc($args, array_keys($defaults))) . serialize($taxonomies) . $filter_key);
    $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) {
        /**
         * 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 arguments to get terms.
         */
        $cache = apply_filters('get_terms', $cache, $taxonomies, $args);
        return $cache;
    }
    $_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) {
        $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("', '", array_map('esc_sql', $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'])) {
        if (is_array($args['name'])) {
            $name = array_map('sanitize_text_field', $args['name']);
            $where .= " AND t.name IN ('" . implode("', '", array_map('esc_sql', $name)) . "')";
        } else {
            $name = sanitize_text_field($args['name']);
            $where .= $wpdb->prepare(" AND t.name = %s", $name);
        }
    }
    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);
    }
    $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', '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'] : '';
    $orderby = isset($clauses['orderby']) ? $clauses['orderby'] : '';
    $order = isset($clauses['order']) ? $clauses['order'] : '';
    $limits = isset($clauses['limits']) ? $clauses['limits'] : '';
    $query = "SELECT {$fields} FROM {$wpdb->terms} AS t {$join} WHERE {$where} {$orderby} {$order} {$limits}";
    if ('count' == $_fields) {
        $term_count = $wpdb->get_var($query);
        return $term_count;
    }
    $terms = $wpdb->get_results($query);
    if ('all' == $_fields) {
        update_term_cache($terms);
    }
    if (empty($terms)) {
        wp_cache_add($cache_key, array(), 'terms', DAY_IN_SECONDS);
        /** This filter is documented in wp-includes/taxonomy.php */
        $terms = apply_filters('get_terms', array(), $taxonomies, $args);
        return $terms;
    }
    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);
    }
    wp_cache_add($cache_key, $terms, 'terms', DAY_IN_SECONDS);
    /** This filter is documented in wp-includes/taxonomy */
    $terms = apply_filters('get_terms', $terms, $taxonomies, $args);
    return $terms;
}

WordPress Version: 2.3

/**
 * 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 '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 'list_terms_exclusions' filter passes the compiled exclusions along with
 * the $args.
 *
 * The '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.
 *
 * @global wpdb $wpdb WordPress database abstraction object.
 *
 * @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,
 *                                           or 'none' to skip ORDER BY. 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
 *                                           term objects), 'ids' or 'names' (returns an array of integers
 *                                           or strings, respectively. 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'.
 * }
 * @return array|WP_Error List of Term Objects 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)) {
            $error = new WP_Error('invalid_taxonomy', __('Invalid taxonomy'));
            return $error;
        }
    }
    $defaults = array('orderby' => 'name', 'order' => 'ASC', 'hide_empty' => true, 'exclude' => array(), 'exclude_tree' => array(), 'include' => array(), 'number' => '', 'fields' => 'all', 'name' => '', 'slug' => '', 'parent' => '', 'childless' => false, 'hierarchical' => true, 'child_of' => 0, 'get' => '', 'name__like' => '', 'description__like' => '', 'pad_counts' => false, 'offset' => '', 'search' => '', 'cache_domain' => 'core');
    $args = wp_parse_args($args, $defaults);
    $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 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;
        }
    }
    // $args can be whatever, only use the args defined in defaults to compute the key
    $filter_key = has_filter('list_terms_exclusions') ? serialize($GLOBALS['wp_filter']['list_terms_exclusions']) : '';
    $key = md5(serialize(wp_array_slice_assoc($args, array_keys($defaults))) . serialize($taxonomies) . $filter_key);
    $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) {
        /**
         * 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 arguments to get terms.
         */
        $cache = apply_filters('get_terms', $cache, $taxonomies, $args);
        return $cache;
    }
    $_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) {
        $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'])) {
        if (is_array($args['name'])) {
            $name = array_map('sanitize_text_field', $args['name']);
            $where .= " AND t.name IN ('" . implode("', '", array_map('esc_sql', $name)) . "')";
        } else {
            $name = sanitize_text_field($args['name']);
            $where .= $wpdb->prepare(" AND t.name = %s", $name);
        }
    }
    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);
    }
    $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', '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'] : '';
    $orderby = isset($clauses['orderby']) ? $clauses['orderby'] : '';
    $order = isset($clauses['order']) ? $clauses['order'] : '';
    $limits = isset($clauses['limits']) ? $clauses['limits'] : '';
    $query = "SELECT {$fields} FROM {$wpdb->terms} AS t {$join} WHERE {$where} {$orderby} {$order} {$limits}";
    if ('count' == $_fields) {
        $term_count = $wpdb->get_var($query);
        return $term_count;
    }
    $terms = $wpdb->get_results($query);
    if ('all' == $_fields) {
        update_term_cache($terms);
    }
    if (empty($terms)) {
        wp_cache_add($cache_key, array(), 'terms', DAY_IN_SECONDS);
        /** This filter is documented in wp-includes/taxonomy.php */
        $terms = apply_filters('get_terms', array(), $taxonomies, $args);
        return $terms;
    }
    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);
    }
    wp_cache_add($cache_key, $terms, 'terms', DAY_IN_SECONDS);
    /** This filter is documented in wp-includes/taxonomy */
    $terms = apply_filters('get_terms', $terms, $taxonomies, $args);
    return $terms;
}

WordPress Version: .20

/**
 * 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 '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 'list_terms_exclusions' filter passes the compiled exclusions along with
 * the $args.
 *
 * The '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.
 *
 * @global wpdb $wpdb WordPress database abstraction object.
 *
 * @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,
 *                                           or 'none' to skip ORDER BY. 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
 *                                           term objects), 'ids' or 'names' (returns an array of integers
 *                                           or strings, respectively. 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'.
 * }
 * @return array|WP_Error List of Term Objects 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)) {
            $error = new WP_Error('invalid_taxonomy', __('Invalid taxonomy'));
            return $error;
        }
    }
    $defaults = array('orderby' => 'name', 'order' => 'ASC', 'hide_empty' => true, 'exclude' => array(), 'exclude_tree' => array(), 'include' => array(), 'number' => '', 'fields' => 'all', 'name' => '', 'slug' => '', 'parent' => '', 'childless' => false, 'hierarchical' => true, 'child_of' => 0, 'get' => '', 'name__like' => '', 'description__like' => '', 'pad_counts' => false, 'offset' => '', 'search' => '', 'cache_domain' => 'core');
    $args = wp_parse_args($args, $defaults);
    $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 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;
        }
    }
    // $args can be whatever, only use the args defined in defaults to compute the key
    $filter_key = has_filter('list_terms_exclusions') ? serialize($GLOBALS['wp_filter']['list_terms_exclusions']) : '';
    $key = md5(serialize(wp_array_slice_assoc($args, array_keys($defaults))) . serialize($taxonomies) . $filter_key);
    $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) {
        /**
         * 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 arguments to get terms.
         */
        $cache = apply_filters('get_terms', $cache, $taxonomies, $args);
        return $cache;
    }
    $_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) {
        $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("', '", array_map('esc_sql', $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'])) {
        if (is_array($args['name'])) {
            $name = array_map('sanitize_text_field', $args['name']);
            $where .= " AND t.name IN ('" . implode("', '", array_map('esc_sql', $name)) . "')";
        } else {
            $name = sanitize_text_field($args['name']);
            $where .= $wpdb->prepare(" AND t.name = %s", $name);
        }
    }
    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);
    }
    $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', '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'] : '';
    $orderby = isset($clauses['orderby']) ? $clauses['orderby'] : '';
    $order = isset($clauses['order']) ? $clauses['order'] : '';
    $limits = isset($clauses['limits']) ? $clauses['limits'] : '';
    $query = "SELECT {$fields} FROM {$wpdb->terms} AS t {$join} WHERE {$where} {$orderby} {$order} {$limits}";
    if ('count' == $_fields) {
        $term_count = $wpdb->get_var($query);
        return $term_count;
    }
    $terms = $wpdb->get_results($query);
    if ('all' == $_fields) {
        update_term_cache($terms);
    }
    if (empty($terms)) {
        wp_cache_add($cache_key, array(), 'terms', DAY_IN_SECONDS);
        /** This filter is documented in wp-includes/taxonomy.php */
        $terms = apply_filters('get_terms', array(), $taxonomies, $args);
        return $terms;
    }
    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);
    }
    wp_cache_add($cache_key, $terms, 'terms', DAY_IN_SECONDS);
    /** This filter is documented in wp-includes/taxonomy */
    $terms = apply_filters('get_terms', $terms, $taxonomies, $args);
    return $terms;
}

WordPress Version: 2.2

/**
 * 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 '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 'list_terms_exclusions' filter passes the compiled exclusions along with
 * the $args.
 *
 * The '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.
 *
 * @global wpdb $wpdb WordPress database abstraction object.
 *
 * @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,
 *                                           or 'none' to skip ORDER BY. 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
 *                                           term objects), 'ids' or 'names' (returns an array of integers
 *                                           or strings, respectively. 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'.
 * }
 * @return array|WP_Error List of Term Objects 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)) {
            $error = new WP_Error('invalid_taxonomy', __('Invalid taxonomy'));
            return $error;
        }
    }
    $defaults = array('orderby' => 'name', 'order' => 'ASC', 'hide_empty' => true, 'exclude' => array(), 'exclude_tree' => array(), 'include' => array(), 'number' => '', 'fields' => 'all', 'name' => '', 'slug' => '', 'parent' => '', 'childless' => false, 'hierarchical' => true, 'child_of' => 0, 'get' => '', 'name__like' => '', 'description__like' => '', 'pad_counts' => false, 'offset' => '', 'search' => '', 'cache_domain' => 'core');
    $args = wp_parse_args($args, $defaults);
    $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 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;
        }
    }
    // $args can be whatever, only use the args defined in defaults to compute the key
    $filter_key = has_filter('list_terms_exclusions') ? serialize($GLOBALS['wp_filter']['list_terms_exclusions']) : '';
    $key = md5(serialize(wp_array_slice_assoc($args, array_keys($defaults))) . serialize($taxonomies) . $filter_key);
    $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) {
        /**
         * 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 arguments to get terms.
         */
        $cache = apply_filters('get_terms', $cache, $taxonomies, $args);
        return $cache;
    }
    $_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) {
        $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'])) {
        if (is_array($args['name'])) {
            $name = array_map('sanitize_text_field', $args['name']);
            $where .= " AND t.name IN ('" . implode("', '", array_map('esc_sql', $name)) . "')";
        } else {
            $name = sanitize_text_field($args['name']);
            $where .= $wpdb->prepare(" AND t.name = %s", $name);
        }
    }
    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);
    }
    $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', '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'] : '';
    $orderby = isset($clauses['orderby']) ? $clauses['orderby'] : '';
    $order = isset($clauses['order']) ? $clauses['order'] : '';
    $limits = isset($clauses['limits']) ? $clauses['limits'] : '';
    $query = "SELECT {$fields} FROM {$wpdb->terms} AS t {$join} WHERE {$where} {$orderby} {$order} {$limits}";
    if ('count' == $_fields) {
        $term_count = $wpdb->get_var($query);
        return $term_count;
    }
    $terms = $wpdb->get_results($query);
    if ('all' == $_fields) {
        update_term_cache($terms);
    }
    if (empty($terms)) {
        wp_cache_add($cache_key, array(), 'terms', DAY_IN_SECONDS);
        /** This filter is documented in wp-includes/taxonomy.php */
        $terms = apply_filters('get_terms', array(), $taxonomies, $args);
        return $terms;
    }
    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);
    }
    wp_cache_add($cache_key, $terms, 'terms', DAY_IN_SECONDS);
    /** This filter is documented in wp-includes/taxonomy */
    $terms = apply_filters('get_terms', $terms, $taxonomies, $args);
    return $terms;
}

WordPress Version: .10

/**
 * 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 '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 'list_terms_exclusions' filter passes the compiled exclusions along with
 * the $args.
 *
 * The '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.
 *
 * @global wpdb $wpdb WordPress database abstraction object.
 *
 * @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,
 *                                           or 'none' to skip ORDER BY. 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
 *                                           term objects), 'ids' or 'names' (returns an array of integers
 *                                           or strings, respectively. 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'.
 * }
 * @return array|WP_Error List of Term Objects 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)) {
            $error = new WP_Error('invalid_taxonomy', __('Invalid taxonomy'));
            return $error;
        }
    }
    $defaults = array('orderby' => 'name', 'order' => 'ASC', 'hide_empty' => true, 'exclude' => array(), 'exclude_tree' => array(), 'include' => array(), 'number' => '', 'fields' => 'all', 'name' => '', 'slug' => '', 'parent' => '', 'childless' => false, 'hierarchical' => true, 'child_of' => 0, 'get' => '', 'name__like' => '', 'description__like' => '', 'pad_counts' => false, 'offset' => '', 'search' => '', 'cache_domain' => 'core');
    $args = wp_parse_args($args, $defaults);
    $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 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;
        }
    }
    // $args can be whatever, only use the args defined in defaults to compute the key
    $filter_key = has_filter('list_terms_exclusions') ? serialize($GLOBALS['wp_filter']['list_terms_exclusions']) : '';
    $key = md5(serialize(wp_array_slice_assoc($args, array_keys($defaults))) . serialize($taxonomies) . $filter_key);
    $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) {
        /**
         * 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 arguments to get terms.
         */
        $cache = apply_filters('get_terms', $cache, $taxonomies, $args);
        return $cache;
    }
    $_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) {
        $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("', '", array_map('esc_sql', $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'])) {
        if (is_array($args['name'])) {
            $name = array_map('sanitize_text_field', $args['name']);
            $where .= " AND t.name IN ('" . implode("', '", array_map('esc_sql', $name)) . "')";
        } else {
            $name = sanitize_text_field($args['name']);
            $where .= $wpdb->prepare(" AND t.name = %s", $name);
        }
    }
    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);
    }
    $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', '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'] : '';
    $orderby = isset($clauses['orderby']) ? $clauses['orderby'] : '';
    $order = isset($clauses['order']) ? $clauses['order'] : '';
    $limits = isset($clauses['limits']) ? $clauses['limits'] : '';
    $query = "SELECT {$fields} FROM {$wpdb->terms} AS t {$join} WHERE {$where} {$orderby} {$order} {$limits}";
    if ('count' == $_fields) {
        $term_count = $wpdb->get_var($query);
        return $term_count;
    }
    $terms = $wpdb->get_results($query);
    if ('all' == $_fields) {
        update_term_cache($terms);
    }
    if (empty($terms)) {
        wp_cache_add($cache_key, array(), 'terms', DAY_IN_SECONDS);
        /** This filter is documented in wp-includes/taxonomy.php */
        $terms = apply_filters('get_terms', array(), $taxonomies, $args);
        return $terms;
    }
    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);
    }
    wp_cache_add($cache_key, $terms, 'terms', DAY_IN_SECONDS);
    /** This filter is documented in wp-includes/taxonomy */
    $terms = apply_filters('get_terms', $terms, $taxonomies, $args);
    return $terms;
}

WordPress Version: 4.2

/**
 * 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 '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 'list_terms_exclusions' filter passes the compiled exclusions along with
 * the $args.
 *
 * The '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.
 *
 * @global wpdb $wpdb WordPress database abstraction object.
 *
 * @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,
 *                                           or 'none' to skip ORDER BY. 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
 *                                           term objects), 'ids' or 'names' (returns an array of integers
 *                                           or strings, respectively. 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'.
 * }
 * @return array|WP_Error List of Term Objects 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)) {
            $error = new WP_Error('invalid_taxonomy', __('Invalid taxonomy'));
            return $error;
        }
    }
    $defaults = array('orderby' => 'name', 'order' => 'ASC', 'hide_empty' => true, 'exclude' => array(), 'exclude_tree' => array(), 'include' => array(), 'number' => '', 'fields' => 'all', 'name' => '', 'slug' => '', 'parent' => '', 'childless' => false, 'hierarchical' => true, 'child_of' => 0, 'get' => '', 'name__like' => '', 'description__like' => '', 'pad_counts' => false, 'offset' => '', 'search' => '', 'cache_domain' => 'core');
    $args = wp_parse_args($args, $defaults);
    $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 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;
        }
    }
    // $args can be whatever, only use the args defined in defaults to compute the key
    $filter_key = has_filter('list_terms_exclusions') ? serialize($GLOBALS['wp_filter']['list_terms_exclusions']) : '';
    $key = md5(serialize(wp_array_slice_assoc($args, array_keys($defaults))) . serialize($taxonomies) . $filter_key);
    $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) {
        /**
         * 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 arguments to get terms.
         */
        $cache = apply_filters('get_terms', $cache, $taxonomies, $args);
        return $cache;
    }
    $_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) {
        $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'])) {
        if (is_array($args['name'])) {
            $name = array_map('sanitize_text_field', $args['name']);
            $where .= " AND t.name IN ('" . implode("', '", array_map('esc_sql', $name)) . "')";
        } else {
            $name = sanitize_text_field($args['name']);
            $where .= $wpdb->prepare(" AND t.name = %s", $name);
        }
    }
    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);
    }
    $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', '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'] : '';
    $orderby = isset($clauses['orderby']) ? $clauses['orderby'] : '';
    $order = isset($clauses['order']) ? $clauses['order'] : '';
    $limits = isset($clauses['limits']) ? $clauses['limits'] : '';
    $query = "SELECT {$fields} FROM {$wpdb->terms} AS t {$join} WHERE {$where} {$orderby} {$order} {$limits}";
    if ('count' == $_fields) {
        $term_count = $wpdb->get_var($query);
        return $term_count;
    }
    $terms = $wpdb->get_results($query);
    if ('all' == $_fields) {
        update_term_cache($terms);
    }
    if (empty($terms)) {
        wp_cache_add($cache_key, array(), 'terms', DAY_IN_SECONDS);
        /** This filter is documented in wp-includes/taxonomy.php */
        $terms = apply_filters('get_terms', array(), $taxonomies, $args);
        return $terms;
    }
    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);
    }
    wp_cache_add($cache_key, $terms, 'terms', DAY_IN_SECONDS);
    /** This filter is documented in wp-includes/taxonomy */
    $terms = apply_filters('get_terms', $terms, $taxonomies, $args);
    return $terms;
}

WordPress Version: 1.5

/**
 * 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 '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 'list_terms_exclusions' filter passes the compiled exclusions along with
 * the $args.
 *
 * The 'get_terms_orderby' filter passes the ORDER BY clause for the query
 * along with the $args array.
 *
 * The 'get_terms_fields' filter passes the fields for the SELECT query
 * along with the $args array.
 *
 * @since 2.3.0
 *
 * @global wpdb $wpdb WordPress database abstraction object.
 *
 * @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'), 'count' for term taxonomy count,
 *                                           'include' to match the 'order' of the $include param, or 'none'
 *                                           to skip ORDER BY. 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          $number            Maximum number of terms to return. Accepts 1+ or -1 (all).
 *                                           Default -1.
 *     @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
 *                                           term objects), 'ids' or 'names' (returns an array of integers
 *                                           or strings, respectively. Default 'all'.
 *     @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 string       $cache_domain      Unique cache key to be produced when this query is stored in an
 *                                           object cache. Default is 'core'.
 * }
 * @return array|WP_Error List of Term Objects 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)) {
            $error = new WP_Error('invalid_taxonomy', __('Invalid taxonomy'));
            return $error;
        }
    }
    $defaults = array('orderby' => 'name', 'order' => 'ASC', 'hide_empty' => true, 'exclude' => array(), 'exclude_tree' => array(), 'include' => array(), 'number' => '', 'fields' => 'all', 'slug' => '', 'parent' => '', 'hierarchical' => true, 'child_of' => 0, 'get' => '', 'name__like' => '', 'description__like' => '', 'pad_counts' => false, 'offset' => '', 'search' => '', 'cache_domain' => 'core');
    $args = wp_parse_args($args, $defaults);
    $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.
    if (!$single_taxonomy || !is_taxonomy_hierarchical(reset($taxonomies))) {
        $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['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 arguments.
     * @param string|array $taxonomies A taxonomy or array of taxonomies.
     */
    $args = apply_filters('get_terms_args', $args, $taxonomies);
    $child_of = $args['child_of'];
    if ($child_of) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$child_of])) {
            return $empty_array;
        }
    }
    $parent = $args['parent'];
    if ($parent) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$parent])) {
            return $empty_array;
        }
    }
    // $args can be whatever, only use the args defined in defaults to compute the key
    $filter_key = has_filter('list_terms_exclusions') ? serialize($GLOBALS['wp_filter']['list_terms_exclusions']) : '';
    $key = md5(serialize(wp_array_slice_assoc($args, array_keys($defaults))) . serialize($taxonomies) . $filter_key);
    $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) {
        /**
         * Filter the given taxonomy's terms cache.
         *
         * @since 2.3.0
         *
         * @param array        $cache      Cached array of terms for the given taxonomy.
         * @param string|array $taxonomies A taxonomy or array of taxonomies.
         * @param array        $args       An array of arguments to get terms.
         */
        $cache = apply_filters('get_terms', $cache, $taxonomies, $args);
        return $cache;
    }
    $_orderby = strtolower($args['orderby']);
    if ('count' == $_orderby) {
        $orderby = 'tt.count';
    } else if ('name' == $_orderby) {
        $orderby = 't.name';
    } else if ('slug' == $_orderby) {
        $orderby = 't.slug';
    } else if ('include' == $_orderby && !empty($args['include'])) {
        $include = implode(',', array_map('absint', $args['include']));
        $orderby = "FIELD( t.term_id, {$include} )";
    } else if ('term_group' == $_orderby) {
        $orderby = 't.term_group';
    } else if ('none' == $_orderby) {
        $orderby = '';
    } elseif (empty($_orderby) || '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 string|array $taxonomies A taxonomy or 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;
    }
    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 = implode(',', array_map('intval', $excluded_children));
    } else {
        $exclusions = '';
    }
    if (!empty($exclude)) {
        $exterms = wp_parse_id_list($exclude);
        if (empty($exclusions)) {
            $exclusions = implode(',', $exterms);
        } else {
            $exclusions .= ', ' . implode(',', $exterms);
        }
    }
    if (!empty($exclusions)) {
        $exclusions = ' AND t.term_id NOT IN (' . $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 string|array $taxonomies A taxonomy or array of taxonomies.
     */
    $exclusions = apply_filters('list_terms_exclusions', $exclusions, $args, $taxonomies);
    if (!empty($exclusions)) {
        $where .= $exclusions;
    }
    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);
    }
    $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');
            break;
        case 'names':
            $selects = array('t.term_id', 'tt.parent', 'tt.count', 't.name');
            break;
        case 'count':
            $orderby = '';
            $order = '';
            $selects = array('COUNT(*)');
            break;
        case 'id=>name':
            $selects = array('t.term_id', 't.name', 'tt.count');
            break;
        case 'id=>slug':
            $selects = array('t.term_id', 't.slug', 'tt.count');
            break;
    }
    $_fields = $args['fields'];
    /**
     * Filter the fields to select in the terms query.
     *
     * @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 string|array $taxonomies A taxonomy or 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', 'orderby', 'order', 'limits');
    /**
     * Filter the terms query SQL clauses.
     *
     * @since 3.1.0
     *
     * @param array        $pieces     Terms query SQL clauses.
     * @param string|array $taxonomies A taxonomy or 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'] : '';
    $orderby = isset($clauses['orderby']) ? $clauses['orderby'] : '';
    $order = isset($clauses['order']) ? $clauses['order'] : '';
    $limits = isset($clauses['limits']) ? $clauses['limits'] : '';
    $query = "SELECT {$fields} FROM {$wpdb->terms} AS t {$join} WHERE {$where} {$orderby} {$order} {$limits}";
    if ('count' == $_fields) {
        $term_count = $wpdb->get_var($query);
        return $term_count;
    }
    $terms = $wpdb->get_results($query);
    if ('all' == $_fields) {
        update_term_cache($terms);
    }
    if (empty($terms)) {
        wp_cache_add($cache_key, array(), 'terms', DAY_IN_SECONDS);
        /** This filter is documented in wp-includes/taxonomy.php */
        $terms = apply_filters('get_terms', array(), $taxonomies, $args);
        return $terms;
    }
    if ($child_of) {
        $children = _get_term_hierarchy(reset($taxonomies));
        if (!empty($children)) {
            $terms = _get_term_children($child_of, $terms, reset($taxonomies));
        }
    }
    // Update term counts to include children.
    if ($args['pad_counts'] && 'all' == $_fields) {
        _pad_term_counts($terms, reset($taxonomies));
    }
    // 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, reset($taxonomies));
                if (is_array($children)) {
                    foreach ($children as $child_id) {
                        $child = get_term($child_id, reset($taxonomies));
                        if ($child->count) {
                            continue 2;
                        }
                    }
                }
                // It really is empty
                unset($terms[$k]);
            }
        }
    }
    reset($terms);
    $_terms = array();
    if ('id=>parent' == $_fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->parent;
        }
    } elseif ('ids' == $_fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->term_id;
        }
    } elseif ('names' == $_fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->name;
        }
    } elseif ('id=>name' == $_fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->name;
        }
    } elseif ('id=>slug' == $_fields) {
        while ($term = array_shift($terms)) {
            $_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);
    }
    wp_cache_add($cache_key, $terms, 'terms', DAY_IN_SECONDS);
    /** This filter is documented in wp-includes/taxonomy */
    $terms = apply_filters('get_terms', $terms, $taxonomies, $args);
    return $terms;
}

WordPress Version: .40

/**
 * 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 '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 'list_terms_exclusions' filter passes the compiled exclusions along with
 * the $args.
 *
 * The 'get_terms_orderby' filter passes the ORDER BY clause for the query
 * along with the $args array.
 *
 * The 'get_terms_fields' filter passes the fields for the SELECT query
 * along with the $args array.
 *
 * @since 2.3.0
 *
 * @global wpdb $wpdb WordPress database abstraction object.
 *
 * @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'), 'count' for term taxonomy count,
 *                                           'include' to match the 'order' of the $include param, or 'none'
 *                                           to skip ORDER BY. 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          $number            Maximum number of terms to return. Accepts 1+ or -1 (all).
 *                                           Default -1.
 *     @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
 *                                           term objects), 'ids' or 'names' (returns an array of integers
 *                                           or strings, respectively. Default 'all'.
 *     @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 string       $cache_domain      Unique cache key to be produced when this query is stored in an
 *                                           object cache. Default is 'core'.
 * }
 * @return array|WP_Error List of Term Objects 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)) {
            $error = new WP_Error('invalid_taxonomy', __('Invalid taxonomy'));
            return $error;
        }
    }
    $defaults = array('orderby' => 'name', 'order' => 'ASC', 'hide_empty' => true, 'exclude' => array(), 'exclude_tree' => array(), 'include' => array(), 'number' => '', 'fields' => 'all', 'slug' => '', 'parent' => '', 'hierarchical' => true, 'child_of' => 0, 'get' => '', 'name__like' => '', 'description__like' => '', 'pad_counts' => false, 'offset' => '', 'search' => '', 'cache_domain' => 'core');
    $args = wp_parse_args($args, $defaults);
    $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.
    if (!$single_taxonomy || !is_taxonomy_hierarchical(reset($taxonomies))) {
        $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['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 arguments.
     * @param string|array $taxonomies A taxonomy or array of taxonomies.
     */
    $args = apply_filters('get_terms_args', $args, $taxonomies);
    $child_of = $args['child_of'];
    if ($child_of) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$child_of])) {
            return $empty_array;
        }
    }
    $parent = $args['parent'];
    if ($parent) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$parent])) {
            return $empty_array;
        }
    }
    // $args can be whatever, only use the args defined in defaults to compute the key
    $filter_key = has_filter('list_terms_exclusions') ? serialize($GLOBALS['wp_filter']['list_terms_exclusions']) : '';
    $key = md5(serialize(wp_array_slice_assoc($args, array_keys($defaults))) . serialize($taxonomies) . $filter_key);
    $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) {
        /**
         * Filter the given taxonomy's terms cache.
         *
         * @since 2.3.0
         *
         * @param array        $cache      Cached array of terms for the given taxonomy.
         * @param string|array $taxonomies A taxonomy or array of taxonomies.
         * @param array        $args       An array of arguments to get terms.
         */
        $cache = apply_filters('get_terms', $cache, $taxonomies, $args);
        return $cache;
    }
    $_orderby = strtolower($args['orderby']);
    if ('count' == $_orderby) {
        $orderby = 'tt.count';
    } else if ('name' == $_orderby) {
        $orderby = 't.name';
    } else if ('slug' == $_orderby) {
        $orderby = 't.slug';
    } else if ('include' == $_orderby && !empty($args['include'])) {
        $include = implode(',', array_map('absint', $args['include']));
        $orderby = "FIELD( t.term_id, {$include} )";
    } else if ('term_group' == $_orderby) {
        $orderby = 't.term_group';
    } else if ('none' == $_orderby) {
        $orderby = '';
    } elseif (empty($_orderby) || '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 string|array $taxonomies A taxonomy or 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("', '", array_map('esc_sql', $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;
    }
    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 = implode(',', array_map('intval', $excluded_children));
    } else {
        $exclusions = '';
    }
    if (!empty($exclude)) {
        $exterms = wp_parse_id_list($exclude);
        if (empty($exclusions)) {
            $exclusions = implode(',', $exterms);
        } else {
            $exclusions .= ', ' . implode(',', $exterms);
        }
    }
    if (!empty($exclusions)) {
        $exclusions = ' AND t.term_id NOT IN (' . $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 string|array $taxonomies A taxonomy or array of taxonomies.
     */
    $exclusions = apply_filters('list_terms_exclusions', $exclusions, $args, $taxonomies);
    if (!empty($exclusions)) {
        $where .= $exclusions;
    }
    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);
    }
    $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');
            break;
        case 'names':
            $selects = array('t.term_id', 'tt.parent', 'tt.count', 't.name');
            break;
        case 'count':
            $orderby = '';
            $order = '';
            $selects = array('COUNT(*)');
            break;
        case 'id=>name':
            $selects = array('t.term_id', 't.name', 'tt.count');
            break;
        case 'id=>slug':
            $selects = array('t.term_id', 't.slug', 'tt.count');
            break;
    }
    $_fields = $args['fields'];
    /**
     * Filter the fields to select in the terms query.
     *
     * @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 string|array $taxonomies A taxonomy or 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', 'orderby', 'order', 'limits');
    /**
     * Filter the terms query SQL clauses.
     *
     * @since 3.1.0
     *
     * @param array        $pieces     Terms query SQL clauses.
     * @param string|array $taxonomies A taxonomy or 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'] : '';
    $orderby = isset($clauses['orderby']) ? $clauses['orderby'] : '';
    $order = isset($clauses['order']) ? $clauses['order'] : '';
    $limits = isset($clauses['limits']) ? $clauses['limits'] : '';
    $query = "SELECT {$fields} FROM {$wpdb->terms} AS t {$join} WHERE {$where} {$orderby} {$order} {$limits}";
    if ('count' == $_fields) {
        $term_count = $wpdb->get_var($query);
        return $term_count;
    }
    $terms = $wpdb->get_results($query);
    if ('all' == $_fields) {
        update_term_cache($terms);
    }
    if (empty($terms)) {
        wp_cache_add($cache_key, array(), 'terms', DAY_IN_SECONDS);
        /** This filter is documented in wp-includes/taxonomy.php */
        $terms = apply_filters('get_terms', array(), $taxonomies, $args);
        return $terms;
    }
    if ($child_of) {
        $children = _get_term_hierarchy(reset($taxonomies));
        if (!empty($children)) {
            $terms = _get_term_children($child_of, $terms, reset($taxonomies));
        }
    }
    // Update term counts to include children.
    if ($args['pad_counts'] && 'all' == $_fields) {
        _pad_term_counts($terms, reset($taxonomies));
    }
    // 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, reset($taxonomies));
                if (is_array($children)) {
                    foreach ($children as $child_id) {
                        $child = get_term($child_id, reset($taxonomies));
                        if ($child->count) {
                            continue 2;
                        }
                    }
                }
                // It really is empty
                unset($terms[$k]);
            }
        }
    }
    reset($terms);
    $_terms = array();
    if ('id=>parent' == $_fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->parent;
        }
    } elseif ('ids' == $_fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->term_id;
        }
    } elseif ('names' == $_fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->name;
        }
    } elseif ('id=>name' == $_fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->name;
        }
    } elseif ('id=>slug' == $_fields) {
        while ($term = array_shift($terms)) {
            $_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);
    }
    wp_cache_add($cache_key, $terms, 'terms', DAY_IN_SECONDS);
    /** This filter is documented in wp-includes/taxonomy */
    $terms = apply_filters('get_terms', $terms, $taxonomies, $args);
    return $terms;
}

WordPress Version: 1.4

/**
 * 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 '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 'list_terms_exclusions' filter passes the compiled exclusions along with
 * the $args.
 *
 * The 'get_terms_orderby' filter passes the ORDER BY clause for the query
 * along with the $args array.
 *
 * The 'get_terms_fields' filter passes the fields for the SELECT query
 * along with the $args array.
 *
 * @since 2.3.0
 *
 * @global wpdb $wpdb WordPress database abstraction object.
 *
 * @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'), 'count' for term taxonomy count,
 *                                           'include' to match the 'order' of the $include param, or 'none'
 *                                           to skip ORDER BY. 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          $number            Maximum number of terms to return. Accepts 1+ or -1 (all).
 *                                           Default -1.
 *     @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
 *                                           term objects), 'ids' or 'names' (returns an array of integers
 *                                           or strings, respectively. Default 'all'.
 *     @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 string       $cache_domain      Unique cache key to be produced when this query is stored in an
 *                                           object cache. Default is 'core'.
 * }
 * @return array|WP_Error List of Term Objects 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)) {
            $error = new WP_Error('invalid_taxonomy', __('Invalid taxonomy'));
            return $error;
        }
    }
    $defaults = array('orderby' => 'name', 'order' => 'ASC', 'hide_empty' => true, 'exclude' => array(), 'exclude_tree' => array(), 'include' => array(), 'number' => '', 'fields' => 'all', 'slug' => '', 'parent' => '', 'hierarchical' => true, 'child_of' => 0, 'get' => '', 'name__like' => '', 'description__like' => '', 'pad_counts' => false, 'offset' => '', 'search' => '', 'cache_domain' => 'core');
    $args = wp_parse_args($args, $defaults);
    $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.
    if (!$single_taxonomy || !is_taxonomy_hierarchical(reset($taxonomies))) {
        $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['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 arguments.
     * @param string|array $taxonomies A taxonomy or array of taxonomies.
     */
    $args = apply_filters('get_terms_args', $args, $taxonomies);
    $child_of = $args['child_of'];
    if ($child_of) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$child_of])) {
            return $empty_array;
        }
    }
    $parent = $args['parent'];
    if ($parent) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$parent])) {
            return $empty_array;
        }
    }
    // $args can be whatever, only use the args defined in defaults to compute the key
    $filter_key = has_filter('list_terms_exclusions') ? serialize($GLOBALS['wp_filter']['list_terms_exclusions']) : '';
    $key = md5(serialize(wp_array_slice_assoc($args, array_keys($defaults))) . serialize($taxonomies) . $filter_key);
    $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) {
        /**
         * Filter the given taxonomy's terms cache.
         *
         * @since 2.3.0
         *
         * @param array        $cache      Cached array of terms for the given taxonomy.
         * @param string|array $taxonomies A taxonomy or array of taxonomies.
         * @param array        $args       An array of arguments to get terms.
         */
        $cache = apply_filters('get_terms', $cache, $taxonomies, $args);
        return $cache;
    }
    $_orderby = strtolower($args['orderby']);
    if ('count' == $_orderby) {
        $orderby = 'tt.count';
    } else if ('name' == $_orderby) {
        $orderby = 't.name';
    } else if ('slug' == $_orderby) {
        $orderby = 't.slug';
    } else if ('include' == $_orderby && !empty($args['include'])) {
        $include = implode(',', array_map('absint', $args['include']));
        $orderby = "FIELD( t.term_id, {$include} )";
    } else if ('term_group' == $_orderby) {
        $orderby = 't.term_group';
    } else if ('none' == $_orderby) {
        $orderby = '';
    } elseif (empty($_orderby) || '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 string|array $taxonomies A taxonomy or 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;
    }
    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 = implode(',', array_map('intval', $excluded_children));
    } else {
        $exclusions = '';
    }
    if (!empty($exclude)) {
        $exterms = wp_parse_id_list($exclude);
        if (empty($exclusions)) {
            $exclusions = implode(',', $exterms);
        } else {
            $exclusions .= ', ' . implode(',', $exterms);
        }
    }
    if (!empty($exclusions)) {
        $exclusions = ' AND t.term_id NOT IN (' . $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 string|array $taxonomies A taxonomy or array of taxonomies.
     */
    $exclusions = apply_filters('list_terms_exclusions', $exclusions, $args, $taxonomies);
    if (!empty($exclusions)) {
        $where .= $exclusions;
    }
    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);
    }
    $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');
            break;
        case 'names':
            $selects = array('t.term_id', 'tt.parent', 'tt.count', 't.name');
            break;
        case 'count':
            $orderby = '';
            $order = '';
            $selects = array('COUNT(*)');
            break;
        case 'id=>name':
            $selects = array('t.term_id', 't.name', 'tt.count');
            break;
        case 'id=>slug':
            $selects = array('t.term_id', 't.slug', 'tt.count');
            break;
    }
    $_fields = $args['fields'];
    /**
     * Filter the fields to select in the terms query.
     *
     * @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 string|array $taxonomies A taxonomy or 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', 'orderby', 'order', 'limits');
    /**
     * Filter the terms query SQL clauses.
     *
     * @since 3.1.0
     *
     * @param array        $pieces     Terms query SQL clauses.
     * @param string|array $taxonomies A taxonomy or 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'] : '';
    $orderby = isset($clauses['orderby']) ? $clauses['orderby'] : '';
    $order = isset($clauses['order']) ? $clauses['order'] : '';
    $limits = isset($clauses['limits']) ? $clauses['limits'] : '';
    $query = "SELECT {$fields} FROM {$wpdb->terms} AS t {$join} WHERE {$where} {$orderby} {$order} {$limits}";
    if ('count' == $_fields) {
        $term_count = $wpdb->get_var($query);
        return $term_count;
    }
    $terms = $wpdb->get_results($query);
    if ('all' == $_fields) {
        update_term_cache($terms);
    }
    if (empty($terms)) {
        wp_cache_add($cache_key, array(), 'terms', DAY_IN_SECONDS);
        /** This filter is documented in wp-includes/taxonomy.php */
        $terms = apply_filters('get_terms', array(), $taxonomies, $args);
        return $terms;
    }
    if ($child_of) {
        $children = _get_term_hierarchy(reset($taxonomies));
        if (!empty($children)) {
            $terms = _get_term_children($child_of, $terms, reset($taxonomies));
        }
    }
    // Update term counts to include children.
    if ($args['pad_counts'] && 'all' == $_fields) {
        _pad_term_counts($terms, reset($taxonomies));
    }
    // 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, reset($taxonomies));
                if (is_array($children)) {
                    foreach ($children as $child_id) {
                        $child = get_term($child_id, reset($taxonomies));
                        if ($child->count) {
                            continue 2;
                        }
                    }
                }
                // It really is empty
                unset($terms[$k]);
            }
        }
    }
    reset($terms);
    $_terms = array();
    if ('id=>parent' == $_fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->parent;
        }
    } elseif ('ids' == $_fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->term_id;
        }
    } elseif ('names' == $_fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->name;
        }
    } elseif ('id=>name' == $_fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->name;
        }
    } elseif ('id=>slug' == $_fields) {
        while ($term = array_shift($terms)) {
            $_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);
    }
    wp_cache_add($cache_key, $terms, 'terms', DAY_IN_SECONDS);
    /** This filter is documented in wp-includes/taxonomy */
    $terms = apply_filters('get_terms', $terms, $taxonomies, $args);
    return $terms;
}

WordPress Version: .30

/**
 * 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 '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 'list_terms_exclusions' filter passes the compiled exclusions along with
 * the $args.
 *
 * The 'get_terms_orderby' filter passes the ORDER BY clause for the query
 * along with the $args array.
 *
 * The 'get_terms_fields' filter passes the fields for the SELECT query
 * along with the $args array.
 *
 * @since 2.3.0
 *
 * @global wpdb $wpdb WordPress database abstraction object.
 *
 * @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'), 'count' for term taxonomy count,
 *                                           'include' to match the 'order' of the $include param, or 'none'
 *                                           to skip ORDER BY. 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          $number            Maximum number of terms to return. Accepts 1+ or -1 (all).
 *                                           Default -1.
 *     @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
 *                                           term objects), 'ids' or 'names' (returns an array of integers
 *                                           or strings, respectively. Default 'all'.
 *     @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 string       $cache_domain      Unique cache key to be produced when this query is stored in an
 *                                           object cache. Default is 'core'.
 * }
 * @return array|WP_Error List of Term Objects 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)) {
            $error = new WP_Error('invalid_taxonomy', __('Invalid taxonomy'));
            return $error;
        }
    }
    $defaults = array('orderby' => 'name', 'order' => 'ASC', 'hide_empty' => true, 'exclude' => array(), 'exclude_tree' => array(), 'include' => array(), 'number' => '', 'fields' => 'all', 'slug' => '', 'parent' => '', 'hierarchical' => true, 'child_of' => 0, 'get' => '', 'name__like' => '', 'description__like' => '', 'pad_counts' => false, 'offset' => '', 'search' => '', 'cache_domain' => 'core');
    $args = wp_parse_args($args, $defaults);
    $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.
    if (!$single_taxonomy || !is_taxonomy_hierarchical(reset($taxonomies))) {
        $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['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 arguments.
     * @param string|array $taxonomies A taxonomy or array of taxonomies.
     */
    $args = apply_filters('get_terms_args', $args, $taxonomies);
    $child_of = $args['child_of'];
    if ($child_of) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$child_of])) {
            return $empty_array;
        }
    }
    $parent = $args['parent'];
    if ($parent) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$parent])) {
            return $empty_array;
        }
    }
    // $args can be whatever, only use the args defined in defaults to compute the key
    $filter_key = has_filter('list_terms_exclusions') ? serialize($GLOBALS['wp_filter']['list_terms_exclusions']) : '';
    $key = md5(serialize(wp_array_slice_assoc($args, array_keys($defaults))) . serialize($taxonomies) . $filter_key);
    $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) {
        /**
         * Filter the given taxonomy's terms cache.
         *
         * @since 2.3.0
         *
         * @param array        $cache      Cached array of terms for the given taxonomy.
         * @param string|array $taxonomies A taxonomy or array of taxonomies.
         * @param array        $args       An array of arguments to get terms.
         */
        $cache = apply_filters('get_terms', $cache, $taxonomies, $args);
        return $cache;
    }
    $_orderby = strtolower($args['orderby']);
    if ('count' == $_orderby) {
        $orderby = 'tt.count';
    } else if ('name' == $_orderby) {
        $orderby = 't.name';
    } else if ('slug' == $_orderby) {
        $orderby = 't.slug';
    } else if ('include' == $_orderby && !empty($args['include'])) {
        $include = implode(',', array_map('absint', $args['include']));
        $orderby = "FIELD( t.term_id, {$include} )";
    } else if ('term_group' == $_orderby) {
        $orderby = 't.term_group';
    } else if ('none' == $_orderby) {
        $orderby = '';
    } elseif (empty($_orderby) || '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 string|array $taxonomies A taxonomy or 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("', '", array_map('esc_sql', $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;
    }
    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 = implode(',', array_map('intval', $excluded_children));
    } else {
        $exclusions = '';
    }
    if (!empty($exclude)) {
        $exterms = wp_parse_id_list($exclude);
        if (empty($exclusions)) {
            $exclusions = implode(',', $exterms);
        } else {
            $exclusions .= ', ' . implode(',', $exterms);
        }
    }
    if (!empty($exclusions)) {
        $exclusions = ' AND t.term_id NOT IN (' . $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 string|array $taxonomies A taxonomy or array of taxonomies.
     */
    $exclusions = apply_filters('list_terms_exclusions', $exclusions, $args, $taxonomies);
    if (!empty($exclusions)) {
        $where .= $exclusions;
    }
    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);
    }
    $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');
            break;
        case 'names':
            $selects = array('t.term_id', 'tt.parent', 'tt.count', 't.name');
            break;
        case 'count':
            $orderby = '';
            $order = '';
            $selects = array('COUNT(*)');
            break;
        case 'id=>name':
            $selects = array('t.term_id', 't.name', 'tt.count');
            break;
        case 'id=>slug':
            $selects = array('t.term_id', 't.slug', 'tt.count');
            break;
    }
    $_fields = $args['fields'];
    /**
     * Filter the fields to select in the terms query.
     *
     * @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 string|array $taxonomies A taxonomy or 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', 'orderby', 'order', 'limits');
    /**
     * Filter the terms query SQL clauses.
     *
     * @since 3.1.0
     *
     * @param array        $pieces     Terms query SQL clauses.
     * @param string|array $taxonomies A taxonomy or 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'] : '';
    $orderby = isset($clauses['orderby']) ? $clauses['orderby'] : '';
    $order = isset($clauses['order']) ? $clauses['order'] : '';
    $limits = isset($clauses['limits']) ? $clauses['limits'] : '';
    $query = "SELECT {$fields} FROM {$wpdb->terms} AS t {$join} WHERE {$where} {$orderby} {$order} {$limits}";
    if ('count' == $_fields) {
        $term_count = $wpdb->get_var($query);
        return $term_count;
    }
    $terms = $wpdb->get_results($query);
    if ('all' == $_fields) {
        update_term_cache($terms);
    }
    if (empty($terms)) {
        wp_cache_add($cache_key, array(), 'terms', DAY_IN_SECONDS);
        /** This filter is documented in wp-includes/taxonomy.php */
        $terms = apply_filters('get_terms', array(), $taxonomies, $args);
        return $terms;
    }
    if ($child_of) {
        $children = _get_term_hierarchy(reset($taxonomies));
        if (!empty($children)) {
            $terms = _get_term_children($child_of, $terms, reset($taxonomies));
        }
    }
    // Update term counts to include children.
    if ($args['pad_counts'] && 'all' == $_fields) {
        _pad_term_counts($terms, reset($taxonomies));
    }
    // 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, reset($taxonomies));
                if (is_array($children)) {
                    foreach ($children as $child_id) {
                        $child = get_term($child_id, reset($taxonomies));
                        if ($child->count) {
                            continue 2;
                        }
                    }
                }
                // It really is empty
                unset($terms[$k]);
            }
        }
    }
    reset($terms);
    $_terms = array();
    if ('id=>parent' == $_fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->parent;
        }
    } elseif ('ids' == $_fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->term_id;
        }
    } elseif ('names' == $_fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->name;
        }
    } elseif ('id=>name' == $_fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->name;
        }
    } elseif ('id=>slug' == $_fields) {
        while ($term = array_shift($terms)) {
            $_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);
    }
    wp_cache_add($cache_key, $terms, 'terms', DAY_IN_SECONDS);
    /** This filter is documented in wp-includes/taxonomy */
    $terms = apply_filters('get_terms', $terms, $taxonomies, $args);
    return $terms;
}

WordPress Version: 1.3

/**
 * 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 '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 'list_terms_exclusions' filter passes the compiled exclusions along with
 * the $args.
 *
 * The 'get_terms_orderby' filter passes the ORDER BY clause for the query
 * along with the $args array.
 *
 * The 'get_terms_fields' filter passes the fields for the SELECT query
 * along with the $args array.
 *
 * @since 2.3.0
 *
 * @global wpdb $wpdb WordPress database abstraction object.
 *
 * @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'), 'count' for term taxonomy count,
 *                                           'include' to match the 'order' of the $include param, or 'none'
 *                                           to skip ORDER BY. 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          $number            Maximum number of terms to return. Accepts 1+ or -1 (all).
 *                                           Default -1.
 *     @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
 *                                           term objects), 'ids' or 'names' (returns an array of integers
 *                                           or strings, respectively. Default 'all'.
 *     @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 string       $cache_domain      Unique cache key to be produced when this query is stored in an
 *                                           object cache. Default is 'core'.
 * }
 * @return array|WP_Error List of Term Objects 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)) {
            $error = new WP_Error('invalid_taxonomy', __('Invalid taxonomy'));
            return $error;
        }
    }
    $defaults = array('orderby' => 'name', 'order' => 'ASC', 'hide_empty' => true, 'exclude' => array(), 'exclude_tree' => array(), 'include' => array(), 'number' => '', 'fields' => 'all', 'slug' => '', 'parent' => '', 'hierarchical' => true, 'child_of' => 0, 'get' => '', 'name__like' => '', 'description__like' => '', 'pad_counts' => false, 'offset' => '', 'search' => '', 'cache_domain' => 'core');
    $args = wp_parse_args($args, $defaults);
    $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.
    if (!$single_taxonomy || !is_taxonomy_hierarchical(reset($taxonomies))) {
        $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['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 arguments.
     * @param string|array $taxonomies A taxonomy or array of taxonomies.
     */
    $args = apply_filters('get_terms_args', $args, $taxonomies);
    $child_of = $args['child_of'];
    if ($child_of) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$child_of])) {
            return $empty_array;
        }
    }
    $parent = $args['parent'];
    if ($parent) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$parent])) {
            return $empty_array;
        }
    }
    // $args can be whatever, only use the args defined in defaults to compute the key
    $filter_key = has_filter('list_terms_exclusions') ? serialize($GLOBALS['wp_filter']['list_terms_exclusions']) : '';
    $key = md5(serialize(wp_array_slice_assoc($args, array_keys($defaults))) . serialize($taxonomies) . $filter_key);
    $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) {
        /**
         * Filter the given taxonomy's terms cache.
         *
         * @since 2.3.0
         *
         * @param array        $cache      Cached array of terms for the given taxonomy.
         * @param string|array $taxonomies A taxonomy or array of taxonomies.
         * @param array        $args       An array of arguments to get terms.
         */
        $cache = apply_filters('get_terms', $cache, $taxonomies, $args);
        return $cache;
    }
    $_orderby = strtolower($args['orderby']);
    if ('count' == $_orderby) {
        $orderby = 'tt.count';
    } else if ('name' == $_orderby) {
        $orderby = 't.name';
    } else if ('slug' == $_orderby) {
        $orderby = 't.slug';
    } else if ('include' == $_orderby && !empty($args['include'])) {
        $include = implode(',', array_map('absint', $args['include']));
        $orderby = "FIELD( t.term_id, {$include} )";
    } else if ('term_group' == $_orderby) {
        $orderby = 't.term_group';
    } else if ('none' == $_orderby) {
        $orderby = '';
    } elseif (empty($_orderby) || '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 string|array $taxonomies A taxonomy or 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;
    }
    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 = implode(',', array_map('intval', $excluded_children));
    } else {
        $exclusions = '';
    }
    if (!empty($exclude)) {
        $exterms = wp_parse_id_list($exclude);
        if (empty($exclusions)) {
            $exclusions = implode(',', $exterms);
        } else {
            $exclusions .= ', ' . implode(',', $exterms);
        }
    }
    if (!empty($exclusions)) {
        $exclusions = ' AND t.term_id NOT IN (' . $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 string|array $taxonomies A taxonomy or array of taxonomies.
     */
    $exclusions = apply_filters('list_terms_exclusions', $exclusions, $args, $taxonomies);
    if (!empty($exclusions)) {
        $where .= $exclusions;
    }
    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);
    }
    $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');
            break;
        case 'names':
            $selects = array('t.term_id', 'tt.parent', 'tt.count', 't.name');
            break;
        case 'count':
            $orderby = '';
            $order = '';
            $selects = array('COUNT(*)');
            break;
        case 'id=>name':
            $selects = array('t.term_id', 't.name', 'tt.count');
            break;
        case 'id=>slug':
            $selects = array('t.term_id', 't.slug', 'tt.count');
            break;
    }
    $_fields = $args['fields'];
    /**
     * Filter the fields to select in the terms query.
     *
     * @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 string|array $taxonomies A taxonomy or 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', 'orderby', 'order', 'limits');
    /**
     * Filter the terms query SQL clauses.
     *
     * @since 3.1.0
     *
     * @param array        $pieces     Terms query SQL clauses.
     * @param string|array $taxonomies A taxonomy or 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'] : '';
    $orderby = isset($clauses['orderby']) ? $clauses['orderby'] : '';
    $order = isset($clauses['order']) ? $clauses['order'] : '';
    $limits = isset($clauses['limits']) ? $clauses['limits'] : '';
    $query = "SELECT {$fields} FROM {$wpdb->terms} AS t {$join} WHERE {$where} {$orderby} {$order} {$limits}";
    if ('count' == $_fields) {
        $term_count = $wpdb->get_var($query);
        return $term_count;
    }
    $terms = $wpdb->get_results($query);
    if ('all' == $_fields) {
        update_term_cache($terms);
    }
    if (empty($terms)) {
        wp_cache_add($cache_key, array(), 'terms', DAY_IN_SECONDS);
        /** This filter is documented in wp-includes/taxonomy.php */
        $terms = apply_filters('get_terms', array(), $taxonomies, $args);
        return $terms;
    }
    if ($child_of) {
        $children = _get_term_hierarchy(reset($taxonomies));
        if (!empty($children)) {
            $terms = _get_term_children($child_of, $terms, reset($taxonomies));
        }
    }
    // Update term counts to include children.
    if ($args['pad_counts'] && 'all' == $_fields) {
        _pad_term_counts($terms, reset($taxonomies));
    }
    // 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, reset($taxonomies));
                if (is_array($children)) {
                    foreach ($children as $child_id) {
                        $child = get_term($child_id, reset($taxonomies));
                        if ($child->count) {
                            continue 2;
                        }
                    }
                }
                // It really is empty
                unset($terms[$k]);
            }
        }
    }
    reset($terms);
    $_terms = array();
    if ('id=>parent' == $_fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->parent;
        }
    } elseif ('ids' == $_fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->term_id;
        }
    } elseif ('names' == $_fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->name;
        }
    } elseif ('id=>name' == $_fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->name;
        }
    } elseif ('id=>slug' == $_fields) {
        while ($term = array_shift($terms)) {
            $_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);
    }
    wp_cache_add($cache_key, $terms, 'terms', DAY_IN_SECONDS);
    /** This filter is documented in wp-includes/taxonomy */
    $terms = apply_filters('get_terms', $terms, $taxonomies, $args);
    return $terms;
}

WordPress Version: .20

/**
 * 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 '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 'list_terms_exclusions' filter passes the compiled exclusions along with
 * the $args.
 *
 * The 'get_terms_orderby' filter passes the ORDER BY clause for the query
 * along with the $args array.
 *
 * The 'get_terms_fields' filter passes the fields for the SELECT query
 * along with the $args array.
 *
 * @since 2.3.0
 *
 * @global wpdb $wpdb WordPress database abstraction object.
 *
 * @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'), 'count' for term taxonomy count,
 *                                           'include' to match the 'order' of the $include param, or 'none'
 *                                           to skip ORDER BY. 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          $number            Maximum number of terms to return. Accepts 1+ or -1 (all).
 *                                           Default -1.
 *     @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
 *                                           term objects), 'ids' or 'names' (returns an array of integers
 *                                           or strings, respectively. Default 'all'.
 *     @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 string       $cache_domain      Unique cache key to be produced when this query is stored in an
 *                                           object cache. Default is 'core'.
 * }
 * @return array|WP_Error List of Term Objects 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)) {
            $error = new WP_Error('invalid_taxonomy', __('Invalid taxonomy'));
            return $error;
        }
    }
    $defaults = array('orderby' => 'name', 'order' => 'ASC', 'hide_empty' => true, 'exclude' => array(), 'exclude_tree' => array(), 'include' => array(), 'number' => '', 'fields' => 'all', 'slug' => '', 'parent' => '', 'hierarchical' => true, 'child_of' => 0, 'get' => '', 'name__like' => '', 'description__like' => '', 'pad_counts' => false, 'offset' => '', 'search' => '', 'cache_domain' => 'core');
    $args = wp_parse_args($args, $defaults);
    $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.
    if (!$single_taxonomy || !is_taxonomy_hierarchical(reset($taxonomies))) {
        $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['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 arguments.
     * @param string|array $taxonomies A taxonomy or array of taxonomies.
     */
    $args = apply_filters('get_terms_args', $args, $taxonomies);
    $child_of = $args['child_of'];
    if ($child_of) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$child_of])) {
            return $empty_array;
        }
    }
    $parent = $args['parent'];
    if ($parent) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$parent])) {
            return $empty_array;
        }
    }
    // $args can be whatever, only use the args defined in defaults to compute the key
    $filter_key = has_filter('list_terms_exclusions') ? serialize($GLOBALS['wp_filter']['list_terms_exclusions']) : '';
    $key = md5(serialize(wp_array_slice_assoc($args, array_keys($defaults))) . serialize($taxonomies) . $filter_key);
    $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) {
        /**
         * Filter the given taxonomy's terms cache.
         *
         * @since 2.3.0
         *
         * @param array        $cache      Cached array of terms for the given taxonomy.
         * @param string|array $taxonomies A taxonomy or array of taxonomies.
         * @param array        $args       An array of arguments to get terms.
         */
        $cache = apply_filters('get_terms', $cache, $taxonomies, $args);
        return $cache;
    }
    $_orderby = strtolower($args['orderby']);
    if ('count' == $_orderby) {
        $orderby = 'tt.count';
    } else if ('name' == $_orderby) {
        $orderby = 't.name';
    } else if ('slug' == $_orderby) {
        $orderby = 't.slug';
    } else if ('include' == $_orderby && !empty($args['include'])) {
        $include = implode(',', array_map('absint', $args['include']));
        $orderby = "FIELD( t.term_id, {$include} )";
    } else if ('term_group' == $_orderby) {
        $orderby = 't.term_group';
    } else if ('none' == $_orderby) {
        $orderby = '';
    } elseif (empty($_orderby) || '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 string|array $taxonomies A taxonomy or 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("', '", array_map('esc_sql', $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;
    }
    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 = implode(',', array_map('intval', $excluded_children));
    } else {
        $exclusions = '';
    }
    if (!empty($exclude)) {
        $exterms = wp_parse_id_list($exclude);
        if (empty($exclusions)) {
            $exclusions = implode(',', $exterms);
        } else {
            $exclusions .= ', ' . implode(',', $exterms);
        }
    }
    if (!empty($exclusions)) {
        $exclusions = ' AND t.term_id NOT IN (' . $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 string|array $taxonomies A taxonomy or array of taxonomies.
     */
    $exclusions = apply_filters('list_terms_exclusions', $exclusions, $args, $taxonomies);
    if (!empty($exclusions)) {
        $where .= $exclusions;
    }
    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);
    }
    $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');
            break;
        case 'names':
            $selects = array('t.term_id', 'tt.parent', 'tt.count', 't.name');
            break;
        case 'count':
            $orderby = '';
            $order = '';
            $selects = array('COUNT(*)');
            break;
        case 'id=>name':
            $selects = array('t.term_id', 't.name', 'tt.count');
            break;
        case 'id=>slug':
            $selects = array('t.term_id', 't.slug', 'tt.count');
            break;
    }
    $_fields = $args['fields'];
    /**
     * Filter the fields to select in the terms query.
     *
     * @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 string|array $taxonomies A taxonomy or 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', 'orderby', 'order', 'limits');
    /**
     * Filter the terms query SQL clauses.
     *
     * @since 3.1.0
     *
     * @param array        $pieces     Terms query SQL clauses.
     * @param string|array $taxonomies A taxonomy or 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'] : '';
    $orderby = isset($clauses['orderby']) ? $clauses['orderby'] : '';
    $order = isset($clauses['order']) ? $clauses['order'] : '';
    $limits = isset($clauses['limits']) ? $clauses['limits'] : '';
    $query = "SELECT {$fields} FROM {$wpdb->terms} AS t {$join} WHERE {$where} {$orderby} {$order} {$limits}";
    if ('count' == $_fields) {
        $term_count = $wpdb->get_var($query);
        return $term_count;
    }
    $terms = $wpdb->get_results($query);
    if ('all' == $_fields) {
        update_term_cache($terms);
    }
    if (empty($terms)) {
        wp_cache_add($cache_key, array(), 'terms', DAY_IN_SECONDS);
        /** This filter is documented in wp-includes/taxonomy.php */
        $terms = apply_filters('get_terms', array(), $taxonomies, $args);
        return $terms;
    }
    if ($child_of) {
        $children = _get_term_hierarchy(reset($taxonomies));
        if (!empty($children)) {
            $terms = _get_term_children($child_of, $terms, reset($taxonomies));
        }
    }
    // Update term counts to include children.
    if ($args['pad_counts'] && 'all' == $_fields) {
        _pad_term_counts($terms, reset($taxonomies));
    }
    // 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, reset($taxonomies));
                if (is_array($children)) {
                    foreach ($children as $child_id) {
                        $child = get_term($child_id, reset($taxonomies));
                        if ($child->count) {
                            continue 2;
                        }
                    }
                }
                // It really is empty
                unset($terms[$k]);
            }
        }
    }
    reset($terms);
    $_terms = array();
    if ('id=>parent' == $_fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->parent;
        }
    } elseif ('ids' == $_fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->term_id;
        }
    } elseif ('names' == $_fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->name;
        }
    } elseif ('id=>name' == $_fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->name;
        }
    } elseif ('id=>slug' == $_fields) {
        while ($term = array_shift($terms)) {
            $_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);
    }
    wp_cache_add($cache_key, $terms, 'terms', DAY_IN_SECONDS);
    /** This filter is documented in wp-includes/taxonomy */
    $terms = apply_filters('get_terms', $terms, $taxonomies, $args);
    return $terms;
}

WordPress Version: 1.2

/**
 * 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 '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 'list_terms_exclusions' filter passes the compiled exclusions along with
 * the $args.
 *
 * The 'get_terms_orderby' filter passes the ORDER BY clause for the query
 * along with the $args array.
 *
 * The 'get_terms_fields' filter passes the fields for the SELECT query
 * along with the $args array.
 *
 * @since 2.3.0
 *
 * @global wpdb $wpdb WordPress database abstraction object.
 *
 * @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'), 'count' for term taxonomy count,
 *                                           'include' to match the 'order' of the $include param, or 'none'
 *                                           to skip ORDER BY. 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          $number            Maximum number of terms to return. Accepts 1+ or -1 (all).
 *                                           Default -1.
 *     @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
 *                                           term objects), 'ids' or 'names' (returns an array of integers
 *                                           or strings, respectively. Default 'all'.
 *     @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 string       $cache_domain      Unique cache key to be produced when this query is stored in an
 *                                           object cache. Default is 'core'.
 * }
 * @return array|WP_Error List of Term Objects 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)) {
            $error = new WP_Error('invalid_taxonomy', __('Invalid taxonomy'));
            return $error;
        }
    }
    $defaults = array('orderby' => 'name', 'order' => 'ASC', 'hide_empty' => true, 'exclude' => array(), 'exclude_tree' => array(), 'include' => array(), 'number' => '', 'fields' => 'all', 'slug' => '', 'parent' => '', 'hierarchical' => true, 'child_of' => 0, 'get' => '', 'name__like' => '', 'description__like' => '', 'pad_counts' => false, 'offset' => '', 'search' => '', 'cache_domain' => 'core');
    $args = wp_parse_args($args, $defaults);
    $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.
    if (!$single_taxonomy || !is_taxonomy_hierarchical(reset($taxonomies))) {
        $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['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 arguments.
     * @param string|array $taxonomies A taxonomy or array of taxonomies.
     */
    $args = apply_filters('get_terms_args', $args, $taxonomies);
    $child_of = $args['child_of'];
    if ($child_of) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$child_of])) {
            return $empty_array;
        }
    }
    $parent = $args['parent'];
    if ($parent) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$parent])) {
            return $empty_array;
        }
    }
    // $args can be whatever, only use the args defined in defaults to compute the key
    $filter_key = has_filter('list_terms_exclusions') ? serialize($GLOBALS['wp_filter']['list_terms_exclusions']) : '';
    $key = md5(serialize(wp_array_slice_assoc($args, array_keys($defaults))) . serialize($taxonomies) . $filter_key);
    $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) {
        /**
         * Filter the given taxonomy's terms cache.
         *
         * @since 2.3.0
         *
         * @param array        $cache      Cached array of terms for the given taxonomy.
         * @param string|array $taxonomies A taxonomy or array of taxonomies.
         * @param array        $args       An array of arguments to get terms.
         */
        $cache = apply_filters('get_terms', $cache, $taxonomies, $args);
        return $cache;
    }
    $_orderby = strtolower($args['orderby']);
    if ('count' == $_orderby) {
        $orderby = 'tt.count';
    } else if ('name' == $_orderby) {
        $orderby = 't.name';
    } else if ('slug' == $_orderby) {
        $orderby = 't.slug';
    } else if ('include' == $_orderby && !empty($args['include'])) {
        $include = implode(',', array_map('absint', $args['include']));
        $orderby = "FIELD( t.term_id, {$include} )";
    } else if ('term_group' == $_orderby) {
        $orderby = 't.term_group';
    } else if ('none' == $_orderby) {
        $orderby = '';
    } elseif (empty($_orderby) || '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 string|array $taxonomies A taxonomy or 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;
    }
    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 = implode(',', array_map('intval', $excluded_children));
    } else {
        $exclusions = '';
    }
    if (!empty($exclude)) {
        $exterms = wp_parse_id_list($exclude);
        if (empty($exclusions)) {
            $exclusions = implode(',', $exterms);
        } else {
            $exclusions .= ', ' . implode(',', $exterms);
        }
    }
    if (!empty($exclusions)) {
        $exclusions = ' AND t.term_id NOT IN (' . $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 string|array $taxonomies A taxonomy or array of taxonomies.
     */
    $exclusions = apply_filters('list_terms_exclusions', $exclusions, $args, $taxonomies);
    if (!empty($exclusions)) {
        $where .= $exclusions;
    }
    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);
    }
    $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');
            break;
        case 'names':
            $selects = array('t.term_id', 'tt.parent', 'tt.count', 't.name');
            break;
        case 'count':
            $orderby = '';
            $order = '';
            $selects = array('COUNT(*)');
            break;
        case 'id=>name':
            $selects = array('t.term_id', 't.name', 'tt.count');
            break;
        case 'id=>slug':
            $selects = array('t.term_id', 't.slug', 'tt.count');
            break;
    }
    $_fields = $args['fields'];
    /**
     * Filter the fields to select in the terms query.
     *
     * @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 string|array $taxonomies A taxonomy or 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', 'orderby', 'order', 'limits');
    /**
     * Filter the terms query SQL clauses.
     *
     * @since 3.1.0
     *
     * @param array        $pieces     Terms query SQL clauses.
     * @param string|array $taxonomies A taxonomy or 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'] : '';
    $orderby = isset($clauses['orderby']) ? $clauses['orderby'] : '';
    $order = isset($clauses['order']) ? $clauses['order'] : '';
    $limits = isset($clauses['limits']) ? $clauses['limits'] : '';
    $query = "SELECT {$fields} FROM {$wpdb->terms} AS t {$join} WHERE {$where} {$orderby} {$order} {$limits}";
    if ('count' == $_fields) {
        $term_count = $wpdb->get_var($query);
        return $term_count;
    }
    $terms = $wpdb->get_results($query);
    if ('all' == $_fields) {
        update_term_cache($terms);
    }
    if (empty($terms)) {
        wp_cache_add($cache_key, array(), 'terms', DAY_IN_SECONDS);
        /** This filter is documented in wp-includes/taxonomy.php */
        $terms = apply_filters('get_terms', array(), $taxonomies, $args);
        return $terms;
    }
    if ($child_of) {
        $children = _get_term_hierarchy(reset($taxonomies));
        if (!empty($children)) {
            $terms = _get_term_children($child_of, $terms, reset($taxonomies));
        }
    }
    // Update term counts to include children.
    if ($args['pad_counts'] && 'all' == $_fields) {
        _pad_term_counts($terms, reset($taxonomies));
    }
    // 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, reset($taxonomies));
                if (is_array($children)) {
                    foreach ($children as $child_id) {
                        $child = get_term($child_id, reset($taxonomies));
                        if ($child->count) {
                            continue 2;
                        }
                    }
                }
                // It really is empty
                unset($terms[$k]);
            }
        }
    }
    reset($terms);
    $_terms = array();
    if ('id=>parent' == $_fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->parent;
        }
    } elseif ('ids' == $_fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->term_id;
        }
    } elseif ('names' == $_fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->name;
        }
    } elseif ('id=>name' == $_fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->name;
        }
    } elseif ('id=>slug' == $_fields) {
        while ($term = array_shift($terms)) {
            $_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);
    }
    wp_cache_add($cache_key, $terms, 'terms', DAY_IN_SECONDS);
    /** This filter is documented in wp-includes/taxonomy */
    $terms = apply_filters('get_terms', $terms, $taxonomies, $args);
    return $terms;
}

WordPress Version: .11

/**
 * 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 '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 'list_terms_exclusions' filter passes the compiled exclusions along with
 * the $args.
 *
 * The 'get_terms_orderby' filter passes the ORDER BY clause for the query
 * along with the $args array.
 *
 * The 'get_terms_fields' filter passes the fields for the SELECT query
 * along with the $args array.
 *
 * @since 2.3.0
 *
 * @global wpdb $wpdb WordPress database abstraction object.
 *
 * @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'), 'count' for term taxonomy count,
 *                                           'include' to match the 'order' of the $include param, or 'none'
 *                                           to skip ORDER BY. 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          $number            Maximum number of terms to return. Accepts 1+ or -1 (all).
 *                                           Default -1.
 *     @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
 *                                           term objects), 'ids' or 'names' (returns an array of integers
 *                                           or strings, respectively. Default 'all'.
 *     @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 string       $cache_domain      Unique cache key to be produced when this query is stored in an
 *                                           object cache. Default is 'core'.
 * }
 * @return array|WP_Error List of Term Objects 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)) {
            $error = new WP_Error('invalid_taxonomy', __('Invalid taxonomy'));
            return $error;
        }
    }
    $defaults = array('orderby' => 'name', 'order' => 'ASC', 'hide_empty' => true, 'exclude' => array(), 'exclude_tree' => array(), 'include' => array(), 'number' => '', 'fields' => 'all', 'slug' => '', 'parent' => '', 'hierarchical' => true, 'child_of' => 0, 'get' => '', 'name__like' => '', 'description__like' => '', 'pad_counts' => false, 'offset' => '', 'search' => '', 'cache_domain' => 'core');
    $args = wp_parse_args($args, $defaults);
    $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.
    if (!$single_taxonomy || !is_taxonomy_hierarchical(reset($taxonomies))) {
        $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['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 arguments.
     * @param string|array $taxonomies A taxonomy or array of taxonomies.
     */
    $args = apply_filters('get_terms_args', $args, $taxonomies);
    $child_of = $args['child_of'];
    if ($child_of) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$child_of])) {
            return $empty_array;
        }
    }
    $parent = $args['parent'];
    if ($parent) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$parent])) {
            return $empty_array;
        }
    }
    // $args can be whatever, only use the args defined in defaults to compute the key
    $filter_key = has_filter('list_terms_exclusions') ? serialize($GLOBALS['wp_filter']['list_terms_exclusions']) : '';
    $key = md5(serialize(wp_array_slice_assoc($args, array_keys($defaults))) . serialize($taxonomies) . $filter_key);
    $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) {
        /**
         * Filter the given taxonomy's terms cache.
         *
         * @since 2.3.0
         *
         * @param array        $cache      Cached array of terms for the given taxonomy.
         * @param string|array $taxonomies A taxonomy or array of taxonomies.
         * @param array        $args       An array of arguments to get terms.
         */
        $cache = apply_filters('get_terms', $cache, $taxonomies, $args);
        return $cache;
    }
    $_orderby = strtolower($args['orderby']);
    if ('count' == $_orderby) {
        $orderby = 'tt.count';
    } else if ('name' == $_orderby) {
        $orderby = 't.name';
    } else if ('slug' == $_orderby) {
        $orderby = 't.slug';
    } else if ('include' == $_orderby && !empty($args['include'])) {
        $include = implode(',', array_map('absint', $args['include']));
        $orderby = "FIELD( t.term_id, {$include} )";
    } else if ('term_group' == $_orderby) {
        $orderby = 't.term_group';
    } else if ('none' == $_orderby) {
        $orderby = '';
    } elseif (empty($_orderby) || '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 string|array $taxonomies A taxonomy or 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("', '", array_map('esc_sql', $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;
    }
    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 = implode(',', array_map('intval', $excluded_children));
    } else {
        $exclusions = '';
    }
    if (!empty($exclude)) {
        $exterms = wp_parse_id_list($exclude);
        if (empty($exclusions)) {
            $exclusions = implode(',', $exterms);
        } else {
            $exclusions .= ', ' . implode(',', $exterms);
        }
    }
    if (!empty($exclusions)) {
        $exclusions = ' AND t.term_id NOT IN (' . $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 string|array $taxonomies A taxonomy or array of taxonomies.
     */
    $exclusions = apply_filters('list_terms_exclusions', $exclusions, $args, $taxonomies);
    if (!empty($exclusions)) {
        $where .= $exclusions;
    }
    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);
    }
    $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');
            break;
        case 'names':
            $selects = array('t.term_id', 'tt.parent', 'tt.count', 't.name');
            break;
        case 'count':
            $orderby = '';
            $order = '';
            $selects = array('COUNT(*)');
            break;
        case 'id=>name':
            $selects = array('t.term_id', 't.name', 'tt.count');
            break;
        case 'id=>slug':
            $selects = array('t.term_id', 't.slug', 'tt.count');
            break;
    }
    $_fields = $args['fields'];
    /**
     * Filter the fields to select in the terms query.
     *
     * @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 string|array $taxonomies A taxonomy or 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', 'orderby', 'order', 'limits');
    /**
     * Filter the terms query SQL clauses.
     *
     * @since 3.1.0
     *
     * @param array        $pieces     Terms query SQL clauses.
     * @param string|array $taxonomies A taxonomy or 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'] : '';
    $orderby = isset($clauses['orderby']) ? $clauses['orderby'] : '';
    $order = isset($clauses['order']) ? $clauses['order'] : '';
    $limits = isset($clauses['limits']) ? $clauses['limits'] : '';
    $query = "SELECT {$fields} FROM {$wpdb->terms} AS t {$join} WHERE {$where} {$orderby} {$order} {$limits}";
    if ('count' == $_fields) {
        $term_count = $wpdb->get_var($query);
        return $term_count;
    }
    $terms = $wpdb->get_results($query);
    if ('all' == $_fields) {
        update_term_cache($terms);
    }
    if (empty($terms)) {
        wp_cache_add($cache_key, array(), 'terms', DAY_IN_SECONDS);
        /** This filter is documented in wp-includes/taxonomy.php */
        $terms = apply_filters('get_terms', array(), $taxonomies, $args);
        return $terms;
    }
    if ($child_of) {
        $children = _get_term_hierarchy(reset($taxonomies));
        if (!empty($children)) {
            $terms = _get_term_children($child_of, $terms, reset($taxonomies));
        }
    }
    // Update term counts to include children.
    if ($args['pad_counts'] && 'all' == $_fields) {
        _pad_term_counts($terms, reset($taxonomies));
    }
    // 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, reset($taxonomies));
                if (is_array($children)) {
                    foreach ($children as $child_id) {
                        $child = get_term($child_id, reset($taxonomies));
                        if ($child->count) {
                            continue 2;
                        }
                    }
                }
                // It really is empty
                unset($terms[$k]);
            }
        }
    }
    reset($terms);
    $_terms = array();
    if ('id=>parent' == $_fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->parent;
        }
    } elseif ('ids' == $_fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->term_id;
        }
    } elseif ('names' == $_fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->name;
        }
    } elseif ('id=>name' == $_fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->name;
        }
    } elseif ('id=>slug' == $_fields) {
        while ($term = array_shift($terms)) {
            $_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);
    }
    wp_cache_add($cache_key, $terms, 'terms', DAY_IN_SECONDS);
    /** This filter is documented in wp-includes/taxonomy */
    $terms = apply_filters('get_terms', $terms, $taxonomies, $args);
    return $terms;
}

WordPress Version: 4.1

/**
 * 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 '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 'list_terms_exclusions' filter passes the compiled exclusions along with
 * the $args.
 *
 * The 'get_terms_orderby' filter passes the ORDER BY clause for the query
 * along with the $args array.
 *
 * The 'get_terms_fields' filter passes the fields for the SELECT query
 * along with the $args array.
 *
 * @since 2.3.0
 *
 * @global wpdb $wpdb WordPress database abstraction object.
 *
 * @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'), 'count' for term taxonomy count,
 *                                           'include' to match the 'order' of the $include param, or 'none'
 *                                           to skip ORDER BY. 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          $number            Maximum number of terms to return. Accepts 1+ or -1 (all).
 *                                           Default -1.
 *     @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
 *                                           term objects), 'ids' or 'names' (returns an array of integers
 *                                           or strings, respectively. Default 'all'.
 *     @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 string       $cache_domain      Unique cache key to be produced when this query is stored in an
 *                                           object cache. Default is 'core'.
 * }
 * @return array|WP_Error List of Term Objects 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)) {
            $error = new WP_Error('invalid_taxonomy', __('Invalid taxonomy'));
            return $error;
        }
    }
    $defaults = array('orderby' => 'name', 'order' => 'ASC', 'hide_empty' => true, 'exclude' => array(), 'exclude_tree' => array(), 'include' => array(), 'number' => '', 'fields' => 'all', 'slug' => '', 'parent' => '', 'hierarchical' => true, 'child_of' => 0, 'get' => '', 'name__like' => '', 'description__like' => '', 'pad_counts' => false, 'offset' => '', 'search' => '', 'cache_domain' => 'core');
    $args = wp_parse_args($args, $defaults);
    $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.
    if (!$single_taxonomy || !is_taxonomy_hierarchical(reset($taxonomies))) {
        $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['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 arguments.
     * @param string|array $taxonomies A taxonomy or array of taxonomies.
     */
    $args = apply_filters('get_terms_args', $args, $taxonomies);
    $child_of = $args['child_of'];
    if ($child_of) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$child_of])) {
            return $empty_array;
        }
    }
    $parent = $args['parent'];
    if ($parent) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$parent])) {
            return $empty_array;
        }
    }
    // $args can be whatever, only use the args defined in defaults to compute the key
    $filter_key = has_filter('list_terms_exclusions') ? serialize($GLOBALS['wp_filter']['list_terms_exclusions']) : '';
    $key = md5(serialize(wp_array_slice_assoc($args, array_keys($defaults))) . serialize($taxonomies) . $filter_key);
    $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) {
        /**
         * Filter the given taxonomy's terms cache.
         *
         * @since 2.3.0
         *
         * @param array        $cache      Cached array of terms for the given taxonomy.
         * @param string|array $taxonomies A taxonomy or array of taxonomies.
         * @param array        $args       An array of arguments to get terms.
         */
        $cache = apply_filters('get_terms', $cache, $taxonomies, $args);
        return $cache;
    }
    $_orderby = strtolower($args['orderby']);
    if ('count' == $_orderby) {
        $orderby = 'tt.count';
    } else if ('name' == $_orderby) {
        $orderby = 't.name';
    } else if ('slug' == $_orderby) {
        $orderby = 't.slug';
    } else if ('include' == $_orderby && !empty($args['include'])) {
        $include = implode(',', array_map('absint', $args['include']));
        $orderby = "FIELD( t.term_id, {$include} )";
    } else if ('term_group' == $_orderby) {
        $orderby = 't.term_group';
    } else if ('none' == $_orderby) {
        $orderby = '';
    } elseif (empty($_orderby) || '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 string|array $taxonomies A taxonomy or 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;
    }
    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 = implode(',', array_map('intval', $excluded_children));
    } else {
        $exclusions = '';
    }
    if (!empty($exclude)) {
        $exterms = wp_parse_id_list($exclude);
        if (empty($exclusions)) {
            $exclusions = implode(',', $exterms);
        } else {
            $exclusions .= ', ' . implode(',', $exterms);
        }
    }
    if (!empty($exclusions)) {
        $exclusions = ' AND t.term_id NOT IN (' . $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 string|array $taxonomies A taxonomy or array of taxonomies.
     */
    $exclusions = apply_filters('list_terms_exclusions', $exclusions, $args, $taxonomies);
    if (!empty($exclusions)) {
        $where .= $exclusions;
    }
    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);
    }
    $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');
            break;
        case 'names':
            $selects = array('t.term_id', 'tt.parent', 'tt.count', 't.name');
            break;
        case 'count':
            $orderby = '';
            $order = '';
            $selects = array('COUNT(*)');
            break;
        case 'id=>name':
            $selects = array('t.term_id', 't.name', 'tt.count');
            break;
        case 'id=>slug':
            $selects = array('t.term_id', 't.slug', 'tt.count');
            break;
    }
    $_fields = $args['fields'];
    /**
     * Filter the fields to select in the terms query.
     *
     * @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 string|array $taxonomies A taxonomy or 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', 'orderby', 'order', 'limits');
    /**
     * Filter the terms query SQL clauses.
     *
     * @since 3.1.0
     *
     * @param array        $pieces     Terms query SQL clauses.
     * @param string|array $taxonomies A taxonomy or 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'] : '';
    $orderby = isset($clauses['orderby']) ? $clauses['orderby'] : '';
    $order = isset($clauses['order']) ? $clauses['order'] : '';
    $limits = isset($clauses['limits']) ? $clauses['limits'] : '';
    $query = "SELECT {$fields} FROM {$wpdb->terms} AS t {$join} WHERE {$where} {$orderby} {$order} {$limits}";
    if ('count' == $_fields) {
        $term_count = $wpdb->get_var($query);
        return $term_count;
    }
    $terms = $wpdb->get_results($query);
    if ('all' == $_fields) {
        update_term_cache($terms);
    }
    if (empty($terms)) {
        wp_cache_add($cache_key, array(), 'terms', DAY_IN_SECONDS);
        /** This filter is documented in wp-includes/taxonomy.php */
        $terms = apply_filters('get_terms', array(), $taxonomies, $args);
        return $terms;
    }
    if ($child_of) {
        $children = _get_term_hierarchy(reset($taxonomies));
        if (!empty($children)) {
            $terms = _get_term_children($child_of, $terms, reset($taxonomies));
        }
    }
    // Update term counts to include children.
    if ($args['pad_counts'] && 'all' == $_fields) {
        _pad_term_counts($terms, reset($taxonomies));
    }
    // 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, reset($taxonomies));
                if (is_array($children)) {
                    foreach ($children as $child_id) {
                        $child = get_term($child_id, reset($taxonomies));
                        if ($child->count) {
                            continue 2;
                        }
                    }
                }
                // It really is empty
                unset($terms[$k]);
            }
        }
    }
    reset($terms);
    $_terms = array();
    if ('id=>parent' == $_fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->parent;
        }
    } elseif ('ids' == $_fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->term_id;
        }
    } elseif ('names' == $_fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->name;
        }
    } elseif ('id=>name' == $_fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->name;
        }
    } elseif ('id=>slug' == $_fields) {
        while ($term = array_shift($terms)) {
            $_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);
    }
    wp_cache_add($cache_key, $terms, 'terms', DAY_IN_SECONDS);
    /** This filter is documented in wp-includes/taxonomy */
    $terms = apply_filters('get_terms', $terms, $taxonomies, $args);
    return $terms;
}

WordPress Version: 0.4

/**
 * 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 '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 'list_terms_exclusions' filter passes the compiled exclusions along with
 * the $args.
 *
 * The 'get_terms_orderby' filter passes the ORDER BY clause for the query
 * along with the $args array.
 *
 * The 'get_terms_fields' filter passes the fields for the SELECT query
 * along with the $args array.
 *
 * @since 2.3.0
 *
 * @global wpdb $wpdb WordPress database access abstraction object.
 *
 * @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, though
 *                                           empty defaults to 'term_id'. Default '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          $number            Maximum number of terms to return. Accepts 1+ or -1 (all).
 *                                           Default -1.
 *     @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
 *                                           term objects), 'ids' or 'names' (returns an array of integers
 *                                           or strings, respectively. Default 'all'.
 *     @type string       $slug              Slug 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 string       $cache_domain      Unique cache key to be produced when this query is stored in an
 *                                           object cache. Default is 'core'.
 * }
 * @return array|WP_Error List of Term Objects 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)) {
            $error = new WP_Error('invalid_taxonomy', __('Invalid taxonomy'));
            return $error;
        }
    }
    $defaults = array('orderby' => 'name', 'order' => 'ASC', 'hide_empty' => true, 'exclude' => array(), 'exclude_tree' => array(), 'include' => array(), 'number' => '', 'fields' => 'all', 'slug' => '', 'parent' => '', 'hierarchical' => true, 'child_of' => 0, 'get' => '', 'name__like' => '', 'description__like' => '', 'pad_counts' => false, 'offset' => '', 'search' => '', 'cache_domain' => 'core');
    $args = wp_parse_args($args, $defaults);
    $args['number'] = absint($args['number']);
    $args['offset'] = absint($args['offset']);
    if (!$single_taxonomy || !is_taxonomy_hierarchical(reset($taxonomies)) || '' !== $args['parent'] && 0 !== $args['parent']) {
        $args['child_of'] = 0;
        $args['hierarchical'] = false;
        $args['pad_counts'] = false;
    }
    if ('all' == $args['get']) {
        $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 arguments.
     * @param string|array $taxonomies A taxonomy or array of taxonomies.
     */
    $args = apply_filters('get_terms_args', $args, $taxonomies);
    $child_of = $args['child_of'];
    if ($child_of) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$child_of])) {
            return $empty_array;
        }
    }
    $parent = $args['parent'];
    if ($parent) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$parent])) {
            return $empty_array;
        }
    }
    // $args can be whatever, only use the args defined in defaults to compute the key
    $filter_key = has_filter('list_terms_exclusions') ? serialize($GLOBALS['wp_filter']['list_terms_exclusions']) : '';
    $key = md5(serialize(wp_array_slice_assoc($args, array_keys($defaults))) . serialize($taxonomies) . $filter_key);
    $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) {
        /**
         * Filter the given taxonomy's terms cache.
         *
         * @since 2.3.0
         *
         * @param array        $cache      Cached array of terms for the given taxonomy.
         * @param string|array $taxonomies A taxonomy or array of taxonomies.
         * @param array        $args       An array of arguments to get terms.
         */
        $cache = apply_filters('get_terms', $cache, $taxonomies, $args);
        return $cache;
    }
    $_orderby = strtolower($args['orderby']);
    if ('count' == $_orderby) {
        $orderby = 'tt.count';
    } else if ('name' == $_orderby) {
        $orderby = 't.name';
    } else if ('slug' == $_orderby) {
        $orderby = 't.slug';
    } else if ('term_group' == $_orderby) {
        $orderby = 't.term_group';
    } else if ('none' == $_orderby) {
        $orderby = '';
    } elseif (empty($_orderby) || '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 string|array $taxonomies A taxonomy or 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;
    }
    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 = implode(',', array_map('intval', $excluded_children));
    } else {
        $exclusions = '';
    }
    if (!empty($exclude)) {
        $exterms = wp_parse_id_list($exclude);
        if (empty($exclusions)) {
            $exclusions = implode(',', $exterms);
        } else {
            $exclusions .= ', ' . implode(',', $exterms);
        }
    }
    if (!empty($exclusions)) {
        $exclusions = ' AND t.term_id NOT IN (' . $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 string|array $taxonomies A taxonomy or array of taxonomies.
     */
    $exclusions = apply_filters('list_terms_exclusions', $exclusions, $args, $taxonomies);
    if (!empty($exclusions)) {
        $where .= $exclusions;
    }
    if (!empty($args['slug'])) {
        $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);
    }
    $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');
            break;
        case 'names':
            $selects = array('t.term_id', 'tt.parent', 'tt.count', 't.name');
            break;
        case 'count':
            $orderby = '';
            $order = '';
            $selects = array('COUNT(*)');
            break;
        case 'id=>name':
            $selects = array('t.term_id', 't.name');
            break;
        case 'id=>slug':
            $selects = array('t.term_id', 't.slug');
            break;
    }
    $_fields = $args['fields'];
    /**
     * Filter the fields to select in the terms query.
     *
     * @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 string|array $taxonomies A taxonomy or 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', 'orderby', 'order', 'limits');
    /**
     * Filter the terms query SQL clauses.
     *
     * @since 3.1.0
     *
     * @param array        $pieces     Terms query SQL clauses.
     * @param string|array $taxonomies A taxonomy or 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'] : '';
    $orderby = isset($clauses['orderby']) ? $clauses['orderby'] : '';
    $order = isset($clauses['order']) ? $clauses['order'] : '';
    $limits = isset($clauses['limits']) ? $clauses['limits'] : '';
    $query = "SELECT {$fields} FROM {$wpdb->terms} AS t {$join} WHERE {$where} {$orderby} {$order} {$limits}";
    if ('count' == $_fields) {
        $term_count = $wpdb->get_var($query);
        return $term_count;
    }
    $terms = $wpdb->get_results($query);
    if ('all' == $_fields) {
        update_term_cache($terms);
    }
    if (empty($terms)) {
        wp_cache_add($cache_key, array(), 'terms', DAY_IN_SECONDS);
        /** This filter is documented in wp-includes/taxonomy.php */
        $terms = apply_filters('get_terms', array(), $taxonomies, $args);
        return $terms;
    }
    if ($child_of) {
        $children = _get_term_hierarchy(reset($taxonomies));
        if (!empty($children)) {
            $terms = _get_term_children($child_of, $terms, reset($taxonomies));
        }
    }
    // Update term counts to include children.
    if ($args['pad_counts'] && 'all' == $_fields) {
        _pad_term_counts($terms, reset($taxonomies));
    }
    // 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, reset($taxonomies));
                if (is_array($children)) {
                    foreach ($children as $child_id) {
                        $child = get_term($child_id, reset($taxonomies));
                        if ($child->count) {
                            continue 2;
                        }
                    }
                }
                // It really is empty
                unset($terms[$k]);
            }
        }
    }
    reset($terms);
    $_terms = array();
    if ('id=>parent' == $_fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->parent;
        }
    } elseif ('ids' == $_fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->term_id;
        }
    } elseif ('names' == $_fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->name;
        }
    } elseif ('id=>name' == $_fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->name;
        }
    } elseif ('id=>slug' == $_fields) {
        while ($term = array_shift($terms)) {
            $_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);
    }
    wp_cache_add($cache_key, $terms, 'terms', DAY_IN_SECONDS);
    /** This filter is documented in wp-includes/taxonomy */
    $terms = apply_filters('get_terms', $terms, $taxonomies, $args);
    return $terms;
}

WordPress Version: .30

/**
 * 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 '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 'list_terms_exclusions' filter passes the compiled exclusions along with
 * the $args.
 *
 * The 'get_terms_orderby' filter passes the ORDER BY clause for the query
 * along with the $args array.
 *
 * The 'get_terms_fields' filter passes the fields for the SELECT query
 * along with the $args array.
 *
 * @since 2.3.0
 *
 * @global wpdb $wpdb WordPress database access abstraction object.
 *
 * @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, though
 *                                           empty defaults to 'term_id'. Default '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          $number            Maximum number of terms to return. Accepts 1+ or -1 (all).
 *                                           Default -1.
 *     @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
 *                                           term objects), 'ids' or 'names' (returns an array of integers
 *                                           or strings, respectively. Default 'all'.
 *     @type string       $slug              Slug 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 string       $cache_domain      Unique cache key to be produced when this query is stored in an
 *                                           object cache. Default is 'core'.
 * }
 * @return array|WP_Error List of Term Objects 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)) {
            $error = new WP_Error('invalid_taxonomy', __('Invalid taxonomy'));
            return $error;
        }
    }
    $defaults = array('orderby' => 'name', 'order' => 'ASC', 'hide_empty' => true, 'exclude' => array(), 'exclude_tree' => array(), 'include' => array(), 'number' => '', 'fields' => 'all', 'slug' => '', 'parent' => '', 'hierarchical' => true, 'child_of' => 0, 'get' => '', 'name__like' => '', 'description__like' => '', 'pad_counts' => false, 'offset' => '', 'search' => '', 'cache_domain' => 'core');
    $args = wp_parse_args($args, $defaults);
    $args['number'] = absint($args['number']);
    $args['offset'] = absint($args['offset']);
    if (!$single_taxonomy || !is_taxonomy_hierarchical(reset($taxonomies)) || '' !== $args['parent'] && 0 !== $args['parent']) {
        $args['child_of'] = 0;
        $args['hierarchical'] = false;
        $args['pad_counts'] = false;
    }
    if ('all' == $args['get']) {
        $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 arguments.
     * @param string|array $taxonomies A taxonomy or array of taxonomies.
     */
    $args = apply_filters('get_terms_args', $args, $taxonomies);
    $child_of = $args['child_of'];
    if ($child_of) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$child_of])) {
            return $empty_array;
        }
    }
    $parent = $args['parent'];
    if ($parent) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$parent])) {
            return $empty_array;
        }
    }
    // $args can be whatever, only use the args defined in defaults to compute the key
    $filter_key = has_filter('list_terms_exclusions') ? serialize($GLOBALS['wp_filter']['list_terms_exclusions']) : '';
    $key = md5(serialize(wp_array_slice_assoc($args, array_keys($defaults))) . serialize($taxonomies) . $filter_key);
    $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) {
        /**
         * Filter the given taxonomy's terms cache.
         *
         * @since 2.3.0
         *
         * @param array        $cache      Cached array of terms for the given taxonomy.
         * @param string|array $taxonomies A taxonomy or array of taxonomies.
         * @param array        $args       An array of arguments to get terms.
         */
        $cache = apply_filters('get_terms', $cache, $taxonomies, $args);
        return $cache;
    }
    $_orderby = strtolower($args['orderby']);
    if ('count' == $_orderby) {
        $orderby = 'tt.count';
    } else if ('name' == $_orderby) {
        $orderby = 't.name';
    } else if ('slug' == $_orderby) {
        $orderby = 't.slug';
    } else if ('term_group' == $_orderby) {
        $orderby = 't.term_group';
    } else if ('none' == $_orderby) {
        $orderby = '';
    } elseif (empty($_orderby) || '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 string|array $taxonomies A taxonomy or 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("', '", array_map('esc_sql', $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;
    }
    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 = implode(',', array_map('intval', $excluded_children));
    } else {
        $exclusions = '';
    }
    if (!empty($exclude)) {
        $exterms = wp_parse_id_list($exclude);
        if (empty($exclusions)) {
            $exclusions = implode(',', $exterms);
        } else {
            $exclusions .= ', ' . implode(',', $exterms);
        }
    }
    if (!empty($exclusions)) {
        $exclusions = ' AND t.term_id NOT IN (' . $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 string|array $taxonomies A taxonomy or array of taxonomies.
     */
    $exclusions = apply_filters('list_terms_exclusions', $exclusions, $args, $taxonomies);
    if (!empty($exclusions)) {
        $where .= $exclusions;
    }
    if (!empty($args['slug'])) {
        $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);
    }
    $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');
            break;
        case 'names':
            $selects = array('t.term_id', 'tt.parent', 'tt.count', 't.name');
            break;
        case 'count':
            $orderby = '';
            $order = '';
            $selects = array('COUNT(*)');
            break;
        case 'id=>name':
            $selects = array('t.term_id', 't.name');
            break;
        case 'id=>slug':
            $selects = array('t.term_id', 't.slug');
            break;
    }
    $_fields = $args['fields'];
    /**
     * Filter the fields to select in the terms query.
     *
     * @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 string|array $taxonomies A taxonomy or 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', 'orderby', 'order', 'limits');
    /**
     * Filter the terms query SQL clauses.
     *
     * @since 3.1.0
     *
     * @param array        $pieces     Terms query SQL clauses.
     * @param string|array $taxonomies A taxonomy or 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'] : '';
    $orderby = isset($clauses['orderby']) ? $clauses['orderby'] : '';
    $order = isset($clauses['order']) ? $clauses['order'] : '';
    $limits = isset($clauses['limits']) ? $clauses['limits'] : '';
    $query = "SELECT {$fields} FROM {$wpdb->terms} AS t {$join} WHERE {$where} {$orderby} {$order} {$limits}";
    if ('count' == $_fields) {
        $term_count = $wpdb->get_var($query);
        return $term_count;
    }
    $terms = $wpdb->get_results($query);
    if ('all' == $_fields) {
        update_term_cache($terms);
    }
    if (empty($terms)) {
        wp_cache_add($cache_key, array(), 'terms', DAY_IN_SECONDS);
        /** This filter is documented in wp-includes/taxonomy.php */
        $terms = apply_filters('get_terms', array(), $taxonomies, $args);
        return $terms;
    }
    if ($child_of) {
        $children = _get_term_hierarchy(reset($taxonomies));
        if (!empty($children)) {
            $terms = _get_term_children($child_of, $terms, reset($taxonomies));
        }
    }
    // Update term counts to include children.
    if ($args['pad_counts'] && 'all' == $_fields) {
        _pad_term_counts($terms, reset($taxonomies));
    }
    // 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, reset($taxonomies));
                if (is_array($children)) {
                    foreach ($children as $child_id) {
                        $child = get_term($child_id, reset($taxonomies));
                        if ($child->count) {
                            continue 2;
                        }
                    }
                }
                // It really is empty
                unset($terms[$k]);
            }
        }
    }
    reset($terms);
    $_terms = array();
    if ('id=>parent' == $_fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->parent;
        }
    } elseif ('ids' == $_fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->term_id;
        }
    } elseif ('names' == $_fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->name;
        }
    } elseif ('id=>name' == $_fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->name;
        }
    } elseif ('id=>slug' == $_fields) {
        while ($term = array_shift($terms)) {
            $_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);
    }
    wp_cache_add($cache_key, $terms, 'terms', DAY_IN_SECONDS);
    /** This filter is documented in wp-includes/taxonomy */
    $terms = apply_filters('get_terms', $terms, $taxonomies, $args);
    return $terms;
}

WordPress Version: 0.3

/**
 * 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 '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 'list_terms_exclusions' filter passes the compiled exclusions along with
 * the $args.
 *
 * The 'get_terms_orderby' filter passes the ORDER BY clause for the query
 * along with the $args array.
 *
 * The 'get_terms_fields' filter passes the fields for the SELECT query
 * along with the $args array.
 *
 * @since 2.3.0
 *
 * @global wpdb $wpdb WordPress database access abstraction object.
 *
 * @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, though
 *                                           empty defaults to 'term_id'. Default '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          $number            Maximum number of terms to return. Accepts 1+ or -1 (all).
 *                                           Default -1.
 *     @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
 *                                           term objects), 'ids' or 'names' (returns an array of integers
 *                                           or strings, respectively. Default 'all'.
 *     @type string       $slug              Slug 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 string       $cache_domain      Unique cache key to be produced when this query is stored in an
 *                                           object cache. Default is 'core'.
 * }
 * @return array|WP_Error List of Term Objects 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)) {
            $error = new WP_Error('invalid_taxonomy', __('Invalid taxonomy'));
            return $error;
        }
    }
    $defaults = array('orderby' => 'name', 'order' => 'ASC', 'hide_empty' => true, 'exclude' => array(), 'exclude_tree' => array(), 'include' => array(), 'number' => '', 'fields' => 'all', 'slug' => '', 'parent' => '', 'hierarchical' => true, 'child_of' => 0, 'get' => '', 'name__like' => '', 'description__like' => '', 'pad_counts' => false, 'offset' => '', 'search' => '', 'cache_domain' => 'core');
    $args = wp_parse_args($args, $defaults);
    $args['number'] = absint($args['number']);
    $args['offset'] = absint($args['offset']);
    if (!$single_taxonomy || !is_taxonomy_hierarchical(reset($taxonomies)) || '' !== $args['parent'] && 0 !== $args['parent']) {
        $args['child_of'] = 0;
        $args['hierarchical'] = false;
        $args['pad_counts'] = false;
    }
    if ('all' == $args['get']) {
        $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 arguments.
     * @param string|array $taxonomies A taxonomy or array of taxonomies.
     */
    $args = apply_filters('get_terms_args', $args, $taxonomies);
    $child_of = $args['child_of'];
    if ($child_of) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$child_of])) {
            return $empty_array;
        }
    }
    $parent = $args['parent'];
    if ($parent) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$parent])) {
            return $empty_array;
        }
    }
    // $args can be whatever, only use the args defined in defaults to compute the key
    $filter_key = has_filter('list_terms_exclusions') ? serialize($GLOBALS['wp_filter']['list_terms_exclusions']) : '';
    $key = md5(serialize(wp_array_slice_assoc($args, array_keys($defaults))) . serialize($taxonomies) . $filter_key);
    $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) {
        /**
         * Filter the given taxonomy's terms cache.
         *
         * @since 2.3.0
         *
         * @param array        $cache      Cached array of terms for the given taxonomy.
         * @param string|array $taxonomies A taxonomy or array of taxonomies.
         * @param array        $args       An array of arguments to get terms.
         */
        $cache = apply_filters('get_terms', $cache, $taxonomies, $args);
        return $cache;
    }
    $_orderby = strtolower($args['orderby']);
    if ('count' == $_orderby) {
        $orderby = 'tt.count';
    } else if ('name' == $_orderby) {
        $orderby = 't.name';
    } else if ('slug' == $_orderby) {
        $orderby = 't.slug';
    } else if ('term_group' == $_orderby) {
        $orderby = 't.term_group';
    } else if ('none' == $_orderby) {
        $orderby = '';
    } elseif (empty($_orderby) || '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 string|array $taxonomies A taxonomy or 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;
    }
    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 = implode(',', array_map('intval', $excluded_children));
    } else {
        $exclusions = '';
    }
    if (!empty($exclude)) {
        $exterms = wp_parse_id_list($exclude);
        if (empty($exclusions)) {
            $exclusions = implode(',', $exterms);
        } else {
            $exclusions .= ', ' . implode(',', $exterms);
        }
    }
    if (!empty($exclusions)) {
        $exclusions = ' AND t.term_id NOT IN (' . $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 string|array $taxonomies A taxonomy or array of taxonomies.
     */
    $exclusions = apply_filters('list_terms_exclusions', $exclusions, $args, $taxonomies);
    if (!empty($exclusions)) {
        $where .= $exclusions;
    }
    if (!empty($args['slug'])) {
        $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);
    }
    $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');
            break;
        case 'names':
            $selects = array('t.term_id', 'tt.parent', 'tt.count', 't.name');
            break;
        case 'count':
            $orderby = '';
            $order = '';
            $selects = array('COUNT(*)');
            break;
        case 'id=>name':
            $selects = array('t.term_id', 't.name');
            break;
        case 'id=>slug':
            $selects = array('t.term_id', 't.slug');
            break;
    }
    $_fields = $args['fields'];
    /**
     * Filter the fields to select in the terms query.
     *
     * @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 string|array $taxonomies A taxonomy or 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', 'orderby', 'order', 'limits');
    /**
     * Filter the terms query SQL clauses.
     *
     * @since 3.1.0
     *
     * @param array        $pieces     Terms query SQL clauses.
     * @param string|array $taxonomies A taxonomy or 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'] : '';
    $orderby = isset($clauses['orderby']) ? $clauses['orderby'] : '';
    $order = isset($clauses['order']) ? $clauses['order'] : '';
    $limits = isset($clauses['limits']) ? $clauses['limits'] : '';
    $query = "SELECT {$fields} FROM {$wpdb->terms} AS t {$join} WHERE {$where} {$orderby} {$order} {$limits}";
    if ('count' == $_fields) {
        $term_count = $wpdb->get_var($query);
        return $term_count;
    }
    $terms = $wpdb->get_results($query);
    if ('all' == $_fields) {
        update_term_cache($terms);
    }
    if (empty($terms)) {
        wp_cache_add($cache_key, array(), 'terms', DAY_IN_SECONDS);
        /** This filter is documented in wp-includes/taxonomy.php */
        $terms = apply_filters('get_terms', array(), $taxonomies, $args);
        return $terms;
    }
    if ($child_of) {
        $children = _get_term_hierarchy(reset($taxonomies));
        if (!empty($children)) {
            $terms = _get_term_children($child_of, $terms, reset($taxonomies));
        }
    }
    // Update term counts to include children.
    if ($args['pad_counts'] && 'all' == $_fields) {
        _pad_term_counts($terms, reset($taxonomies));
    }
    // 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, reset($taxonomies));
                if (is_array($children)) {
                    foreach ($children as $child_id) {
                        $child = get_term($child_id, reset($taxonomies));
                        if ($child->count) {
                            continue 2;
                        }
                    }
                }
                // It really is empty
                unset($terms[$k]);
            }
        }
    }
    reset($terms);
    $_terms = array();
    if ('id=>parent' == $_fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->parent;
        }
    } elseif ('ids' == $_fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->term_id;
        }
    } elseif ('names' == $_fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->name;
        }
    } elseif ('id=>name' == $_fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->name;
        }
    } elseif ('id=>slug' == $_fields) {
        while ($term = array_shift($terms)) {
            $_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);
    }
    wp_cache_add($cache_key, $terms, 'terms', DAY_IN_SECONDS);
    /** This filter is documented in wp-includes/taxonomy */
    $terms = apply_filters('get_terms', $terms, $taxonomies, $args);
    return $terms;
}

WordPress Version: .20

/**
 * 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 '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 'list_terms_exclusions' filter passes the compiled exclusions along with
 * the $args.
 *
 * The 'get_terms_orderby' filter passes the ORDER BY clause for the query
 * along with the $args array.
 *
 * The 'get_terms_fields' filter passes the fields for the SELECT query
 * along with the $args array.
 *
 * @since 2.3.0
 *
 * @global wpdb $wpdb WordPress database access abstraction object.
 *
 * @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, though
 *                                           empty defaults to 'term_id'. Default '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          $number            Maximum number of terms to return. Accepts 1+ or -1 (all).
 *                                           Default -1.
 *     @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
 *                                           term objects), 'ids' or 'names' (returns an array of integers
 *                                           or strings, respectively. Default 'all'.
 *     @type string       $slug              Slug 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 string       $cache_domain      Unique cache key to be produced when this query is stored in an
 *                                           object cache. Default is 'core'.
 * }
 * @return array|WP_Error List of Term Objects 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)) {
            $error = new WP_Error('invalid_taxonomy', __('Invalid taxonomy'));
            return $error;
        }
    }
    $defaults = array('orderby' => 'name', 'order' => 'ASC', 'hide_empty' => true, 'exclude' => array(), 'exclude_tree' => array(), 'include' => array(), 'number' => '', 'fields' => 'all', 'slug' => '', 'parent' => '', 'hierarchical' => true, 'child_of' => 0, 'get' => '', 'name__like' => '', 'description__like' => '', 'pad_counts' => false, 'offset' => '', 'search' => '', 'cache_domain' => 'core');
    $args = wp_parse_args($args, $defaults);
    $args['number'] = absint($args['number']);
    $args['offset'] = absint($args['offset']);
    if (!$single_taxonomy || !is_taxonomy_hierarchical(reset($taxonomies)) || '' !== $args['parent'] && 0 !== $args['parent']) {
        $args['child_of'] = 0;
        $args['hierarchical'] = false;
        $args['pad_counts'] = false;
    }
    if ('all' == $args['get']) {
        $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 arguments.
     * @param string|array $taxonomies A taxonomy or array of taxonomies.
     */
    $args = apply_filters('get_terms_args', $args, $taxonomies);
    $child_of = $args['child_of'];
    if ($child_of) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$child_of])) {
            return $empty_array;
        }
    }
    $parent = $args['parent'];
    if ($parent) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$parent])) {
            return $empty_array;
        }
    }
    // $args can be whatever, only use the args defined in defaults to compute the key
    $filter_key = has_filter('list_terms_exclusions') ? serialize($GLOBALS['wp_filter']['list_terms_exclusions']) : '';
    $key = md5(serialize(wp_array_slice_assoc($args, array_keys($defaults))) . serialize($taxonomies) . $filter_key);
    $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) {
        /**
         * Filter the given taxonomy's terms cache.
         *
         * @since 2.3.0
         *
         * @param array        $cache      Cached array of terms for the given taxonomy.
         * @param string|array $taxonomies A taxonomy or array of taxonomies.
         * @param array        $args       An array of arguments to get terms.
         */
        $cache = apply_filters('get_terms', $cache, $taxonomies, $args);
        return $cache;
    }
    $_orderby = strtolower($args['orderby']);
    if ('count' == $_orderby) {
        $orderby = 'tt.count';
    } else if ('name' == $_orderby) {
        $orderby = 't.name';
    } else if ('slug' == $_orderby) {
        $orderby = 't.slug';
    } else if ('term_group' == $_orderby) {
        $orderby = 't.term_group';
    } else if ('none' == $_orderby) {
        $orderby = '';
    } elseif (empty($_orderby) || '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 string|array $taxonomies A taxonomy or 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("', '", array_map('esc_sql', $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;
    }
    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 = implode(',', array_map('intval', $excluded_children));
    } else {
        $exclusions = '';
    }
    if (!empty($exclude)) {
        $exterms = wp_parse_id_list($exclude);
        if (empty($exclusions)) {
            $exclusions = implode(',', $exterms);
        } else {
            $exclusions .= ', ' . implode(',', $exterms);
        }
    }
    if (!empty($exclusions)) {
        $exclusions = ' AND t.term_id NOT IN (' . $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 string|array $taxonomies A taxonomy or array of taxonomies.
     */
    $exclusions = apply_filters('list_terms_exclusions', $exclusions, $args, $taxonomies);
    if (!empty($exclusions)) {
        $where .= $exclusions;
    }
    if (!empty($args['slug'])) {
        $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);
    }
    $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');
            break;
        case 'names':
            $selects = array('t.term_id', 'tt.parent', 'tt.count', 't.name');
            break;
        case 'count':
            $orderby = '';
            $order = '';
            $selects = array('COUNT(*)');
            break;
        case 'id=>name':
            $selects = array('t.term_id', 't.name');
            break;
        case 'id=>slug':
            $selects = array('t.term_id', 't.slug');
            break;
    }
    $_fields = $args['fields'];
    /**
     * Filter the fields to select in the terms query.
     *
     * @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 string|array $taxonomies A taxonomy or 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', 'orderby', 'order', 'limits');
    /**
     * Filter the terms query SQL clauses.
     *
     * @since 3.1.0
     *
     * @param array        $pieces     Terms query SQL clauses.
     * @param string|array $taxonomies A taxonomy or 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'] : '';
    $orderby = isset($clauses['orderby']) ? $clauses['orderby'] : '';
    $order = isset($clauses['order']) ? $clauses['order'] : '';
    $limits = isset($clauses['limits']) ? $clauses['limits'] : '';
    $query = "SELECT {$fields} FROM {$wpdb->terms} AS t {$join} WHERE {$where} {$orderby} {$order} {$limits}";
    if ('count' == $_fields) {
        $term_count = $wpdb->get_var($query);
        return $term_count;
    }
    $terms = $wpdb->get_results($query);
    if ('all' == $_fields) {
        update_term_cache($terms);
    }
    if (empty($terms)) {
        wp_cache_add($cache_key, array(), 'terms', DAY_IN_SECONDS);
        /** This filter is documented in wp-includes/taxonomy.php */
        $terms = apply_filters('get_terms', array(), $taxonomies, $args);
        return $terms;
    }
    if ($child_of) {
        $children = _get_term_hierarchy(reset($taxonomies));
        if (!empty($children)) {
            $terms = _get_term_children($child_of, $terms, reset($taxonomies));
        }
    }
    // Update term counts to include children.
    if ($args['pad_counts'] && 'all' == $_fields) {
        _pad_term_counts($terms, reset($taxonomies));
    }
    // 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, reset($taxonomies));
                if (is_array($children)) {
                    foreach ($children as $child_id) {
                        $child = get_term($child_id, reset($taxonomies));
                        if ($child->count) {
                            continue 2;
                        }
                    }
                }
                // It really is empty
                unset($terms[$k]);
            }
        }
    }
    reset($terms);
    $_terms = array();
    if ('id=>parent' == $_fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->parent;
        }
    } elseif ('ids' == $_fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->term_id;
        }
    } elseif ('names' == $_fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->name;
        }
    } elseif ('id=>name' == $_fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->name;
        }
    } elseif ('id=>slug' == $_fields) {
        while ($term = array_shift($terms)) {
            $_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);
    }
    wp_cache_add($cache_key, $terms, 'terms', DAY_IN_SECONDS);
    /** This filter is documented in wp-includes/taxonomy */
    $terms = apply_filters('get_terms', $terms, $taxonomies, $args);
    return $terms;
}

WordPress Version: 0.2

/**
 * 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 '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 'list_terms_exclusions' filter passes the compiled exclusions along with
 * the $args.
 *
 * The 'get_terms_orderby' filter passes the ORDER BY clause for the query
 * along with the $args array.
 *
 * The 'get_terms_fields' filter passes the fields for the SELECT query
 * along with the $args array.
 *
 * @since 2.3.0
 *
 * @global wpdb $wpdb WordPress database access abstraction object.
 *
 * @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, though
 *                                           empty defaults to 'term_id'. Default '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          $number            Maximum number of terms to return. Accepts 1+ or -1 (all).
 *                                           Default -1.
 *     @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
 *                                           term objects), 'ids' or 'names' (returns an array of integers
 *                                           or strings, respectively. Default 'all'.
 *     @type string       $slug              Slug 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 string       $cache_domain      Unique cache key to be produced when this query is stored in an
 *                                           object cache. Default is 'core'.
 * }
 * @return array|WP_Error List of Term Objects 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)) {
            $error = new WP_Error('invalid_taxonomy', __('Invalid taxonomy'));
            return $error;
        }
    }
    $defaults = array('orderby' => 'name', 'order' => 'ASC', 'hide_empty' => true, 'exclude' => array(), 'exclude_tree' => array(), 'include' => array(), 'number' => '', 'fields' => 'all', 'slug' => '', 'parent' => '', 'hierarchical' => true, 'child_of' => 0, 'get' => '', 'name__like' => '', 'description__like' => '', 'pad_counts' => false, 'offset' => '', 'search' => '', 'cache_domain' => 'core');
    $args = wp_parse_args($args, $defaults);
    $args['number'] = absint($args['number']);
    $args['offset'] = absint($args['offset']);
    if (!$single_taxonomy || !is_taxonomy_hierarchical(reset($taxonomies)) || '' !== $args['parent'] && 0 !== $args['parent']) {
        $args['child_of'] = 0;
        $args['hierarchical'] = false;
        $args['pad_counts'] = false;
    }
    if ('all' == $args['get']) {
        $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 arguments.
     * @param string|array $taxonomies A taxonomy or array of taxonomies.
     */
    $args = apply_filters('get_terms_args', $args, $taxonomies);
    $child_of = $args['child_of'];
    if ($child_of) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$child_of])) {
            return $empty_array;
        }
    }
    $parent = $args['parent'];
    if ($parent) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$parent])) {
            return $empty_array;
        }
    }
    // $args can be whatever, only use the args defined in defaults to compute the key
    $filter_key = has_filter('list_terms_exclusions') ? serialize($GLOBALS['wp_filter']['list_terms_exclusions']) : '';
    $key = md5(serialize(wp_array_slice_assoc($args, array_keys($defaults))) . serialize($taxonomies) . $filter_key);
    $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) {
        /**
         * Filter the given taxonomy's terms cache.
         *
         * @since 2.3.0
         *
         * @param array        $cache      Cached array of terms for the given taxonomy.
         * @param string|array $taxonomies A taxonomy or array of taxonomies.
         * @param array        $args       An array of arguments to get terms.
         */
        $cache = apply_filters('get_terms', $cache, $taxonomies, $args);
        return $cache;
    }
    $_orderby = strtolower($args['orderby']);
    if ('count' == $_orderby) {
        $orderby = 'tt.count';
    } else if ('name' == $_orderby) {
        $orderby = 't.name';
    } else if ('slug' == $_orderby) {
        $orderby = 't.slug';
    } else if ('term_group' == $_orderby) {
        $orderby = 't.term_group';
    } else if ('none' == $_orderby) {
        $orderby = '';
    } elseif (empty($_orderby) || '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 string|array $taxonomies A taxonomy or 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;
    }
    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 = implode(',', array_map('intval', $excluded_children));
    } else {
        $exclusions = '';
    }
    if (!empty($exclude)) {
        $exterms = wp_parse_id_list($exclude);
        if (empty($exclusions)) {
            $exclusions = implode(',', $exterms);
        } else {
            $exclusions .= ', ' . implode(',', $exterms);
        }
    }
    if (!empty($exclusions)) {
        $exclusions = ' AND t.term_id NOT IN (' . $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 string|array $taxonomies A taxonomy or array of taxonomies.
     */
    $exclusions = apply_filters('list_terms_exclusions', $exclusions, $args, $taxonomies);
    if (!empty($exclusions)) {
        $where .= $exclusions;
    }
    if (!empty($args['slug'])) {
        $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);
    }
    $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');
            break;
        case 'names':
            $selects = array('t.term_id', 'tt.parent', 'tt.count', 't.name');
            break;
        case 'count':
            $orderby = '';
            $order = '';
            $selects = array('COUNT(*)');
            break;
        case 'id=>name':
            $selects = array('t.term_id', 't.name');
            break;
        case 'id=>slug':
            $selects = array('t.term_id', 't.slug');
            break;
    }
    $_fields = $args['fields'];
    /**
     * Filter the fields to select in the terms query.
     *
     * @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 string|array $taxonomies A taxonomy or 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', 'orderby', 'order', 'limits');
    /**
     * Filter the terms query SQL clauses.
     *
     * @since 3.1.0
     *
     * @param array        $pieces     Terms query SQL clauses.
     * @param string|array $taxonomies A taxonomy or 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'] : '';
    $orderby = isset($clauses['orderby']) ? $clauses['orderby'] : '';
    $order = isset($clauses['order']) ? $clauses['order'] : '';
    $limits = isset($clauses['limits']) ? $clauses['limits'] : '';
    $query = "SELECT {$fields} FROM {$wpdb->terms} AS t {$join} WHERE {$where} {$orderby} {$order} {$limits}";
    if ('count' == $_fields) {
        $term_count = $wpdb->get_var($query);
        return $term_count;
    }
    $terms = $wpdb->get_results($query);
    if ('all' == $_fields) {
        update_term_cache($terms);
    }
    if (empty($terms)) {
        wp_cache_add($cache_key, array(), 'terms', DAY_IN_SECONDS);
        /** This filter is documented in wp-includes/taxonomy.php */
        $terms = apply_filters('get_terms', array(), $taxonomies, $args);
        return $terms;
    }
    if ($child_of) {
        $children = _get_term_hierarchy(reset($taxonomies));
        if (!empty($children)) {
            $terms = _get_term_children($child_of, $terms, reset($taxonomies));
        }
    }
    // Update term counts to include children.
    if ($args['pad_counts'] && 'all' == $_fields) {
        _pad_term_counts($terms, reset($taxonomies));
    }
    // 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, reset($taxonomies));
                if (is_array($children)) {
                    foreach ($children as $child_id) {
                        $child = get_term($child_id, reset($taxonomies));
                        if ($child->count) {
                            continue 2;
                        }
                    }
                }
                // It really is empty
                unset($terms[$k]);
            }
        }
    }
    reset($terms);
    $_terms = array();
    if ('id=>parent' == $_fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->parent;
        }
    } elseif ('ids' == $_fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->term_id;
        }
    } elseif ('names' == $_fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->name;
        }
    } elseif ('id=>name' == $_fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->name;
        }
    } elseif ('id=>slug' == $_fields) {
        while ($term = array_shift($terms)) {
            $_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);
    }
    wp_cache_add($cache_key, $terms, 'terms', DAY_IN_SECONDS);
    /** This filter is documented in wp-includes/taxonomy */
    $terms = apply_filters('get_terms', $terms, $taxonomies, $args);
    return $terms;
}

WordPress Version: .11

/**
 * 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 '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 'list_terms_exclusions' filter passes the compiled exclusions along with
 * the $args.
 *
 * The 'get_terms_orderby' filter passes the ORDER BY clause for the query
 * along with the $args array.
 *
 * The 'get_terms_fields' filter passes the fields for the SELECT query
 * along with the $args array.
 *
 * @since 2.3.0
 *
 * @global wpdb $wpdb WordPress database access abstraction object.
 *
 * @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, though
 *                                           empty defaults to 'term_id'. Default '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          $number            Maximum number of terms to return. Accepts 1+ or -1 (all).
 *                                           Default -1.
 *     @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
 *                                           term objects), 'ids' or 'names' (returns an array of integers
 *                                           or strings, respectively. Default 'all'.
 *     @type string       $slug              Slug 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 string       $cache_domain      Unique cache key to be produced when this query is stored in an
 *                                           object cache. Default is 'core'.
 * }
 * @return array|WP_Error List of Term Objects 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)) {
            $error = new WP_Error('invalid_taxonomy', __('Invalid taxonomy'));
            return $error;
        }
    }
    $defaults = array('orderby' => 'name', 'order' => 'ASC', 'hide_empty' => true, 'exclude' => array(), 'exclude_tree' => array(), 'include' => array(), 'number' => '', 'fields' => 'all', 'slug' => '', 'parent' => '', 'hierarchical' => true, 'child_of' => 0, 'get' => '', 'name__like' => '', 'description__like' => '', 'pad_counts' => false, 'offset' => '', 'search' => '', 'cache_domain' => 'core');
    $args = wp_parse_args($args, $defaults);
    $args['number'] = absint($args['number']);
    $args['offset'] = absint($args['offset']);
    if (!$single_taxonomy || !is_taxonomy_hierarchical(reset($taxonomies)) || '' !== $args['parent'] && 0 !== $args['parent']) {
        $args['child_of'] = 0;
        $args['hierarchical'] = false;
        $args['pad_counts'] = false;
    }
    if ('all' == $args['get']) {
        $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 arguments.
     * @param string|array $taxonomies A taxonomy or array of taxonomies.
     */
    $args = apply_filters('get_terms_args', $args, $taxonomies);
    $child_of = $args['child_of'];
    if ($child_of) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$child_of])) {
            return $empty_array;
        }
    }
    $parent = $args['parent'];
    if ($parent) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$parent])) {
            return $empty_array;
        }
    }
    // $args can be whatever, only use the args defined in defaults to compute the key
    $filter_key = has_filter('list_terms_exclusions') ? serialize($GLOBALS['wp_filter']['list_terms_exclusions']) : '';
    $key = md5(serialize(wp_array_slice_assoc($args, array_keys($defaults))) . serialize($taxonomies) . $filter_key);
    $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) {
        /**
         * Filter the given taxonomy's terms cache.
         *
         * @since 2.3.0
         *
         * @param array        $cache      Cached array of terms for the given taxonomy.
         * @param string|array $taxonomies A taxonomy or array of taxonomies.
         * @param array        $args       An array of arguments to get terms.
         */
        $cache = apply_filters('get_terms', $cache, $taxonomies, $args);
        return $cache;
    }
    $_orderby = strtolower($args['orderby']);
    if ('count' == $_orderby) {
        $orderby = 'tt.count';
    } else if ('name' == $_orderby) {
        $orderby = 't.name';
    } else if ('slug' == $_orderby) {
        $orderby = 't.slug';
    } else if ('term_group' == $_orderby) {
        $orderby = 't.term_group';
    } else if ('none' == $_orderby) {
        $orderby = '';
    } elseif (empty($_orderby) || '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 string|array $taxonomies A taxonomy or 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("', '", array_map('esc_sql', $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;
    }
    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 = implode(',', array_map('intval', $excluded_children));
    } else {
        $exclusions = '';
    }
    if (!empty($exclude)) {
        $exterms = wp_parse_id_list($exclude);
        if (empty($exclusions)) {
            $exclusions = implode(',', $exterms);
        } else {
            $exclusions .= ', ' . implode(',', $exterms);
        }
    }
    if (!empty($exclusions)) {
        $exclusions = ' AND t.term_id NOT IN (' . $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 string|array $taxonomies A taxonomy or array of taxonomies.
     */
    $exclusions = apply_filters('list_terms_exclusions', $exclusions, $args, $taxonomies);
    if (!empty($exclusions)) {
        $where .= $exclusions;
    }
    if (!empty($args['slug'])) {
        $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);
    }
    $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');
            break;
        case 'names':
            $selects = array('t.term_id', 'tt.parent', 'tt.count', 't.name');
            break;
        case 'count':
            $orderby = '';
            $order = '';
            $selects = array('COUNT(*)');
            break;
        case 'id=>name':
            $selects = array('t.term_id', 't.name');
            break;
        case 'id=>slug':
            $selects = array('t.term_id', 't.slug');
            break;
    }
    $_fields = $args['fields'];
    /**
     * Filter the fields to select in the terms query.
     *
     * @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 string|array $taxonomies A taxonomy or 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', 'orderby', 'order', 'limits');
    /**
     * Filter the terms query SQL clauses.
     *
     * @since 3.1.0
     *
     * @param array        $pieces     Terms query SQL clauses.
     * @param string|array $taxonomies A taxonomy or 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'] : '';
    $orderby = isset($clauses['orderby']) ? $clauses['orderby'] : '';
    $order = isset($clauses['order']) ? $clauses['order'] : '';
    $limits = isset($clauses['limits']) ? $clauses['limits'] : '';
    $query = "SELECT {$fields} FROM {$wpdb->terms} AS t {$join} WHERE {$where} {$orderby} {$order} {$limits}";
    if ('count' == $_fields) {
        $term_count = $wpdb->get_var($query);
        return $term_count;
    }
    $terms = $wpdb->get_results($query);
    if ('all' == $_fields) {
        update_term_cache($terms);
    }
    if (empty($terms)) {
        wp_cache_add($cache_key, array(), 'terms', DAY_IN_SECONDS);
        /** This filter is documented in wp-includes/taxonomy.php */
        $terms = apply_filters('get_terms', array(), $taxonomies, $args);
        return $terms;
    }
    if ($child_of) {
        $children = _get_term_hierarchy(reset($taxonomies));
        if (!empty($children)) {
            $terms = _get_term_children($child_of, $terms, reset($taxonomies));
        }
    }
    // Update term counts to include children.
    if ($args['pad_counts'] && 'all' == $_fields) {
        _pad_term_counts($terms, reset($taxonomies));
    }
    // 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, reset($taxonomies));
                if (is_array($children)) {
                    foreach ($children as $child_id) {
                        $child = get_term($child_id, reset($taxonomies));
                        if ($child->count) {
                            continue 2;
                        }
                    }
                }
                // It really is empty
                unset($terms[$k]);
            }
        }
    }
    reset($terms);
    $_terms = array();
    if ('id=>parent' == $_fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->parent;
        }
    } elseif ('ids' == $_fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->term_id;
        }
    } elseif ('names' == $_fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->name;
        }
    } elseif ('id=>name' == $_fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->name;
        }
    } elseif ('id=>slug' == $_fields) {
        while ($term = array_shift($terms)) {
            $_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);
    }
    wp_cache_add($cache_key, $terms, 'terms', DAY_IN_SECONDS);
    /** This filter is documented in wp-includes/taxonomy */
    $terms = apply_filters('get_terms', $terms, $taxonomies, $args);
    return $terms;
}

WordPress Version: 4.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 '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 'list_terms_exclusions' filter passes the compiled exclusions along with
 * the $args.
 *
 * The 'get_terms_orderby' filter passes the ORDER BY clause for the query
 * along with the $args array.
 *
 * The 'get_terms_fields' filter passes the fields for the SELECT query
 * along with the $args array.
 *
 * @since 2.3.0
 *
 * @global wpdb $wpdb WordPress database access abstraction object.
 *
 * @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, though
 *                                           empty defaults to 'term_id'. Default '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          $number            Maximum number of terms to return. Accepts 1+ or -1 (all).
 *                                           Default -1.
 *     @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
 *                                           term objects), 'ids' or 'names' (returns an array of integers
 *                                           or strings, respectively. Default 'all'.
 *     @type string       $slug              Slug 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 string       $cache_domain      Unique cache key to be produced when this query is stored in an
 *                                           object cache. Default is 'core'.
 * }
 * @return array|WP_Error List of Term Objects 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)) {
            $error = new WP_Error('invalid_taxonomy', __('Invalid taxonomy'));
            return $error;
        }
    }
    $defaults = array('orderby' => 'name', 'order' => 'ASC', 'hide_empty' => true, 'exclude' => array(), 'exclude_tree' => array(), 'include' => array(), 'number' => '', 'fields' => 'all', 'slug' => '', 'parent' => '', 'hierarchical' => true, 'child_of' => 0, 'get' => '', 'name__like' => '', 'description__like' => '', 'pad_counts' => false, 'offset' => '', 'search' => '', 'cache_domain' => 'core');
    $args = wp_parse_args($args, $defaults);
    $args['number'] = absint($args['number']);
    $args['offset'] = absint($args['offset']);
    if (!$single_taxonomy || !is_taxonomy_hierarchical(reset($taxonomies)) || '' !== $args['parent'] && 0 !== $args['parent']) {
        $args['child_of'] = 0;
        $args['hierarchical'] = false;
        $args['pad_counts'] = false;
    }
    if ('all' == $args['get']) {
        $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 arguments.
     * @param string|array $taxonomies A taxonomy or array of taxonomies.
     */
    $args = apply_filters('get_terms_args', $args, $taxonomies);
    $child_of = $args['child_of'];
    if ($child_of) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$child_of])) {
            return $empty_array;
        }
    }
    $parent = $args['parent'];
    if ($parent) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$parent])) {
            return $empty_array;
        }
    }
    // $args can be whatever, only use the args defined in defaults to compute the key
    $filter_key = has_filter('list_terms_exclusions') ? serialize($GLOBALS['wp_filter']['list_terms_exclusions']) : '';
    $key = md5(serialize(wp_array_slice_assoc($args, array_keys($defaults))) . serialize($taxonomies) . $filter_key);
    $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) {
        /**
         * Filter the given taxonomy's terms cache.
         *
         * @since 2.3.0
         *
         * @param array        $cache      Cached array of terms for the given taxonomy.
         * @param string|array $taxonomies A taxonomy or array of taxonomies.
         * @param array        $args       An array of arguments to get terms.
         */
        $cache = apply_filters('get_terms', $cache, $taxonomies, $args);
        return $cache;
    }
    $_orderby = strtolower($args['orderby']);
    if ('count' == $_orderby) {
        $orderby = 'tt.count';
    } else if ('name' == $_orderby) {
        $orderby = 't.name';
    } else if ('slug' == $_orderby) {
        $orderby = 't.slug';
    } else if ('term_group' == $_orderby) {
        $orderby = 't.term_group';
    } else if ('none' == $_orderby) {
        $orderby = '';
    } elseif (empty($_orderby) || '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 string|array $taxonomies A taxonomy or 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;
    }
    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 = implode(',', array_map('intval', $excluded_children));
    } else {
        $exclusions = '';
    }
    if (!empty($exclude)) {
        $exterms = wp_parse_id_list($exclude);
        if (empty($exclusions)) {
            $exclusions = implode(',', $exterms);
        } else {
            $exclusions .= ', ' . implode(',', $exterms);
        }
    }
    if (!empty($exclusions)) {
        $exclusions = ' AND t.term_id NOT IN (' . $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 string|array $taxonomies A taxonomy or array of taxonomies.
     */
    $exclusions = apply_filters('list_terms_exclusions', $exclusions, $args, $taxonomies);
    if (!empty($exclusions)) {
        $where .= $exclusions;
    }
    if (!empty($args['slug'])) {
        $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);
    }
    $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');
            break;
        case 'names':
            $selects = array('t.term_id', 'tt.parent', 'tt.count', 't.name');
            break;
        case 'count':
            $orderby = '';
            $order = '';
            $selects = array('COUNT(*)');
            break;
        case 'id=>name':
            $selects = array('t.term_id', 't.name');
            break;
        case 'id=>slug':
            $selects = array('t.term_id', 't.slug');
            break;
    }
    $_fields = $args['fields'];
    /**
     * Filter the fields to select in the terms query.
     *
     * @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 string|array $taxonomies A taxonomy or 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', 'orderby', 'order', 'limits');
    /**
     * Filter the terms query SQL clauses.
     *
     * @since 3.1.0
     *
     * @param array        $pieces     Terms query SQL clauses.
     * @param string|array $taxonomies A taxonomy or 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'] : '';
    $orderby = isset($clauses['orderby']) ? $clauses['orderby'] : '';
    $order = isset($clauses['order']) ? $clauses['order'] : '';
    $limits = isset($clauses['limits']) ? $clauses['limits'] : '';
    $query = "SELECT {$fields} FROM {$wpdb->terms} AS t {$join} WHERE {$where} {$orderby} {$order} {$limits}";
    if ('count' == $_fields) {
        $term_count = $wpdb->get_var($query);
        return $term_count;
    }
    $terms = $wpdb->get_results($query);
    if ('all' == $_fields) {
        update_term_cache($terms);
    }
    if (empty($terms)) {
        wp_cache_add($cache_key, array(), 'terms', DAY_IN_SECONDS);
        /** This filter is documented in wp-includes/taxonomy.php */
        $terms = apply_filters('get_terms', array(), $taxonomies, $args);
        return $terms;
    }
    if ($child_of) {
        $children = _get_term_hierarchy(reset($taxonomies));
        if (!empty($children)) {
            $terms = _get_term_children($child_of, $terms, reset($taxonomies));
        }
    }
    // Update term counts to include children.
    if ($args['pad_counts'] && 'all' == $_fields) {
        _pad_term_counts($terms, reset($taxonomies));
    }
    // 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, reset($taxonomies));
                if (is_array($children)) {
                    foreach ($children as $child_id) {
                        $child = get_term($child_id, reset($taxonomies));
                        if ($child->count) {
                            continue 2;
                        }
                    }
                }
                // It really is empty
                unset($terms[$k]);
            }
        }
    }
    reset($terms);
    $_terms = array();
    if ('id=>parent' == $_fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->parent;
        }
    } elseif ('ids' == $_fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->term_id;
        }
    } elseif ('names' == $_fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->name;
        }
    } elseif ('id=>name' == $_fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->name;
        }
    } elseif ('id=>slug' == $_fields) {
        while ($term = array_shift($terms)) {
            $_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);
    }
    wp_cache_add($cache_key, $terms, 'terms', DAY_IN_SECONDS);
    /** This filter is documented in wp-includes/taxonomy */
    $terms = apply_filters('get_terms', $terms, $taxonomies, $args);
    return $terms;
}

WordPress Version: 9.2

/**
 * 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 '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 'list_terms_exclusions' filter passes the compiled exclusions along with
 * the $args.
 *
 * The 'get_terms_orderby' filter passes the ORDER BY clause for the query
 * along with the $args array.
 *
 * The 'get_terms_fields' filter passes the fields for the SELECT query
 * along with the $args array.
 *
 * The list of arguments that $args can contain, which will overwrite the defaults:
 *
 * orderby - Default is 'name'. Can be name, count, term_group, slug or nothing
 * (will use term_id), Passing a custom value other than these will cause it to
 * order based on the custom value.
 *
 * order - Default is ASC. Can use DESC.
 *
 * hide_empty - Default is true. Will not return empty terms, which means
 * terms whose count is 0 according to the given taxonomy.
 *
 * exclude - Default is an empty array. An array, comma- or space-delimited string
 * of term ids to exclude from the return array. If 'include' is non-empty,
 * 'exclude' is ignored.
 *
 * exclude_tree - Default is an empty array. An array, comma- or space-delimited
 * string of term ids to exclude from the return array, along with all of their
 * descendant terms according to the primary taxonomy. If 'include' is non-empty,
 * 'exclude_tree' is ignored.
 *
 * include - Default is an empty array. An array, comma- or space-delimited string
 * of term ids to include in the return array.
 *
 * number - The maximum number of terms to return. Default is to return them all.
 *
 * offset - The number by which to offset the terms query.
 *
 * fields - Default is 'all', which returns an array of term objects.
 * If 'fields' is 'ids' or 'names', returns an array of
 * integers or strings, respectively.
 *
 * slug - Returns terms whose "slug" matches this value. Default is empty string.
 *
 * hierarchical - Whether to include terms that have non-empty descendants
 * (even if 'hide_empty' is set to true).
 *
 * search - Returned terms' names will contain the value of 'search',
 * case-insensitive. Default is an empty string.
 *
 * name__like - Returned terms' names will contain the value of 'name__like',
 * case-insensitive. Default is empty string.
 *
 * description__like - Returned terms' descriptions will contain the value of
 *  'description__like', case-insensitive. Default is empty string.
 *
 * The argument 'pad_counts', if set to true will include the quantity of a term's
 * children in the quantity of each term's "count" object variable.
 *
 * The 'get' argument, if set to 'all' instead of its default empty string,
 * returns terms regardless of ancestry or whether the terms are empty.
 *
 * The 'child_of' argument, when used, should be set to the integer of a term ID. Its default
 * is 0. If set to a non-zero value, all returned terms will be descendants
 * of that term according to the given taxonomy. Hence 'child_of' is set to 0
 * if more than one taxonomy is passed in $taxonomies, because multiple taxonomies
 * make term ancestry ambiguous.
 *
 * The 'parent' argument, when used, should be set to the integer of a term ID. Its default is
 * the empty string '', which has a different meaning from the integer 0.
 * If set to an integer value, all returned terms will have as an immediate
 * ancestor the term whose ID is specified by that integer according to the given taxonomy.
 * The 'parent' argument is different from 'child_of' in that a term X is considered a 'parent'
 * of term Y only if term X is the father of term Y, not its grandfather or great-grandfather, etc.
 *
 * The 'cache_domain' argument enables a unique cache key to be produced when this query is stored
 * in object cache. For instance, if you are using one of this function's filters to modify the
 * query (such as 'terms_clauses'), setting 'cache_domain' to a unique value will not overwrite
 * the cache for similar queries. Default value is 'core'.
 *
 * @since 2.3.0
 *
 * @uses $wpdb
 * @uses wp_parse_args() Merges the defaults with those defined by $args and allows for strings.
 *
 * @param string|array $taxonomies Taxonomy name or list of Taxonomy names
 * @param string|array $args The values of what to search for when returning terms
 * @return array|WP_Error List of Term Objects 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)) {
            $error = new WP_Error('invalid_taxonomy', __('Invalid taxonomy'));
            return $error;
        }
    }
    $defaults = array('orderby' => 'name', 'order' => 'ASC', 'hide_empty' => true, 'exclude' => array(), 'exclude_tree' => array(), 'include' => array(), 'number' => '', 'fields' => 'all', 'slug' => '', 'parent' => '', 'hierarchical' => true, 'child_of' => 0, 'get' => '', 'name__like' => '', 'description__like' => '', 'pad_counts' => false, 'offset' => '', 'search' => '', 'cache_domain' => 'core');
    $args = wp_parse_args($args, $defaults);
    $args['number'] = absint($args['number']);
    $args['offset'] = absint($args['offset']);
    if (!$single_taxonomy || !is_taxonomy_hierarchical(reset($taxonomies)) || '' !== $args['parent'] && 0 !== $args['parent']) {
        $args['child_of'] = 0;
        $args['hierarchical'] = false;
        $args['pad_counts'] = false;
    }
    if ('all' == $args['get']) {
        $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 arguments.
     * @param string|array $taxonomies A taxonomy or array of taxonomies.
     */
    $args = apply_filters('get_terms_args', $args, $taxonomies);
    extract($args, EXTR_SKIP);
    if ($child_of) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$child_of])) {
            return $empty_array;
        }
    }
    if ($parent) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$parent])) {
            return $empty_array;
        }
    }
    // $args can be whatever, only use the args defined in defaults to compute the key
    $filter_key = has_filter('list_terms_exclusions') ? serialize($GLOBALS['wp_filter']['list_terms_exclusions']) : '';
    $key = md5(serialize(compact(array_keys($defaults))) . serialize($taxonomies) . $filter_key);
    $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) {
        /**
         * Filter the given taxonomy's terms cache.
         *
         * @since 2.3.0
         *
         * @param array        $cache      Cached array of terms for the given taxonomy.
         * @param string|array $taxonomies A taxonomy or array of taxonomies.
         * @param array        $args       An array of arguments to get terms.
         */
        $cache = apply_filters('get_terms', $cache, $taxonomies, $args);
        return $cache;
    }
    $_orderby = strtolower($orderby);
    if ('count' == $_orderby) {
        $orderby = 'tt.count';
    } else if ('name' == $_orderby) {
        $orderby = 't.name';
    } else if ('slug' == $_orderby) {
        $orderby = 't.slug';
    } else if ('term_group' == $_orderby) {
        $orderby = 't.term_group';
    } else if ('none' == $_orderby) {
        $orderby = '';
    } elseif (empty($_orderby) || '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 string|array $taxonomies A taxonomy or array of taxonomies.
     */
    $orderby = apply_filters('get_terms_orderby', $orderby, $args, $taxonomies);
    if (!empty($orderby)) {
        $orderby = "ORDER BY {$orderby}";
    } else {
        $order = '';
    }
    $order = strtoupper($order);
    if ('' !== $order && !in_array($order, array('ASC', 'DESC'))) {
        $order = 'ASC';
    }
    $where = "tt.taxonomy IN ('" . implode("', '", $taxonomies) . "')";
    $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 = '';
    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 = implode(',', array_map('intval', $excluded_children));
    }
    if (!empty($exclude)) {
        $exterms = wp_parse_id_list($exclude);
        if (empty($exclusions)) {
            $exclusions = implode(',', $exterms);
        } else {
            $exclusions .= ', ' . implode(',', $exterms);
        }
    }
    if (!empty($exclusions)) {
        $exclusions = ' AND t.term_id NOT IN (' . $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 string|array $taxonomies A taxonomy or array of taxonomies.
     */
    $exclusions = apply_filters('list_terms_exclusions', $exclusions, $args, $taxonomies);
    if (!empty($exclusions)) {
        $where .= $exclusions;
    }
    if (!empty($slug)) {
        $slug = sanitize_title($slug);
        $where .= " AND t.slug = '{$slug}'";
    }
    if (!empty($name__like)) {
        $name__like = like_escape($name__like);
        $where .= $wpdb->prepare(" AND t.name LIKE %s", '%' . $name__like . '%');
    }
    if (!empty($description__like)) {
        $description__like = like_escape($description__like);
        $where .= $wpdb->prepare(" AND tt.description LIKE %s", '%' . $description__like . '%');
    }
    if ('' !== $parent) {
        $parent = (int) $parent;
        $where .= " AND tt.parent = '{$parent}'";
    }
    if ('count' == $fields) {
        $hierarchical = false;
    }
    if ($hide_empty && !$hierarchical) {
        $where .= ' AND tt.count > 0';
    }
    // 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($search)) {
        $search = like_escape($search);
        $where .= $wpdb->prepare(' AND ((t.name LIKE %s) OR (t.slug LIKE %s))', '%' . $search . '%', '%' . $search . '%');
    }
    $selects = array();
    switch ($fields) {
        case 'all':
            $selects = array('t.*', 'tt.*');
            break;
        case 'ids':
        case 'id=>parent':
            $selects = array('t.term_id', 'tt.parent', 'tt.count');
            break;
        case 'names':
            $selects = array('t.term_id', 'tt.parent', 'tt.count', 't.name');
            break;
        case 'count':
            $orderby = '';
            $order = '';
            $selects = array('COUNT(*)');
            break;
        case 'id=>name':
            $selects = array('t.term_id', 't.name');
            break;
        case 'id=>slug':
            $selects = array('t.term_id', 't.slug');
            break;
    }
    $_fields = $fields;
    /**
     * Filter the fields to select in the terms query.
     *
     * @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 string|array $taxonomies A taxonomy or 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', 'orderby', 'order', 'limits');
    /**
     * Filter the terms query SQL clauses.
     *
     * @since 3.1.0
     *
     * @param array        $pieces     Terms query SQL clauses.
     * @param string|array $taxonomies A taxonomy or array of taxonomies.
     * @param array        $args       An array of terms query arguments.
     */
    $clauses = apply_filters('terms_clauses', compact($pieces), $taxonomies, $args);
    foreach ($pieces as $piece) {
        ${$piece} = isset($clauses[$piece]) ? $clauses[$piece] : '';
    }
    $query = "SELECT {$fields} FROM {$wpdb->terms} AS t {$join} WHERE {$where} {$orderby} {$order} {$limits}";
    $fields = $_fields;
    if ('count' == $fields) {
        $term_count = $wpdb->get_var($query);
        return $term_count;
    }
    $terms = $wpdb->get_results($query);
    if ('all' == $fields) {
        update_term_cache($terms);
    }
    if (empty($terms)) {
        wp_cache_add($cache_key, array(), 'terms', DAY_IN_SECONDS);
        /** This filter is documented in wp-includes/taxonomy.php */
        $terms = apply_filters('get_terms', array(), $taxonomies, $args);
        return $terms;
    }
    if ($child_of) {
        $children = _get_term_hierarchy(reset($taxonomies));
        if (!empty($children)) {
            $terms = _get_term_children($child_of, $terms, reset($taxonomies));
        }
    }
    // Update term counts to include children.
    if ($pad_counts && 'all' == $fields) {
        _pad_term_counts($terms, reset($taxonomies));
    }
    // Make sure we show empty categories that have children.
    if ($hierarchical && $hide_empty && is_array($terms)) {
        foreach ($terms as $k => $term) {
            if (!$term->count) {
                $children = get_term_children($term->term_id, reset($taxonomies));
                if (is_array($children)) {
                    foreach ($children as $child_id) {
                        $child = get_term($child_id, reset($taxonomies));
                        if ($child->count) {
                            continue 2;
                        }
                    }
                }
                // It really is empty
                unset($terms[$k]);
            }
        }
    }
    reset($terms);
    $_terms = array();
    if ('id=>parent' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->parent;
        }
    } elseif ('ids' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->term_id;
        }
    } elseif ('names' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->name;
        }
    } elseif ('id=>name' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->name;
        }
    } elseif ('id=>slug' == $fields) {
        while ($term = array_shift($terms)) {
            $_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);
    }
    wp_cache_add($cache_key, $terms, 'terms', DAY_IN_SECONDS);
    /** This filter is documented in wp-includes/taxonomy */
    $terms = apply_filters('get_terms', $terms, $taxonomies, $args);
    return $terms;
}

WordPress Version: .12

/**
 * 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 '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 'list_terms_exclusions' filter passes the compiled exclusions along with
 * the $args.
 *
 * The 'get_terms_orderby' filter passes the ORDER BY clause for the query
 * along with the $args array.
 *
 * The 'get_terms_fields' filter passes the fields for the SELECT query
 * along with the $args array.
 *
 * The list of arguments that $args can contain, which will overwrite the defaults:
 *
 * orderby - Default is 'name'. Can be name, count, term_group, slug or nothing
 * (will use term_id), Passing a custom value other than these will cause it to
 * order based on the custom value.
 *
 * order - Default is ASC. Can use DESC.
 *
 * hide_empty - Default is true. Will not return empty terms, which means
 * terms whose count is 0 according to the given taxonomy.
 *
 * exclude - Default is an empty array. An array, comma- or space-delimited string
 * of term ids to exclude from the return array. If 'include' is non-empty,
 * 'exclude' is ignored.
 *
 * exclude_tree - Default is an empty array. An array, comma- or space-delimited
 * string of term ids to exclude from the return array, along with all of their
 * descendant terms according to the primary taxonomy. If 'include' is non-empty,
 * 'exclude_tree' is ignored.
 *
 * include - Default is an empty array. An array, comma- or space-delimited string
 * of term ids to include in the return array.
 *
 * number - The maximum number of terms to return. Default is to return them all.
 *
 * offset - The number by which to offset the terms query.
 *
 * fields - Default is 'all', which returns an array of term objects.
 * If 'fields' is 'ids' or 'names', returns an array of
 * integers or strings, respectively.
 *
 * slug - Returns terms whose "slug" matches this value. Default is empty string.
 *
 * hierarchical - Whether to include terms that have non-empty descendants
 * (even if 'hide_empty' is set to true).
 *
 * search - Returned terms' names will contain the value of 'search',
 * case-insensitive. Default is an empty string.
 *
 * name__like - Returned terms' names will contain the value of 'name__like',
 * case-insensitive. Default is empty string.
 *
 * description__like - Returned terms' descriptions will contain the value of
 *  'description__like', case-insensitive. Default is empty string.
 *
 * The argument 'pad_counts', if set to true will include the quantity of a term's
 * children in the quantity of each term's "count" object variable.
 *
 * The 'get' argument, if set to 'all' instead of its default empty string,
 * returns terms regardless of ancestry or whether the terms are empty.
 *
 * The 'child_of' argument, when used, should be set to the integer of a term ID. Its default
 * is 0. If set to a non-zero value, all returned terms will be descendants
 * of that term according to the given taxonomy. Hence 'child_of' is set to 0
 * if more than one taxonomy is passed in $taxonomies, because multiple taxonomies
 * make term ancestry ambiguous.
 *
 * The 'parent' argument, when used, should be set to the integer of a term ID. Its default is
 * the empty string '', which has a different meaning from the integer 0.
 * If set to an integer value, all returned terms will have as an immediate
 * ancestor the term whose ID is specified by that integer according to the given taxonomy.
 * The 'parent' argument is different from 'child_of' in that a term X is considered a 'parent'
 * of term Y only if term X is the father of term Y, not its grandfather or great-grandfather, etc.
 *
 * The 'cache_domain' argument enables a unique cache key to be produced when this query is stored
 * in object cache. For instance, if you are using one of this function's filters to modify the
 * query (such as 'terms_clauses'), setting 'cache_domain' to a unique value will not overwrite
 * the cache for similar queries. Default value is 'core'.
 *
 * @since 2.3.0
 *
 * @uses $wpdb
 * @uses wp_parse_args() Merges the defaults with those defined by $args and allows for strings.
 *
 * @param string|array $taxonomies Taxonomy name or list of Taxonomy names
 * @param string|array $args The values of what to search for when returning terms
 * @return array|WP_Error List of Term Objects 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)) {
            $error = new WP_Error('invalid_taxonomy', __('Invalid taxonomy'));
            return $error;
        }
    }
    $defaults = array('orderby' => 'name', 'order' => 'ASC', 'hide_empty' => true, 'exclude' => array(), 'exclude_tree' => array(), 'include' => array(), 'number' => '', 'fields' => 'all', 'slug' => '', 'parent' => '', 'hierarchical' => true, 'child_of' => 0, 'get' => '', 'name__like' => '', 'description__like' => '', 'pad_counts' => false, 'offset' => '', 'search' => '', 'cache_domain' => 'core');
    $args = wp_parse_args($args, $defaults);
    $args['number'] = absint($args['number']);
    $args['offset'] = absint($args['offset']);
    if (!$single_taxonomy || !is_taxonomy_hierarchical(reset($taxonomies)) || '' !== $args['parent'] && 0 !== $args['parent']) {
        $args['child_of'] = 0;
        $args['hierarchical'] = false;
        $args['pad_counts'] = false;
    }
    if ('all' == $args['get']) {
        $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 arguments.
     * @param string|array $taxonomies A taxonomy or array of taxonomies.
     */
    $args = apply_filters('get_terms_args', $args, $taxonomies);
    extract($args, EXTR_SKIP);
    if ($child_of) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$child_of])) {
            return $empty_array;
        }
    }
    if ($parent) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$parent])) {
            return $empty_array;
        }
    }
    // $args can be whatever, only use the args defined in defaults to compute the key
    $filter_key = has_filter('list_terms_exclusions') ? serialize($GLOBALS['wp_filter']['list_terms_exclusions']) : '';
    $key = md5(serialize(compact(array_keys($defaults))) . serialize($taxonomies) . $filter_key);
    $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) {
        /**
         * Filter the given taxonomy's terms cache.
         *
         * @since 2.3.0
         *
         * @param array        $cache      Cached array of terms for the given taxonomy.
         * @param string|array $taxonomies A taxonomy or array of taxonomies.
         * @param array        $args       An array of arguments to get terms.
         */
        $cache = apply_filters('get_terms', $cache, $taxonomies, $args);
        return $cache;
    }
    $_orderby = strtolower($orderby);
    if ('count' == $_orderby) {
        $orderby = 'tt.count';
    } else if ('name' == $_orderby) {
        $orderby = 't.name';
    } else if ('slug' == $_orderby) {
        $orderby = 't.slug';
    } else if ('term_group' == $_orderby) {
        $orderby = 't.term_group';
    } else if ('none' == $_orderby) {
        $orderby = '';
    } elseif (empty($_orderby) || '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 string|array $taxonomies A taxonomy or array of taxonomies.
     */
    $orderby = apply_filters('get_terms_orderby', $orderby, $args, $taxonomies);
    if (!empty($orderby)) {
        $orderby = "ORDER BY {$orderby}";
    } else {
        $order = '';
    }
    $order = strtoupper($order);
    if ('' !== $order && !in_array($order, array('ASC', 'DESC'))) {
        $order = 'ASC';
    }
    $where = "tt.taxonomy IN ('" . implode("', '", array_map('esc_sql', $taxonomies)) . "')";
    $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 = '';
    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 = implode(',', array_map('intval', $excluded_children));
    }
    if (!empty($exclude)) {
        $exterms = wp_parse_id_list($exclude);
        if (empty($exclusions)) {
            $exclusions = implode(',', $exterms);
        } else {
            $exclusions .= ', ' . implode(',', $exterms);
        }
    }
    if (!empty($exclusions)) {
        $exclusions = ' AND t.term_id NOT IN (' . $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 string|array $taxonomies A taxonomy or array of taxonomies.
     */
    $exclusions = apply_filters('list_terms_exclusions', $exclusions, $args, $taxonomies);
    if (!empty($exclusions)) {
        $where .= $exclusions;
    }
    if (!empty($slug)) {
        $slug = sanitize_title($slug);
        $where .= " AND t.slug = '{$slug}'";
    }
    if (!empty($name__like)) {
        $name__like = like_escape($name__like);
        $where .= $wpdb->prepare(" AND t.name LIKE %s", '%' . $name__like . '%');
    }
    if (!empty($description__like)) {
        $description__like = like_escape($description__like);
        $where .= $wpdb->prepare(" AND tt.description LIKE %s", '%' . $description__like . '%');
    }
    if ('' !== $parent) {
        $parent = (int) $parent;
        $where .= " AND tt.parent = '{$parent}'";
    }
    if ('count' == $fields) {
        $hierarchical = false;
    }
    if ($hide_empty && !$hierarchical) {
        $where .= ' AND tt.count > 0';
    }
    // 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($search)) {
        $search = like_escape($search);
        $where .= $wpdb->prepare(' AND ((t.name LIKE %s) OR (t.slug LIKE %s))', '%' . $search . '%', '%' . $search . '%');
    }
    $selects = array();
    switch ($fields) {
        case 'all':
            $selects = array('t.*', 'tt.*');
            break;
        case 'ids':
        case 'id=>parent':
            $selects = array('t.term_id', 'tt.parent', 'tt.count');
            break;
        case 'names':
            $selects = array('t.term_id', 'tt.parent', 'tt.count', 't.name');
            break;
        case 'count':
            $orderby = '';
            $order = '';
            $selects = array('COUNT(*)');
            break;
        case 'id=>name':
            $selects = array('t.term_id', 't.name');
            break;
        case 'id=>slug':
            $selects = array('t.term_id', 't.slug');
            break;
    }
    $_fields = $fields;
    /**
     * Filter the fields to select in the terms query.
     *
     * @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 string|array $taxonomies A taxonomy or 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', 'orderby', 'order', 'limits');
    /**
     * Filter the terms query SQL clauses.
     *
     * @since 3.1.0
     *
     * @param array        $pieces     Terms query SQL clauses.
     * @param string|array $taxonomies A taxonomy or array of taxonomies.
     * @param array        $args       An array of terms query arguments.
     */
    $clauses = apply_filters('terms_clauses', compact($pieces), $taxonomies, $args);
    foreach ($pieces as $piece) {
        ${$piece} = isset($clauses[$piece]) ? $clauses[$piece] : '';
    }
    $query = "SELECT {$fields} FROM {$wpdb->terms} AS t {$join} WHERE {$where} {$orderby} {$order} {$limits}";
    $fields = $_fields;
    if ('count' == $fields) {
        $term_count = $wpdb->get_var($query);
        return $term_count;
    }
    $terms = $wpdb->get_results($query);
    if ('all' == $fields) {
        update_term_cache($terms);
    }
    if (empty($terms)) {
        wp_cache_add($cache_key, array(), 'terms', DAY_IN_SECONDS);
        /** This filter is documented in wp-includes/taxonomy.php */
        $terms = apply_filters('get_terms', array(), $taxonomies, $args);
        return $terms;
    }
    if ($child_of) {
        $children = _get_term_hierarchy(reset($taxonomies));
        if (!empty($children)) {
            $terms = _get_term_children($child_of, $terms, reset($taxonomies));
        }
    }
    // Update term counts to include children.
    if ($pad_counts && 'all' == $fields) {
        _pad_term_counts($terms, reset($taxonomies));
    }
    // Make sure we show empty categories that have children.
    if ($hierarchical && $hide_empty && is_array($terms)) {
        foreach ($terms as $k => $term) {
            if (!$term->count) {
                $children = get_term_children($term->term_id, reset($taxonomies));
                if (is_array($children)) {
                    foreach ($children as $child_id) {
                        $child = get_term($child_id, reset($taxonomies));
                        if ($child->count) {
                            continue 2;
                        }
                    }
                }
                // It really is empty
                unset($terms[$k]);
            }
        }
    }
    reset($terms);
    $_terms = array();
    if ('id=>parent' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->parent;
        }
    } elseif ('ids' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->term_id;
        }
    } elseif ('names' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->name;
        }
    } elseif ('id=>name' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->name;
        }
    } elseif ('id=>slug' == $fields) {
        while ($term = array_shift($terms)) {
            $_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);
    }
    wp_cache_add($cache_key, $terms, 'terms', DAY_IN_SECONDS);
    /** This filter is documented in wp-includes/taxonomy */
    $terms = apply_filters('get_terms', $terms, $taxonomies, $args);
    return $terms;
}

WordPress Version: 3.9

/**
 * 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 '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 'list_terms_exclusions' filter passes the compiled exclusions along with
 * the $args.
 *
 * The 'get_terms_orderby' filter passes the ORDER BY clause for the query
 * along with the $args array.
 *
 * The 'get_terms_fields' filter passes the fields for the SELECT query
 * along with the $args array.
 *
 * The list of arguments that $args can contain, which will overwrite the defaults:
 *
 * orderby - Default is 'name'. Can be name, count, term_group, slug or nothing
 * (will use term_id), Passing a custom value other than these will cause it to
 * order based on the custom value.
 *
 * order - Default is ASC. Can use DESC.
 *
 * hide_empty - Default is true. Will not return empty terms, which means
 * terms whose count is 0 according to the given taxonomy.
 *
 * exclude - Default is an empty array. An array, comma- or space-delimited string
 * of term ids to exclude from the return array. If 'include' is non-empty,
 * 'exclude' is ignored.
 *
 * exclude_tree - Default is an empty array. An array, comma- or space-delimited
 * string of term ids to exclude from the return array, along with all of their
 * descendant terms according to the primary taxonomy. If 'include' is non-empty,
 * 'exclude_tree' is ignored.
 *
 * include - Default is an empty array. An array, comma- or space-delimited string
 * of term ids to include in the return array.
 *
 * number - The maximum number of terms to return. Default is to return them all.
 *
 * offset - The number by which to offset the terms query.
 *
 * fields - Default is 'all', which returns an array of term objects.
 * If 'fields' is 'ids' or 'names', returns an array of
 * integers or strings, respectively.
 *
 * slug - Returns terms whose "slug" matches this value. Default is empty string.
 *
 * hierarchical - Whether to include terms that have non-empty descendants
 * (even if 'hide_empty' is set to true).
 *
 * search - Returned terms' names will contain the value of 'search',
 * case-insensitive. Default is an empty string.
 *
 * name__like - Returned terms' names will contain the value of 'name__like',
 * case-insensitive. Default is empty string.
 *
 * description__like - Returned terms' descriptions will contain the value of
 *  'description__like', case-insensitive. Default is empty string.
 *
 * The argument 'pad_counts', if set to true will include the quantity of a term's
 * children in the quantity of each term's "count" object variable.
 *
 * The 'get' argument, if set to 'all' instead of its default empty string,
 * returns terms regardless of ancestry or whether the terms are empty.
 *
 * The 'child_of' argument, when used, should be set to the integer of a term ID. Its default
 * is 0. If set to a non-zero value, all returned terms will be descendants
 * of that term according to the given taxonomy. Hence 'child_of' is set to 0
 * if more than one taxonomy is passed in $taxonomies, because multiple taxonomies
 * make term ancestry ambiguous.
 *
 * The 'parent' argument, when used, should be set to the integer of a term ID. Its default is
 * the empty string '', which has a different meaning from the integer 0.
 * If set to an integer value, all returned terms will have as an immediate
 * ancestor the term whose ID is specified by that integer according to the given taxonomy.
 * The 'parent' argument is different from 'child_of' in that a term X is considered a 'parent'
 * of term Y only if term X is the father of term Y, not its grandfather or great-grandfather, etc.
 *
 * The 'cache_domain' argument enables a unique cache key to be produced when this query is stored
 * in object cache. For instance, if you are using one of this function's filters to modify the
 * query (such as 'terms_clauses'), setting 'cache_domain' to a unique value will not overwrite
 * the cache for similar queries. Default value is 'core'.
 *
 * @since 2.3.0
 *
 * @uses $wpdb
 * @uses wp_parse_args() Merges the defaults with those defined by $args and allows for strings.
 *
 * @param string|array $taxonomies Taxonomy name or list of Taxonomy names
 * @param string|array $args The values of what to search for when returning terms
 * @return array|WP_Error List of Term Objects 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)) {
            $error = new WP_Error('invalid_taxonomy', __('Invalid taxonomy'));
            return $error;
        }
    }
    $defaults = array('orderby' => 'name', 'order' => 'ASC', 'hide_empty' => true, 'exclude' => array(), 'exclude_tree' => array(), 'include' => array(), 'number' => '', 'fields' => 'all', 'slug' => '', 'parent' => '', 'hierarchical' => true, 'child_of' => 0, 'get' => '', 'name__like' => '', 'description__like' => '', 'pad_counts' => false, 'offset' => '', 'search' => '', 'cache_domain' => 'core');
    $args = wp_parse_args($args, $defaults);
    $args['number'] = absint($args['number']);
    $args['offset'] = absint($args['offset']);
    if (!$single_taxonomy || !is_taxonomy_hierarchical(reset($taxonomies)) || '' !== $args['parent'] && 0 !== $args['parent']) {
        $args['child_of'] = 0;
        $args['hierarchical'] = false;
        $args['pad_counts'] = false;
    }
    if ('all' == $args['get']) {
        $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 arguments.
     * @param string|array $taxonomies A taxonomy or array of taxonomies.
     */
    $args = apply_filters('get_terms_args', $args, $taxonomies);
    extract($args, EXTR_SKIP);
    if ($child_of) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$child_of])) {
            return $empty_array;
        }
    }
    if ($parent) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$parent])) {
            return $empty_array;
        }
    }
    // $args can be whatever, only use the args defined in defaults to compute the key
    $filter_key = has_filter('list_terms_exclusions') ? serialize($GLOBALS['wp_filter']['list_terms_exclusions']) : '';
    $key = md5(serialize(compact(array_keys($defaults))) . serialize($taxonomies) . $filter_key);
    $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) {
        /**
         * Filter the given taxonomy's terms cache.
         *
         * @since 2.3.0
         *
         * @param array        $cache      Cached array of terms for the given taxonomy.
         * @param string|array $taxonomies A taxonomy or array of taxonomies.
         * @param array        $args       An array of arguments to get terms.
         */
        $cache = apply_filters('get_terms', $cache, $taxonomies, $args);
        return $cache;
    }
    $_orderby = strtolower($orderby);
    if ('count' == $_orderby) {
        $orderby = 'tt.count';
    } else if ('name' == $_orderby) {
        $orderby = 't.name';
    } else if ('slug' == $_orderby) {
        $orderby = 't.slug';
    } else if ('term_group' == $_orderby) {
        $orderby = 't.term_group';
    } else if ('none' == $_orderby) {
        $orderby = '';
    } elseif (empty($_orderby) || '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 string|array $taxonomies A taxonomy or array of taxonomies.
     */
    $orderby = apply_filters('get_terms_orderby', $orderby, $args, $taxonomies);
    if (!empty($orderby)) {
        $orderby = "ORDER BY {$orderby}";
    } else {
        $order = '';
    }
    $order = strtoupper($order);
    if ('' !== $order && !in_array($order, array('ASC', 'DESC'))) {
        $order = 'ASC';
    }
    $where = "tt.taxonomy IN ('" . implode("', '", $taxonomies) . "')";
    $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 = '';
    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 = implode(',', array_map('intval', $excluded_children));
    }
    if (!empty($exclude)) {
        $exterms = wp_parse_id_list($exclude);
        if (empty($exclusions)) {
            $exclusions = implode(',', $exterms);
        } else {
            $exclusions .= ', ' . implode(',', $exterms);
        }
    }
    if (!empty($exclusions)) {
        $exclusions = ' AND t.term_id NOT IN (' . $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 string|array $taxonomies A taxonomy or array of taxonomies.
     */
    $exclusions = apply_filters('list_terms_exclusions', $exclusions, $args, $taxonomies);
    if (!empty($exclusions)) {
        $where .= $exclusions;
    }
    if (!empty($slug)) {
        $slug = sanitize_title($slug);
        $where .= " AND t.slug = '{$slug}'";
    }
    if (!empty($name__like)) {
        $name__like = like_escape($name__like);
        $where .= $wpdb->prepare(" AND t.name LIKE %s", '%' . $name__like . '%');
    }
    if (!empty($description__like)) {
        $description__like = like_escape($description__like);
        $where .= $wpdb->prepare(" AND tt.description LIKE %s", '%' . $description__like . '%');
    }
    if ('' !== $parent) {
        $parent = (int) $parent;
        $where .= " AND tt.parent = '{$parent}'";
    }
    if ('count' == $fields) {
        $hierarchical = false;
    }
    if ($hide_empty && !$hierarchical) {
        $where .= ' AND tt.count > 0';
    }
    // 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($search)) {
        $search = like_escape($search);
        $where .= $wpdb->prepare(' AND ((t.name LIKE %s) OR (t.slug LIKE %s))', '%' . $search . '%', '%' . $search . '%');
    }
    $selects = array();
    switch ($fields) {
        case 'all':
            $selects = array('t.*', 'tt.*');
            break;
        case 'ids':
        case 'id=>parent':
            $selects = array('t.term_id', 'tt.parent', 'tt.count');
            break;
        case 'names':
            $selects = array('t.term_id', 'tt.parent', 'tt.count', 't.name');
            break;
        case 'count':
            $orderby = '';
            $order = '';
            $selects = array('COUNT(*)');
            break;
        case 'id=>name':
            $selects = array('t.term_id', 't.name');
            break;
        case 'id=>slug':
            $selects = array('t.term_id', 't.slug');
            break;
    }
    $_fields = $fields;
    /**
     * Filter the fields to select in the terms query.
     *
     * @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 string|array $taxonomies A taxonomy or 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', 'orderby', 'order', 'limits');
    /**
     * Filter the terms query SQL clauses.
     *
     * @since 3.1.0
     *
     * @param array        $pieces     Terms query SQL clauses.
     * @param string|array $taxonomies A taxonomy or array of taxonomies.
     * @param array        $args       An array of terms query arguments.
     */
    $clauses = apply_filters('terms_clauses', compact($pieces), $taxonomies, $args);
    foreach ($pieces as $piece) {
        ${$piece} = isset($clauses[$piece]) ? $clauses[$piece] : '';
    }
    $query = "SELECT {$fields} FROM {$wpdb->terms} AS t {$join} WHERE {$where} {$orderby} {$order} {$limits}";
    $fields = $_fields;
    if ('count' == $fields) {
        $term_count = $wpdb->get_var($query);
        return $term_count;
    }
    $terms = $wpdb->get_results($query);
    if ('all' == $fields) {
        update_term_cache($terms);
    }
    if (empty($terms)) {
        wp_cache_add($cache_key, array(), 'terms', DAY_IN_SECONDS);
        /** This filter is documented in wp-includes/taxonomy.php */
        $terms = apply_filters('get_terms', array(), $taxonomies, $args);
        return $terms;
    }
    if ($child_of) {
        $children = _get_term_hierarchy(reset($taxonomies));
        if (!empty($children)) {
            $terms = _get_term_children($child_of, $terms, reset($taxonomies));
        }
    }
    // Update term counts to include children.
    if ($pad_counts && 'all' == $fields) {
        _pad_term_counts($terms, reset($taxonomies));
    }
    // Make sure we show empty categories that have children.
    if ($hierarchical && $hide_empty && is_array($terms)) {
        foreach ($terms as $k => $term) {
            if (!$term->count) {
                $children = get_term_children($term->term_id, reset($taxonomies));
                if (is_array($children)) {
                    foreach ($children as $child_id) {
                        $child = get_term($child_id, reset($taxonomies));
                        if ($child->count) {
                            continue 2;
                        }
                    }
                }
                // It really is empty
                unset($terms[$k]);
            }
        }
    }
    reset($terms);
    $_terms = array();
    if ('id=>parent' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->parent;
        }
    } elseif ('ids' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->term_id;
        }
    } elseif ('names' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->name;
        }
    } elseif ('id=>name' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->name;
        }
    } elseif ('id=>slug' == $fields) {
        while ($term = array_shift($terms)) {
            $_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);
    }
    wp_cache_add($cache_key, $terms, 'terms', DAY_IN_SECONDS);
    /** This filter is documented in wp-includes/taxonomy */
    $terms = apply_filters('get_terms', $terms, $taxonomies, $args);
    return $terms;
}

WordPress Version: 8.4

/**
 * 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 '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 'list_terms_exclusions' filter passes the compiled exclusions along with
 * the $args.
 *
 * The 'get_terms_orderby' filter passes the ORDER BY clause for the query
 * along with the $args array.
 *
 * The 'get_terms_fields' filter passes the fields for the SELECT query
 * along with the $args array.
 *
 * The list of arguments that $args can contain, which will overwrite the defaults:
 *
 * orderby - Default is 'name'. Can be name, count, term_group, slug or nothing
 * (will use term_id), Passing a custom value other than these will cause it to
 * order based on the custom value.
 *
 * order - Default is ASC. Can use DESC.
 *
 * hide_empty - Default is true. Will not return empty terms, which means
 * terms whose count is 0 according to the given taxonomy.
 *
 * exclude - Default is an empty array. An array, comma- or space-delimited string
 * of term ids to exclude from the return array. If 'include' is non-empty,
 * 'exclude' is ignored.
 *
 * exclude_tree - Default is an empty array. An array, comma- or space-delimited
 * string of term ids to exclude from the return array, along with all of their
 * descendant terms according to the primary taxonomy. If 'include' is non-empty,
 * 'exclude_tree' is ignored.
 *
 * include - Default is an empty array. An array, comma- or space-delimited string
 * of term ids to include in the return array.
 *
 * number - The maximum number of terms to return. Default is to return them all.
 *
 * offset - The number by which to offset the terms query.
 *
 * fields - Default is 'all', which returns an array of term objects.
 * If 'fields' is 'ids' or 'names', returns an array of
 * integers or strings, respectively.
 *
 * slug - Returns terms whose "slug" matches this value. Default is empty string.
 *
 * hierarchical - Whether to include terms that have non-empty descendants
 * (even if 'hide_empty' is set to true).
 *
 * search - Returned terms' names will contain the value of 'search',
 * case-insensitive. Default is an empty string.
 *
 * name__like - Returned terms' names will contain the value of 'name__like',
 * case-insensitive. Default is empty string.
 *
 * description__like - Returned terms' descriptions will contain the value of
 *  'description__like', case-insensitive. Default is empty string.
 *
 * The argument 'pad_counts', if set to true will include the quantity of a term's
 * children in the quantity of each term's "count" object variable.
 *
 * The 'get' argument, if set to 'all' instead of its default empty string,
 * returns terms regardless of ancestry or whether the terms are empty.
 *
 * The 'child_of' argument, when used, should be set to the integer of a term ID. Its default
 * is 0. If set to a non-zero value, all returned terms will be descendants
 * of that term according to the given taxonomy. Hence 'child_of' is set to 0
 * if more than one taxonomy is passed in $taxonomies, because multiple taxonomies
 * make term ancestry ambiguous.
 *
 * The 'parent' argument, when used, should be set to the integer of a term ID. Its default is
 * the empty string '', which has a different meaning from the integer 0.
 * If set to an integer value, all returned terms will have as an immediate
 * ancestor the term whose ID is specified by that integer according to the given taxonomy.
 * The 'parent' argument is different from 'child_of' in that a term X is considered a 'parent'
 * of term Y only if term X is the father of term Y, not its grandfather or great-grandfather, etc.
 *
 * The 'cache_domain' argument enables a unique cache key to be produced when this query is stored
 * in object cache. For instance, if you are using one of this function's filters to modify the
 * query (such as 'terms_clauses'), setting 'cache_domain' to a unique value will not overwrite
 * the cache for similar queries. Default value is 'core'.
 *
 * @package WordPress
 * @subpackage Taxonomy
 * @since 2.3.0
 *
 * @uses $wpdb
 * @uses wp_parse_args() Merges the defaults with those defined by $args and allows for strings.
 *
 * @param string|array $taxonomies Taxonomy name or list of Taxonomy names
 * @param string|array $args The values of what to search for when returning terms
 * @return array|WP_Error List of Term Objects 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)) {
            $error = new WP_Error('invalid_taxonomy', __('Invalid taxonomy'));
            return $error;
        }
    }
    $defaults = array('orderby' => 'name', 'order' => 'ASC', 'hide_empty' => true, 'exclude' => array(), 'exclude_tree' => array(), 'include' => array(), 'number' => '', 'fields' => 'all', 'slug' => '', 'parent' => '', 'hierarchical' => true, 'child_of' => 0, 'get' => '', 'name__like' => '', 'description__like' => '', 'pad_counts' => false, 'offset' => '', 'search' => '', 'cache_domain' => 'core');
    $args = wp_parse_args($args, $defaults);
    $args['number'] = absint($args['number']);
    $args['offset'] = absint($args['offset']);
    if (!$single_taxonomy || !is_taxonomy_hierarchical(reset($taxonomies)) || '' !== $args['parent']) {
        $args['child_of'] = 0;
        $args['hierarchical'] = false;
        $args['pad_counts'] = false;
    }
    if ('all' == $args['get']) {
        $args['child_of'] = 0;
        $args['hide_empty'] = 0;
        $args['hierarchical'] = false;
        $args['pad_counts'] = false;
    }
    $args = apply_filters('get_terms_args', $args, $taxonomies);
    extract($args, EXTR_SKIP);
    if ($child_of) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$child_of])) {
            return $empty_array;
        }
    }
    if ($parent) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$parent])) {
            return $empty_array;
        }
    }
    // $args can be whatever, only use the args defined in defaults to compute the key
    $filter_key = has_filter('list_terms_exclusions') ? serialize($GLOBALS['wp_filter']['list_terms_exclusions']) : '';
    $key = md5(serialize(compact(array_keys($defaults))) . serialize($taxonomies) . $filter_key);
    $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) {
        $cache = apply_filters('get_terms', $cache, $taxonomies, $args);
        return $cache;
    }
    $_orderby = strtolower($orderby);
    if ('count' == $_orderby) {
        $orderby = 'tt.count';
    } else if ('name' == $_orderby) {
        $orderby = 't.name';
    } else if ('slug' == $_orderby) {
        $orderby = 't.slug';
    } else if ('term_group' == $_orderby) {
        $orderby = 't.term_group';
    } else if ('none' == $_orderby) {
        $orderby = '';
    } elseif (empty($_orderby) || 'id' == $_orderby) {
        $orderby = 't.term_id';
    } else {
        $orderby = 't.name';
    }
    $orderby = apply_filters('get_terms_orderby', $orderby, $args, $taxonomies);
    if (!empty($orderby)) {
        $orderby = "ORDER BY {$orderby}";
    } else {
        $order = '';
    }
    $order = strtoupper($order);
    if ('' !== $order && !in_array($order, array('ASC', 'DESC'))) {
        $order = 'ASC';
    }
    $where = "tt.taxonomy IN ('" . implode("', '", $taxonomies) . "')";
    $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 = '';
    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 = implode(',', array_map('intval', $excluded_children));
    }
    if (!empty($exclude)) {
        $exterms = wp_parse_id_list($exclude);
        if (empty($exclusions)) {
            $exclusions = implode(',', $exterms);
        } else {
            $exclusions .= ', ' . implode(',', $exterms);
        }
    }
    if (!empty($exclusions)) {
        $exclusions = ' AND t.term_id NOT IN (' . $exclusions . ')';
    }
    $exclusions = apply_filters('list_terms_exclusions', $exclusions, $args, $taxonomies);
    if (!empty($exclusions)) {
        $where .= $exclusions;
    }
    if (!empty($slug)) {
        $slug = sanitize_title($slug);
        $where .= " AND t.slug = '{$slug}'";
    }
    if (!empty($name__like)) {
        $name__like = like_escape($name__like);
        $where .= $wpdb->prepare(" AND t.name LIKE %s", '%' . $name__like . '%');
    }
    if (!empty($description__like)) {
        $description__like = like_escape($description__like);
        $where .= $wpdb->prepare(" AND tt.description LIKE %s", '%' . $description__like . '%');
    }
    if ('' !== $parent) {
        $parent = (int) $parent;
        $where .= " AND tt.parent = '{$parent}'";
    }
    if ('count' == $fields) {
        $hierarchical = false;
    }
    if ($hide_empty && !$hierarchical) {
        $where .= ' AND tt.count > 0';
    }
    // 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($search)) {
        $search = like_escape($search);
        $where .= $wpdb->prepare(' AND ((t.name LIKE %s) OR (t.slug LIKE %s))', '%' . $search . '%', '%' . $search . '%');
    }
    $selects = array();
    switch ($fields) {
        case 'all':
            $selects = array('t.*', 'tt.*');
            break;
        case 'ids':
        case 'id=>parent':
            $selects = array('t.term_id', 'tt.parent', 'tt.count');
            break;
        case 'names':
            $selects = array('t.term_id', 'tt.parent', 'tt.count', 't.name');
            break;
        case 'count':
            $orderby = '';
            $order = '';
            $selects = array('COUNT(*)');
            break;
        case 'id=>name':
            $selects = array('t.term_id', 't.name');
            break;
        case 'id=>slug':
            $selects = array('t.term_id', 't.slug');
            break;
    }
    $_fields = $fields;
    $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', 'orderby', 'order', 'limits');
    $clauses = apply_filters('terms_clauses', compact($pieces), $taxonomies, $args);
    foreach ($pieces as $piece) {
        ${$piece} = isset($clauses[$piece]) ? $clauses[$piece] : '';
    }
    $query = "SELECT {$fields} FROM {$wpdb->terms} AS t {$join} WHERE {$where} {$orderby} {$order} {$limits}";
    $fields = $_fields;
    if ('count' == $fields) {
        $term_count = $wpdb->get_var($query);
        return $term_count;
    }
    $terms = $wpdb->get_results($query);
    if ('all' == $fields) {
        update_term_cache($terms);
    }
    if (empty($terms)) {
        wp_cache_add($cache_key, array(), 'terms', DAY_IN_SECONDS);
        $terms = apply_filters('get_terms', array(), $taxonomies, $args);
        return $terms;
    }
    if ($child_of) {
        $children = _get_term_hierarchy(reset($taxonomies));
        if (!empty($children)) {
            $terms = _get_term_children($child_of, $terms, reset($taxonomies));
        }
    }
    // Update term counts to include children.
    if ($pad_counts && 'all' == $fields) {
        _pad_term_counts($terms, reset($taxonomies));
    }
    // Make sure we show empty categories that have children.
    if ($hierarchical && $hide_empty && is_array($terms)) {
        foreach ($terms as $k => $term) {
            if (!$term->count) {
                $children = _get_term_children($term->term_id, $terms, reset($taxonomies));
                if (is_array($children)) {
                    foreach ($children as $child) {
                        if ($child->count) {
                            continue 2;
                        }
                    }
                }
                // It really is empty
                unset($terms[$k]);
            }
        }
    }
    reset($terms);
    $_terms = array();
    if ('id=>parent' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->parent;
        }
    } elseif ('ids' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->term_id;
        }
    } elseif ('names' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->name;
        }
    } elseif ('id=>name' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->name;
        }
    } elseif ('id=>slug' == $fields) {
        while ($term = array_shift($terms)) {
            $_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);
    }
    wp_cache_add($cache_key, $terms, 'terms', DAY_IN_SECONDS);
    $terms = apply_filters('get_terms', $terms, $taxonomies, $args);
    return $terms;
}

WordPress Version: .30

/**
 * 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 '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 'list_terms_exclusions' filter passes the compiled exclusions along with
 * the $args.
 *
 * The 'get_terms_orderby' filter passes the ORDER BY clause for the query
 * along with the $args array.
 *
 * The 'get_terms_fields' filter passes the fields for the SELECT query
 * along with the $args array.
 *
 * The list of arguments that $args can contain, which will overwrite the defaults:
 *
 * orderby - Default is 'name'. Can be name, count, term_group, slug or nothing
 * (will use term_id), Passing a custom value other than these will cause it to
 * order based on the custom value.
 *
 * order - Default is ASC. Can use DESC.
 *
 * hide_empty - Default is true. Will not return empty terms, which means
 * terms whose count is 0 according to the given taxonomy.
 *
 * exclude - Default is an empty array. An array, comma- or space-delimited string
 * of term ids to exclude from the return array. If 'include' is non-empty,
 * 'exclude' is ignored.
 *
 * exclude_tree - Default is an empty array. An array, comma- or space-delimited
 * string of term ids to exclude from the return array, along with all of their
 * descendant terms according to the primary taxonomy. If 'include' is non-empty,
 * 'exclude_tree' is ignored.
 *
 * include - Default is an empty array. An array, comma- or space-delimited string
 * of term ids to include in the return array.
 *
 * number - The maximum number of terms to return. Default is to return them all.
 *
 * offset - The number by which to offset the terms query.
 *
 * fields - Default is 'all', which returns an array of term objects.
 * If 'fields' is 'ids' or 'names', returns an array of
 * integers or strings, respectively.
 *
 * slug - Returns terms whose "slug" matches this value. Default is empty string.
 *
 * hierarchical - Whether to include terms that have non-empty descendants
 * (even if 'hide_empty' is set to true).
 *
 * search - Returned terms' names will contain the value of 'search',
 * case-insensitive. Default is an empty string.
 *
 * name__like - Returned terms' names will contain the value of 'name__like',
 * case-insensitive. Default is empty string.
 *
 * description__like - Returned terms' descriptions will contain the value of
 *  'description__like', case-insensitive. Default is empty string.
 *
 * The argument 'pad_counts', if set to true will include the quantity of a term's
 * children in the quantity of each term's "count" object variable.
 *
 * The 'get' argument, if set to 'all' instead of its default empty string,
 * returns terms regardless of ancestry or whether the terms are empty.
 *
 * The 'child_of' argument, when used, should be set to the integer of a term ID. Its default
 * is 0. If set to a non-zero value, all returned terms will be descendants
 * of that term according to the given taxonomy. Hence 'child_of' is set to 0
 * if more than one taxonomy is passed in $taxonomies, because multiple taxonomies
 * make term ancestry ambiguous.
 *
 * The 'parent' argument, when used, should be set to the integer of a term ID. Its default is
 * the empty string '', which has a different meaning from the integer 0.
 * If set to an integer value, all returned terms will have as an immediate
 * ancestor the term whose ID is specified by that integer according to the given taxonomy.
 * The 'parent' argument is different from 'child_of' in that a term X is considered a 'parent'
 * of term Y only if term X is the father of term Y, not its grandfather or great-grandfather, etc.
 *
 * The 'cache_domain' argument enables a unique cache key to be produced when this query is stored
 * in object cache. For instance, if you are using one of this function's filters to modify the
 * query (such as 'terms_clauses'), setting 'cache_domain' to a unique value will not overwrite
 * the cache for similar queries. Default value is 'core'.
 *
 * @package WordPress
 * @subpackage Taxonomy
 * @since 2.3.0
 *
 * @uses $wpdb
 * @uses wp_parse_args() Merges the defaults with those defined by $args and allows for strings.
 *
 * @param string|array $taxonomies Taxonomy name or list of Taxonomy names
 * @param string|array $args The values of what to search for when returning terms
 * @return array|WP_Error List of Term Objects 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)) {
            $error = new WP_Error('invalid_taxonomy', __('Invalid taxonomy'));
            return $error;
        }
    }
    $defaults = array('orderby' => 'name', 'order' => 'ASC', 'hide_empty' => true, 'exclude' => array(), 'exclude_tree' => array(), 'include' => array(), 'number' => '', 'fields' => 'all', 'slug' => '', 'parent' => '', 'hierarchical' => true, 'child_of' => 0, 'get' => '', 'name__like' => '', 'description__like' => '', 'pad_counts' => false, 'offset' => '', 'search' => '', 'cache_domain' => 'core');
    $args = wp_parse_args($args, $defaults);
    $args['number'] = absint($args['number']);
    $args['offset'] = absint($args['offset']);
    if (!$single_taxonomy || !is_taxonomy_hierarchical(reset($taxonomies)) || '' !== $args['parent']) {
        $args['child_of'] = 0;
        $args['hierarchical'] = false;
        $args['pad_counts'] = false;
    }
    if ('all' == $args['get']) {
        $args['child_of'] = 0;
        $args['hide_empty'] = 0;
        $args['hierarchical'] = false;
        $args['pad_counts'] = false;
    }
    $args = apply_filters('get_terms_args', $args, $taxonomies);
    extract($args, EXTR_SKIP);
    if ($child_of) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$child_of])) {
            return $empty_array;
        }
    }
    if ($parent) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$parent])) {
            return $empty_array;
        }
    }
    // $args can be whatever, only use the args defined in defaults to compute the key
    $filter_key = has_filter('list_terms_exclusions') ? serialize($GLOBALS['wp_filter']['list_terms_exclusions']) : '';
    $key = md5(serialize(compact(array_keys($defaults))) . serialize($taxonomies) . $filter_key);
    $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) {
        $cache = apply_filters('get_terms', $cache, $taxonomies, $args);
        return $cache;
    }
    $_orderby = strtolower($orderby);
    if ('count' == $_orderby) {
        $orderby = 'tt.count';
    } else if ('name' == $_orderby) {
        $orderby = 't.name';
    } else if ('slug' == $_orderby) {
        $orderby = 't.slug';
    } else if ('term_group' == $_orderby) {
        $orderby = 't.term_group';
    } else if ('none' == $_orderby) {
        $orderby = '';
    } elseif (empty($_orderby) || 'id' == $_orderby) {
        $orderby = 't.term_id';
    } else {
        $orderby = 't.name';
    }
    $orderby = apply_filters('get_terms_orderby', $orderby, $args, $taxonomies);
    if (!empty($orderby)) {
        $orderby = "ORDER BY {$orderby}";
    } else {
        $order = '';
    }
    $order = strtoupper($order);
    if ('' !== $order && !in_array($order, array('ASC', 'DESC'))) {
        $order = 'ASC';
    }
    $where = "tt.taxonomy IN ('" . implode("', '", array_map('esc_sql', $taxonomies)) . "')";
    $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 = '';
    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 = implode(',', array_map('intval', $excluded_children));
    }
    if (!empty($exclude)) {
        $exterms = wp_parse_id_list($exclude);
        if (empty($exclusions)) {
            $exclusions = implode(',', $exterms);
        } else {
            $exclusions .= ', ' . implode(',', $exterms);
        }
    }
    if (!empty($exclusions)) {
        $exclusions = ' AND t.term_id NOT IN (' . $exclusions . ')';
    }
    $exclusions = apply_filters('list_terms_exclusions', $exclusions, $args, $taxonomies);
    if (!empty($exclusions)) {
        $where .= $exclusions;
    }
    if (!empty($slug)) {
        $slug = sanitize_title($slug);
        $where .= " AND t.slug = '{$slug}'";
    }
    if (!empty($name__like)) {
        $name__like = like_escape($name__like);
        $where .= $wpdb->prepare(" AND t.name LIKE %s", '%' . $name__like . '%');
    }
    if (!empty($description__like)) {
        $description__like = like_escape($description__like);
        $where .= $wpdb->prepare(" AND tt.description LIKE %s", '%' . $description__like . '%');
    }
    if ('' !== $parent) {
        $parent = (int) $parent;
        $where .= " AND tt.parent = '{$parent}'";
    }
    if ('count' == $fields) {
        $hierarchical = false;
    }
    if ($hide_empty && !$hierarchical) {
        $where .= ' AND tt.count > 0';
    }
    // 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($search)) {
        $search = like_escape($search);
        $where .= $wpdb->prepare(' AND ((t.name LIKE %s) OR (t.slug LIKE %s))', '%' . $search . '%', '%' . $search . '%');
    }
    $selects = array();
    switch ($fields) {
        case 'all':
            $selects = array('t.*', 'tt.*');
            break;
        case 'ids':
        case 'id=>parent':
            $selects = array('t.term_id', 'tt.parent', 'tt.count');
            break;
        case 'names':
            $selects = array('t.term_id', 'tt.parent', 'tt.count', 't.name');
            break;
        case 'count':
            $orderby = '';
            $order = '';
            $selects = array('COUNT(*)');
            break;
        case 'id=>name':
            $selects = array('t.term_id', 't.name');
            break;
        case 'id=>slug':
            $selects = array('t.term_id', 't.slug');
            break;
    }
    $_fields = $fields;
    $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', 'orderby', 'order', 'limits');
    $clauses = apply_filters('terms_clauses', compact($pieces), $taxonomies, $args);
    foreach ($pieces as $piece) {
        ${$piece} = isset($clauses[$piece]) ? $clauses[$piece] : '';
    }
    $query = "SELECT {$fields} FROM {$wpdb->terms} AS t {$join} WHERE {$where} {$orderby} {$order} {$limits}";
    $fields = $_fields;
    if ('count' == $fields) {
        $term_count = $wpdb->get_var($query);
        return $term_count;
    }
    $terms = $wpdb->get_results($query);
    if ('all' == $fields) {
        update_term_cache($terms);
    }
    if (empty($terms)) {
        wp_cache_add($cache_key, array(), 'terms', DAY_IN_SECONDS);
        $terms = apply_filters('get_terms', array(), $taxonomies, $args);
        return $terms;
    }
    if ($child_of) {
        $children = _get_term_hierarchy(reset($taxonomies));
        if (!empty($children)) {
            $terms = _get_term_children($child_of, $terms, reset($taxonomies));
        }
    }
    // Update term counts to include children.
    if ($pad_counts && 'all' == $fields) {
        _pad_term_counts($terms, reset($taxonomies));
    }
    // Make sure we show empty categories that have children.
    if ($hierarchical && $hide_empty && is_array($terms)) {
        foreach ($terms as $k => $term) {
            if (!$term->count) {
                $children = _get_term_children($term->term_id, $terms, reset($taxonomies));
                if (is_array($children)) {
                    foreach ($children as $child) {
                        if ($child->count) {
                            continue 2;
                        }
                    }
                }
                // It really is empty
                unset($terms[$k]);
            }
        }
    }
    reset($terms);
    $_terms = array();
    if ('id=>parent' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->parent;
        }
    } elseif ('ids' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->term_id;
        }
    } elseif ('names' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->name;
        }
    } elseif ('id=>name' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->name;
        }
    } elseif ('id=>slug' == $fields) {
        while ($term = array_shift($terms)) {
            $_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);
    }
    wp_cache_add($cache_key, $terms, 'terms', DAY_IN_SECONDS);
    $terms = apply_filters('get_terms', $terms, $taxonomies, $args);
    return $terms;
}

WordPress Version: 8.3

/**
 * 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 '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 'list_terms_exclusions' filter passes the compiled exclusions along with
 * the $args.
 *
 * The 'get_terms_orderby' filter passes the ORDER BY clause for the query
 * along with the $args array.
 *
 * The 'get_terms_fields' filter passes the fields for the SELECT query
 * along with the $args array.
 *
 * The list of arguments that $args can contain, which will overwrite the defaults:
 *
 * orderby - Default is 'name'. Can be name, count, term_group, slug or nothing
 * (will use term_id), Passing a custom value other than these will cause it to
 * order based on the custom value.
 *
 * order - Default is ASC. Can use DESC.
 *
 * hide_empty - Default is true. Will not return empty terms, which means
 * terms whose count is 0 according to the given taxonomy.
 *
 * exclude - Default is an empty array. An array, comma- or space-delimited string
 * of term ids to exclude from the return array. If 'include' is non-empty,
 * 'exclude' is ignored.
 *
 * exclude_tree - Default is an empty array. An array, comma- or space-delimited
 * string of term ids to exclude from the return array, along with all of their
 * descendant terms according to the primary taxonomy. If 'include' is non-empty,
 * 'exclude_tree' is ignored.
 *
 * include - Default is an empty array. An array, comma- or space-delimited string
 * of term ids to include in the return array.
 *
 * number - The maximum number of terms to return. Default is to return them all.
 *
 * offset - The number by which to offset the terms query.
 *
 * fields - Default is 'all', which returns an array of term objects.
 * If 'fields' is 'ids' or 'names', returns an array of
 * integers or strings, respectively.
 *
 * slug - Returns terms whose "slug" matches this value. Default is empty string.
 *
 * hierarchical - Whether to include terms that have non-empty descendants
 * (even if 'hide_empty' is set to true).
 *
 * search - Returned terms' names will contain the value of 'search',
 * case-insensitive. Default is an empty string.
 *
 * name__like - Returned terms' names will contain the value of 'name__like',
 * case-insensitive. Default is empty string.
 *
 * description__like - Returned terms' descriptions will contain the value of
 *  'description__like', case-insensitive. Default is empty string.
 *
 * The argument 'pad_counts', if set to true will include the quantity of a term's
 * children in the quantity of each term's "count" object variable.
 *
 * The 'get' argument, if set to 'all' instead of its default empty string,
 * returns terms regardless of ancestry or whether the terms are empty.
 *
 * The 'child_of' argument, when used, should be set to the integer of a term ID. Its default
 * is 0. If set to a non-zero value, all returned terms will be descendants
 * of that term according to the given taxonomy. Hence 'child_of' is set to 0
 * if more than one taxonomy is passed in $taxonomies, because multiple taxonomies
 * make term ancestry ambiguous.
 *
 * The 'parent' argument, when used, should be set to the integer of a term ID. Its default is
 * the empty string '', which has a different meaning from the integer 0.
 * If set to an integer value, all returned terms will have as an immediate
 * ancestor the term whose ID is specified by that integer according to the given taxonomy.
 * The 'parent' argument is different from 'child_of' in that a term X is considered a 'parent'
 * of term Y only if term X is the father of term Y, not its grandfather or great-grandfather, etc.
 *
 * The 'cache_domain' argument enables a unique cache key to be produced when this query is stored
 * in object cache. For instance, if you are using one of this function's filters to modify the
 * query (such as 'terms_clauses'), setting 'cache_domain' to a unique value will not overwrite
 * the cache for similar queries. Default value is 'core'.
 *
 * @package WordPress
 * @subpackage Taxonomy
 * @since 2.3.0
 *
 * @uses $wpdb
 * @uses wp_parse_args() Merges the defaults with those defined by $args and allows for strings.
 *
 * @param string|array $taxonomies Taxonomy name or list of Taxonomy names
 * @param string|array $args The values of what to search for when returning terms
 * @return array|WP_Error List of Term Objects 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)) {
            $error = new WP_Error('invalid_taxonomy', __('Invalid taxonomy'));
            return $error;
        }
    }
    $defaults = array('orderby' => 'name', 'order' => 'ASC', 'hide_empty' => true, 'exclude' => array(), 'exclude_tree' => array(), 'include' => array(), 'number' => '', 'fields' => 'all', 'slug' => '', 'parent' => '', 'hierarchical' => true, 'child_of' => 0, 'get' => '', 'name__like' => '', 'description__like' => '', 'pad_counts' => false, 'offset' => '', 'search' => '', 'cache_domain' => 'core');
    $args = wp_parse_args($args, $defaults);
    $args['number'] = absint($args['number']);
    $args['offset'] = absint($args['offset']);
    if (!$single_taxonomy || !is_taxonomy_hierarchical(reset($taxonomies)) || '' !== $args['parent']) {
        $args['child_of'] = 0;
        $args['hierarchical'] = false;
        $args['pad_counts'] = false;
    }
    if ('all' == $args['get']) {
        $args['child_of'] = 0;
        $args['hide_empty'] = 0;
        $args['hierarchical'] = false;
        $args['pad_counts'] = false;
    }
    $args = apply_filters('get_terms_args', $args, $taxonomies);
    extract($args, EXTR_SKIP);
    if ($child_of) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$child_of])) {
            return $empty_array;
        }
    }
    if ($parent) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$parent])) {
            return $empty_array;
        }
    }
    // $args can be whatever, only use the args defined in defaults to compute the key
    $filter_key = has_filter('list_terms_exclusions') ? serialize($GLOBALS['wp_filter']['list_terms_exclusions']) : '';
    $key = md5(serialize(compact(array_keys($defaults))) . serialize($taxonomies) . $filter_key);
    $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) {
        $cache = apply_filters('get_terms', $cache, $taxonomies, $args);
        return $cache;
    }
    $_orderby = strtolower($orderby);
    if ('count' == $_orderby) {
        $orderby = 'tt.count';
    } else if ('name' == $_orderby) {
        $orderby = 't.name';
    } else if ('slug' == $_orderby) {
        $orderby = 't.slug';
    } else if ('term_group' == $_orderby) {
        $orderby = 't.term_group';
    } else if ('none' == $_orderby) {
        $orderby = '';
    } elseif (empty($_orderby) || 'id' == $_orderby) {
        $orderby = 't.term_id';
    } else {
        $orderby = 't.name';
    }
    $orderby = apply_filters('get_terms_orderby', $orderby, $args, $taxonomies);
    if (!empty($orderby)) {
        $orderby = "ORDER BY {$orderby}";
    } else {
        $order = '';
    }
    $order = strtoupper($order);
    if ('' !== $order && !in_array($order, array('ASC', 'DESC'))) {
        $order = 'ASC';
    }
    $where = "tt.taxonomy IN ('" . implode("', '", $taxonomies) . "')";
    $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 = '';
    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 = implode(',', array_map('intval', $excluded_children));
    }
    if (!empty($exclude)) {
        $exterms = wp_parse_id_list($exclude);
        if (empty($exclusions)) {
            $exclusions = implode(',', $exterms);
        } else {
            $exclusions .= ', ' . implode(',', $exterms);
        }
    }
    if (!empty($exclusions)) {
        $exclusions = ' AND t.term_id NOT IN (' . $exclusions . ')';
    }
    $exclusions = apply_filters('list_terms_exclusions', $exclusions, $args, $taxonomies);
    if (!empty($exclusions)) {
        $where .= $exclusions;
    }
    if (!empty($slug)) {
        $slug = sanitize_title($slug);
        $where .= " AND t.slug = '{$slug}'";
    }
    if (!empty($name__like)) {
        $name__like = like_escape($name__like);
        $where .= $wpdb->prepare(" AND t.name LIKE %s", '%' . $name__like . '%');
    }
    if (!empty($description__like)) {
        $description__like = like_escape($description__like);
        $where .= $wpdb->prepare(" AND tt.description LIKE %s", '%' . $description__like . '%');
    }
    if ('' !== $parent) {
        $parent = (int) $parent;
        $where .= " AND tt.parent = '{$parent}'";
    }
    if ('count' == $fields) {
        $hierarchical = false;
    }
    if ($hide_empty && !$hierarchical) {
        $where .= ' AND tt.count > 0';
    }
    // 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($search)) {
        $search = like_escape($search);
        $where .= $wpdb->prepare(' AND ((t.name LIKE %s) OR (t.slug LIKE %s))', '%' . $search . '%', '%' . $search . '%');
    }
    $selects = array();
    switch ($fields) {
        case 'all':
            $selects = array('t.*', 'tt.*');
            break;
        case 'ids':
        case 'id=>parent':
            $selects = array('t.term_id', 'tt.parent', 'tt.count');
            break;
        case 'names':
            $selects = array('t.term_id', 'tt.parent', 'tt.count', 't.name');
            break;
        case 'count':
            $orderby = '';
            $order = '';
            $selects = array('COUNT(*)');
            break;
        case 'id=>name':
            $selects = array('t.term_id', 't.name');
            break;
        case 'id=>slug':
            $selects = array('t.term_id', 't.slug');
            break;
    }
    $_fields = $fields;
    $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', 'orderby', 'order', 'limits');
    $clauses = apply_filters('terms_clauses', compact($pieces), $taxonomies, $args);
    foreach ($pieces as $piece) {
        ${$piece} = isset($clauses[$piece]) ? $clauses[$piece] : '';
    }
    $query = "SELECT {$fields} FROM {$wpdb->terms} AS t {$join} WHERE {$where} {$orderby} {$order} {$limits}";
    $fields = $_fields;
    if ('count' == $fields) {
        $term_count = $wpdb->get_var($query);
        return $term_count;
    }
    $terms = $wpdb->get_results($query);
    if ('all' == $fields) {
        update_term_cache($terms);
    }
    if (empty($terms)) {
        wp_cache_add($cache_key, array(), 'terms', DAY_IN_SECONDS);
        $terms = apply_filters('get_terms', array(), $taxonomies, $args);
        return $terms;
    }
    if ($child_of) {
        $children = _get_term_hierarchy(reset($taxonomies));
        if (!empty($children)) {
            $terms = _get_term_children($child_of, $terms, reset($taxonomies));
        }
    }
    // Update term counts to include children.
    if ($pad_counts && 'all' == $fields) {
        _pad_term_counts($terms, reset($taxonomies));
    }
    // Make sure we show empty categories that have children.
    if ($hierarchical && $hide_empty && is_array($terms)) {
        foreach ($terms as $k => $term) {
            if (!$term->count) {
                $children = _get_term_children($term->term_id, $terms, reset($taxonomies));
                if (is_array($children)) {
                    foreach ($children as $child) {
                        if ($child->count) {
                            continue 2;
                        }
                    }
                }
                // It really is empty
                unset($terms[$k]);
            }
        }
    }
    reset($terms);
    $_terms = array();
    if ('id=>parent' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->parent;
        }
    } elseif ('ids' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->term_id;
        }
    } elseif ('names' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->name;
        }
    } elseif ('id=>name' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->name;
        }
    } elseif ('id=>slug' == $fields) {
        while ($term = array_shift($terms)) {
            $_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);
    }
    wp_cache_add($cache_key, $terms, 'terms', DAY_IN_SECONDS);
    $terms = apply_filters('get_terms', $terms, $taxonomies, $args);
    return $terms;
}

WordPress Version: .20

/**
 * 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 '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 'list_terms_exclusions' filter passes the compiled exclusions along with
 * the $args.
 *
 * The 'get_terms_orderby' filter passes the ORDER BY clause for the query
 * along with the $args array.
 *
 * The 'get_terms_fields' filter passes the fields for the SELECT query
 * along with the $args array.
 *
 * The list of arguments that $args can contain, which will overwrite the defaults:
 *
 * orderby - Default is 'name'. Can be name, count, term_group, slug or nothing
 * (will use term_id), Passing a custom value other than these will cause it to
 * order based on the custom value.
 *
 * order - Default is ASC. Can use DESC.
 *
 * hide_empty - Default is true. Will not return empty terms, which means
 * terms whose count is 0 according to the given taxonomy.
 *
 * exclude - Default is an empty array. An array, comma- or space-delimited string
 * of term ids to exclude from the return array. If 'include' is non-empty,
 * 'exclude' is ignored.
 *
 * exclude_tree - Default is an empty array. An array, comma- or space-delimited
 * string of term ids to exclude from the return array, along with all of their
 * descendant terms according to the primary taxonomy. If 'include' is non-empty,
 * 'exclude_tree' is ignored.
 *
 * include - Default is an empty array. An array, comma- or space-delimited string
 * of term ids to include in the return array.
 *
 * number - The maximum number of terms to return. Default is to return them all.
 *
 * offset - The number by which to offset the terms query.
 *
 * fields - Default is 'all', which returns an array of term objects.
 * If 'fields' is 'ids' or 'names', returns an array of
 * integers or strings, respectively.
 *
 * slug - Returns terms whose "slug" matches this value. Default is empty string.
 *
 * hierarchical - Whether to include terms that have non-empty descendants
 * (even if 'hide_empty' is set to true).
 *
 * search - Returned terms' names will contain the value of 'search',
 * case-insensitive. Default is an empty string.
 *
 * name__like - Returned terms' names will contain the value of 'name__like',
 * case-insensitive. Default is empty string.
 *
 * description__like - Returned terms' descriptions will contain the value of
 *  'description__like', case-insensitive. Default is empty string.
 *
 * The argument 'pad_counts', if set to true will include the quantity of a term's
 * children in the quantity of each term's "count" object variable.
 *
 * The 'get' argument, if set to 'all' instead of its default empty string,
 * returns terms regardless of ancestry or whether the terms are empty.
 *
 * The 'child_of' argument, when used, should be set to the integer of a term ID. Its default
 * is 0. If set to a non-zero value, all returned terms will be descendants
 * of that term according to the given taxonomy. Hence 'child_of' is set to 0
 * if more than one taxonomy is passed in $taxonomies, because multiple taxonomies
 * make term ancestry ambiguous.
 *
 * The 'parent' argument, when used, should be set to the integer of a term ID. Its default is
 * the empty string '', which has a different meaning from the integer 0.
 * If set to an integer value, all returned terms will have as an immediate
 * ancestor the term whose ID is specified by that integer according to the given taxonomy.
 * The 'parent' argument is different from 'child_of' in that a term X is considered a 'parent'
 * of term Y only if term X is the father of term Y, not its grandfather or great-grandfather, etc.
 *
 * The 'cache_domain' argument enables a unique cache key to be produced when this query is stored
 * in object cache. For instance, if you are using one of this function's filters to modify the
 * query (such as 'terms_clauses'), setting 'cache_domain' to a unique value will not overwrite
 * the cache for similar queries. Default value is 'core'.
 *
 * @package WordPress
 * @subpackage Taxonomy
 * @since 2.3.0
 *
 * @uses $wpdb
 * @uses wp_parse_args() Merges the defaults with those defined by $args and allows for strings.
 *
 * @param string|array $taxonomies Taxonomy name or list of Taxonomy names
 * @param string|array $args The values of what to search for when returning terms
 * @return array|WP_Error List of Term Objects 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)) {
            $error = new WP_Error('invalid_taxonomy', __('Invalid taxonomy'));
            return $error;
        }
    }
    $defaults = array('orderby' => 'name', 'order' => 'ASC', 'hide_empty' => true, 'exclude' => array(), 'exclude_tree' => array(), 'include' => array(), 'number' => '', 'fields' => 'all', 'slug' => '', 'parent' => '', 'hierarchical' => true, 'child_of' => 0, 'get' => '', 'name__like' => '', 'description__like' => '', 'pad_counts' => false, 'offset' => '', 'search' => '', 'cache_domain' => 'core');
    $args = wp_parse_args($args, $defaults);
    $args['number'] = absint($args['number']);
    $args['offset'] = absint($args['offset']);
    if (!$single_taxonomy || !is_taxonomy_hierarchical(reset($taxonomies)) || '' !== $args['parent']) {
        $args['child_of'] = 0;
        $args['hierarchical'] = false;
        $args['pad_counts'] = false;
    }
    if ('all' == $args['get']) {
        $args['child_of'] = 0;
        $args['hide_empty'] = 0;
        $args['hierarchical'] = false;
        $args['pad_counts'] = false;
    }
    $args = apply_filters('get_terms_args', $args, $taxonomies);
    extract($args, EXTR_SKIP);
    if ($child_of) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$child_of])) {
            return $empty_array;
        }
    }
    if ($parent) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$parent])) {
            return $empty_array;
        }
    }
    // $args can be whatever, only use the args defined in defaults to compute the key
    $filter_key = has_filter('list_terms_exclusions') ? serialize($GLOBALS['wp_filter']['list_terms_exclusions']) : '';
    $key = md5(serialize(compact(array_keys($defaults))) . serialize($taxonomies) . $filter_key);
    $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) {
        $cache = apply_filters('get_terms', $cache, $taxonomies, $args);
        return $cache;
    }
    $_orderby = strtolower($orderby);
    if ('count' == $_orderby) {
        $orderby = 'tt.count';
    } else if ('name' == $_orderby) {
        $orderby = 't.name';
    } else if ('slug' == $_orderby) {
        $orderby = 't.slug';
    } else if ('term_group' == $_orderby) {
        $orderby = 't.term_group';
    } else if ('none' == $_orderby) {
        $orderby = '';
    } elseif (empty($_orderby) || 'id' == $_orderby) {
        $orderby = 't.term_id';
    } else {
        $orderby = 't.name';
    }
    $orderby = apply_filters('get_terms_orderby', $orderby, $args, $taxonomies);
    if (!empty($orderby)) {
        $orderby = "ORDER BY {$orderby}";
    } else {
        $order = '';
    }
    $order = strtoupper($order);
    if ('' !== $order && !in_array($order, array('ASC', 'DESC'))) {
        $order = 'ASC';
    }
    $where = "tt.taxonomy IN ('" . implode("', '", array_map('esc_sql', $taxonomies)) . "')";
    $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 = '';
    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 = implode(',', array_map('intval', $excluded_children));
    }
    if (!empty($exclude)) {
        $exterms = wp_parse_id_list($exclude);
        if (empty($exclusions)) {
            $exclusions = implode(',', $exterms);
        } else {
            $exclusions .= ', ' . implode(',', $exterms);
        }
    }
    if (!empty($exclusions)) {
        $exclusions = ' AND t.term_id NOT IN (' . $exclusions . ')';
    }
    $exclusions = apply_filters('list_terms_exclusions', $exclusions, $args, $taxonomies);
    if (!empty($exclusions)) {
        $where .= $exclusions;
    }
    if (!empty($slug)) {
        $slug = sanitize_title($slug);
        $where .= " AND t.slug = '{$slug}'";
    }
    if (!empty($name__like)) {
        $name__like = like_escape($name__like);
        $where .= $wpdb->prepare(" AND t.name LIKE %s", '%' . $name__like . '%');
    }
    if (!empty($description__like)) {
        $description__like = like_escape($description__like);
        $where .= $wpdb->prepare(" AND tt.description LIKE %s", '%' . $description__like . '%');
    }
    if ('' !== $parent) {
        $parent = (int) $parent;
        $where .= " AND tt.parent = '{$parent}'";
    }
    if ('count' == $fields) {
        $hierarchical = false;
    }
    if ($hide_empty && !$hierarchical) {
        $where .= ' AND tt.count > 0';
    }
    // 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($search)) {
        $search = like_escape($search);
        $where .= $wpdb->prepare(' AND ((t.name LIKE %s) OR (t.slug LIKE %s))', '%' . $search . '%', '%' . $search . '%');
    }
    $selects = array();
    switch ($fields) {
        case 'all':
            $selects = array('t.*', 'tt.*');
            break;
        case 'ids':
        case 'id=>parent':
            $selects = array('t.term_id', 'tt.parent', 'tt.count');
            break;
        case 'names':
            $selects = array('t.term_id', 'tt.parent', 'tt.count', 't.name');
            break;
        case 'count':
            $orderby = '';
            $order = '';
            $selects = array('COUNT(*)');
            break;
        case 'id=>name':
            $selects = array('t.term_id', 't.name');
            break;
        case 'id=>slug':
            $selects = array('t.term_id', 't.slug');
            break;
    }
    $_fields = $fields;
    $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', 'orderby', 'order', 'limits');
    $clauses = apply_filters('terms_clauses', compact($pieces), $taxonomies, $args);
    foreach ($pieces as $piece) {
        ${$piece} = isset($clauses[$piece]) ? $clauses[$piece] : '';
    }
    $query = "SELECT {$fields} FROM {$wpdb->terms} AS t {$join} WHERE {$where} {$orderby} {$order} {$limits}";
    $fields = $_fields;
    if ('count' == $fields) {
        $term_count = $wpdb->get_var($query);
        return $term_count;
    }
    $terms = $wpdb->get_results($query);
    if ('all' == $fields) {
        update_term_cache($terms);
    }
    if (empty($terms)) {
        wp_cache_add($cache_key, array(), 'terms', DAY_IN_SECONDS);
        $terms = apply_filters('get_terms', array(), $taxonomies, $args);
        return $terms;
    }
    if ($child_of) {
        $children = _get_term_hierarchy(reset($taxonomies));
        if (!empty($children)) {
            $terms = _get_term_children($child_of, $terms, reset($taxonomies));
        }
    }
    // Update term counts to include children.
    if ($pad_counts && 'all' == $fields) {
        _pad_term_counts($terms, reset($taxonomies));
    }
    // Make sure we show empty categories that have children.
    if ($hierarchical && $hide_empty && is_array($terms)) {
        foreach ($terms as $k => $term) {
            if (!$term->count) {
                $children = _get_term_children($term->term_id, $terms, reset($taxonomies));
                if (is_array($children)) {
                    foreach ($children as $child) {
                        if ($child->count) {
                            continue 2;
                        }
                    }
                }
                // It really is empty
                unset($terms[$k]);
            }
        }
    }
    reset($terms);
    $_terms = array();
    if ('id=>parent' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->parent;
        }
    } elseif ('ids' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->term_id;
        }
    } elseif ('names' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->name;
        }
    } elseif ('id=>name' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->name;
        }
    } elseif ('id=>slug' == $fields) {
        while ($term = array_shift($terms)) {
            $_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);
    }
    wp_cache_add($cache_key, $terms, 'terms', DAY_IN_SECONDS);
    $terms = apply_filters('get_terms', $terms, $taxonomies, $args);
    return $terms;
}

WordPress Version: 8.2

/**
 * 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 '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 'list_terms_exclusions' filter passes the compiled exclusions along with
 * the $args.
 *
 * The 'get_terms_orderby' filter passes the ORDER BY clause for the query
 * along with the $args array.
 *
 * The 'get_terms_fields' filter passes the fields for the SELECT query
 * along with the $args array.
 *
 * The list of arguments that $args can contain, which will overwrite the defaults:
 *
 * orderby - Default is 'name'. Can be name, count, term_group, slug or nothing
 * (will use term_id), Passing a custom value other than these will cause it to
 * order based on the custom value.
 *
 * order - Default is ASC. Can use DESC.
 *
 * hide_empty - Default is true. Will not return empty terms, which means
 * terms whose count is 0 according to the given taxonomy.
 *
 * exclude - Default is an empty array. An array, comma- or space-delimited string
 * of term ids to exclude from the return array. If 'include' is non-empty,
 * 'exclude' is ignored.
 *
 * exclude_tree - Default is an empty array. An array, comma- or space-delimited
 * string of term ids to exclude from the return array, along with all of their
 * descendant terms according to the primary taxonomy. If 'include' is non-empty,
 * 'exclude_tree' is ignored.
 *
 * include - Default is an empty array. An array, comma- or space-delimited string
 * of term ids to include in the return array.
 *
 * number - The maximum number of terms to return. Default is to return them all.
 *
 * offset - The number by which to offset the terms query.
 *
 * fields - Default is 'all', which returns an array of term objects.
 * If 'fields' is 'ids' or 'names', returns an array of
 * integers or strings, respectively.
 *
 * slug - Returns terms whose "slug" matches this value. Default is empty string.
 *
 * hierarchical - Whether to include terms that have non-empty descendants
 * (even if 'hide_empty' is set to true).
 *
 * search - Returned terms' names will contain the value of 'search',
 * case-insensitive. Default is an empty string.
 *
 * name__like - Returned terms' names will contain the value of 'name__like',
 * case-insensitive. Default is empty string.
 *
 * description__like - Returned terms' descriptions will contain the value of
 *  'description__like', case-insensitive. Default is empty string.
 *
 * The argument 'pad_counts', if set to true will include the quantity of a term's
 * children in the quantity of each term's "count" object variable.
 *
 * The 'get' argument, if set to 'all' instead of its default empty string,
 * returns terms regardless of ancestry or whether the terms are empty.
 *
 * The 'child_of' argument, when used, should be set to the integer of a term ID. Its default
 * is 0. If set to a non-zero value, all returned terms will be descendants
 * of that term according to the given taxonomy. Hence 'child_of' is set to 0
 * if more than one taxonomy is passed in $taxonomies, because multiple taxonomies
 * make term ancestry ambiguous.
 *
 * The 'parent' argument, when used, should be set to the integer of a term ID. Its default is
 * the empty string '', which has a different meaning from the integer 0.
 * If set to an integer value, all returned terms will have as an immediate
 * ancestor the term whose ID is specified by that integer according to the given taxonomy.
 * The 'parent' argument is different from 'child_of' in that a term X is considered a 'parent'
 * of term Y only if term X is the father of term Y, not its grandfather or great-grandfather, etc.
 *
 * The 'cache_domain' argument enables a unique cache key to be produced when this query is stored
 * in object cache. For instance, if you are using one of this function's filters to modify the
 * query (such as 'terms_clauses'), setting 'cache_domain' to a unique value will not overwrite
 * the cache for similar queries. Default value is 'core'.
 *
 * @package WordPress
 * @subpackage Taxonomy
 * @since 2.3.0
 *
 * @uses $wpdb
 * @uses wp_parse_args() Merges the defaults with those defined by $args and allows for strings.
 *
 * @param string|array $taxonomies Taxonomy name or list of Taxonomy names
 * @param string|array $args The values of what to search for when returning terms
 * @return array|WP_Error List of Term Objects 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)) {
            $error = new WP_Error('invalid_taxonomy', __('Invalid taxonomy'));
            return $error;
        }
    }
    $defaults = array('orderby' => 'name', 'order' => 'ASC', 'hide_empty' => true, 'exclude' => array(), 'exclude_tree' => array(), 'include' => array(), 'number' => '', 'fields' => 'all', 'slug' => '', 'parent' => '', 'hierarchical' => true, 'child_of' => 0, 'get' => '', 'name__like' => '', 'description__like' => '', 'pad_counts' => false, 'offset' => '', 'search' => '', 'cache_domain' => 'core');
    $args = wp_parse_args($args, $defaults);
    $args['number'] = absint($args['number']);
    $args['offset'] = absint($args['offset']);
    if (!$single_taxonomy || !is_taxonomy_hierarchical(reset($taxonomies)) || '' !== $args['parent']) {
        $args['child_of'] = 0;
        $args['hierarchical'] = false;
        $args['pad_counts'] = false;
    }
    if ('all' == $args['get']) {
        $args['child_of'] = 0;
        $args['hide_empty'] = 0;
        $args['hierarchical'] = false;
        $args['pad_counts'] = false;
    }
    $args = apply_filters('get_terms_args', $args, $taxonomies);
    extract($args, EXTR_SKIP);
    if ($child_of) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$child_of])) {
            return $empty_array;
        }
    }
    if ($parent) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$parent])) {
            return $empty_array;
        }
    }
    // $args can be whatever, only use the args defined in defaults to compute the key
    $filter_key = has_filter('list_terms_exclusions') ? serialize($GLOBALS['wp_filter']['list_terms_exclusions']) : '';
    $key = md5(serialize(compact(array_keys($defaults))) . serialize($taxonomies) . $filter_key);
    $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) {
        $cache = apply_filters('get_terms', $cache, $taxonomies, $args);
        return $cache;
    }
    $_orderby = strtolower($orderby);
    if ('count' == $_orderby) {
        $orderby = 'tt.count';
    } else if ('name' == $_orderby) {
        $orderby = 't.name';
    } else if ('slug' == $_orderby) {
        $orderby = 't.slug';
    } else if ('term_group' == $_orderby) {
        $orderby = 't.term_group';
    } else if ('none' == $_orderby) {
        $orderby = '';
    } elseif (empty($_orderby) || 'id' == $_orderby) {
        $orderby = 't.term_id';
    } else {
        $orderby = 't.name';
    }
    $orderby = apply_filters('get_terms_orderby', $orderby, $args, $taxonomies);
    if (!empty($orderby)) {
        $orderby = "ORDER BY {$orderby}";
    } else {
        $order = '';
    }
    $order = strtoupper($order);
    if ('' !== $order && !in_array($order, array('ASC', 'DESC'))) {
        $order = 'ASC';
    }
    $where = "tt.taxonomy IN ('" . implode("', '", $taxonomies) . "')";
    $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 = '';
    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 = implode(',', array_map('intval', $excluded_children));
    }
    if (!empty($exclude)) {
        $exterms = wp_parse_id_list($exclude);
        if (empty($exclusions)) {
            $exclusions = implode(',', $exterms);
        } else {
            $exclusions .= ', ' . implode(',', $exterms);
        }
    }
    if (!empty($exclusions)) {
        $exclusions = ' AND t.term_id NOT IN (' . $exclusions . ')';
    }
    $exclusions = apply_filters('list_terms_exclusions', $exclusions, $args, $taxonomies);
    if (!empty($exclusions)) {
        $where .= $exclusions;
    }
    if (!empty($slug)) {
        $slug = sanitize_title($slug);
        $where .= " AND t.slug = '{$slug}'";
    }
    if (!empty($name__like)) {
        $name__like = like_escape($name__like);
        $where .= $wpdb->prepare(" AND t.name LIKE %s", '%' . $name__like . '%');
    }
    if (!empty($description__like)) {
        $description__like = like_escape($description__like);
        $where .= $wpdb->prepare(" AND tt.description LIKE %s", '%' . $description__like . '%');
    }
    if ('' !== $parent) {
        $parent = (int) $parent;
        $where .= " AND tt.parent = '{$parent}'";
    }
    if ('count' == $fields) {
        $hierarchical = false;
    }
    if ($hide_empty && !$hierarchical) {
        $where .= ' AND tt.count > 0';
    }
    // 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($search)) {
        $search = like_escape($search);
        $where .= $wpdb->prepare(' AND ((t.name LIKE %s) OR (t.slug LIKE %s))', '%' . $search . '%', '%' . $search . '%');
    }
    $selects = array();
    switch ($fields) {
        case 'all':
            $selects = array('t.*', 'tt.*');
            break;
        case 'ids':
        case 'id=>parent':
            $selects = array('t.term_id', 'tt.parent', 'tt.count');
            break;
        case 'names':
            $selects = array('t.term_id', 'tt.parent', 'tt.count', 't.name');
            break;
        case 'count':
            $orderby = '';
            $order = '';
            $selects = array('COUNT(*)');
            break;
        case 'id=>name':
            $selects = array('t.term_id', 't.name');
            break;
        case 'id=>slug':
            $selects = array('t.term_id', 't.slug');
            break;
    }
    $_fields = $fields;
    $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', 'orderby', 'order', 'limits');
    $clauses = apply_filters('terms_clauses', compact($pieces), $taxonomies, $args);
    foreach ($pieces as $piece) {
        ${$piece} = isset($clauses[$piece]) ? $clauses[$piece] : '';
    }
    $query = "SELECT {$fields} FROM {$wpdb->terms} AS t {$join} WHERE {$where} {$orderby} {$order} {$limits}";
    $fields = $_fields;
    if ('count' == $fields) {
        $term_count = $wpdb->get_var($query);
        return $term_count;
    }
    $terms = $wpdb->get_results($query);
    if ('all' == $fields) {
        update_term_cache($terms);
    }
    if (empty($terms)) {
        wp_cache_add($cache_key, array(), 'terms', DAY_IN_SECONDS);
        $terms = apply_filters('get_terms', array(), $taxonomies, $args);
        return $terms;
    }
    if ($child_of) {
        $children = _get_term_hierarchy(reset($taxonomies));
        if (!empty($children)) {
            $terms = _get_term_children($child_of, $terms, reset($taxonomies));
        }
    }
    // Update term counts to include children.
    if ($pad_counts && 'all' == $fields) {
        _pad_term_counts($terms, reset($taxonomies));
    }
    // Make sure we show empty categories that have children.
    if ($hierarchical && $hide_empty && is_array($terms)) {
        foreach ($terms as $k => $term) {
            if (!$term->count) {
                $children = _get_term_children($term->term_id, $terms, reset($taxonomies));
                if (is_array($children)) {
                    foreach ($children as $child) {
                        if ($child->count) {
                            continue 2;
                        }
                    }
                }
                // It really is empty
                unset($terms[$k]);
            }
        }
    }
    reset($terms);
    $_terms = array();
    if ('id=>parent' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->parent;
        }
    } elseif ('ids' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->term_id;
        }
    } elseif ('names' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->name;
        }
    } elseif ('id=>name' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->name;
        }
    } elseif ('id=>slug' == $fields) {
        while ($term = array_shift($terms)) {
            $_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);
    }
    wp_cache_add($cache_key, $terms, 'terms', DAY_IN_SECONDS);
    $terms = apply_filters('get_terms', $terms, $taxonomies, $args);
    return $terms;
}

WordPress Version: .14

/**
 * 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 '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 'list_terms_exclusions' filter passes the compiled exclusions along with
 * the $args.
 *
 * The 'get_terms_orderby' filter passes the ORDER BY clause for the query
 * along with the $args array.
 *
 * The 'get_terms_fields' filter passes the fields for the SELECT query
 * along with the $args array.
 *
 * The list of arguments that $args can contain, which will overwrite the defaults:
 *
 * orderby - Default is 'name'. Can be name, count, term_group, slug or nothing
 * (will use term_id), Passing a custom value other than these will cause it to
 * order based on the custom value.
 *
 * order - Default is ASC. Can use DESC.
 *
 * hide_empty - Default is true. Will not return empty terms, which means
 * terms whose count is 0 according to the given taxonomy.
 *
 * exclude - Default is an empty array. An array, comma- or space-delimited string
 * of term ids to exclude from the return array. If 'include' is non-empty,
 * 'exclude' is ignored.
 *
 * exclude_tree - Default is an empty array. An array, comma- or space-delimited
 * string of term ids to exclude from the return array, along with all of their
 * descendant terms according to the primary taxonomy. If 'include' is non-empty,
 * 'exclude_tree' is ignored.
 *
 * include - Default is an empty array. An array, comma- or space-delimited string
 * of term ids to include in the return array.
 *
 * number - The maximum number of terms to return. Default is to return them all.
 *
 * offset - The number by which to offset the terms query.
 *
 * fields - Default is 'all', which returns an array of term objects.
 * If 'fields' is 'ids' or 'names', returns an array of
 * integers or strings, respectively.
 *
 * slug - Returns terms whose "slug" matches this value. Default is empty string.
 *
 * hierarchical - Whether to include terms that have non-empty descendants
 * (even if 'hide_empty' is set to true).
 *
 * search - Returned terms' names will contain the value of 'search',
 * case-insensitive. Default is an empty string.
 *
 * name__like - Returned terms' names will contain the value of 'name__like',
 * case-insensitive. Default is empty string.
 *
 * description__like - Returned terms' descriptions will contain the value of
 *  'description__like', case-insensitive. Default is empty string.
 *
 * The argument 'pad_counts', if set to true will include the quantity of a term's
 * children in the quantity of each term's "count" object variable.
 *
 * The 'get' argument, if set to 'all' instead of its default empty string,
 * returns terms regardless of ancestry or whether the terms are empty.
 *
 * The 'child_of' argument, when used, should be set to the integer of a term ID. Its default
 * is 0. If set to a non-zero value, all returned terms will be descendants
 * of that term according to the given taxonomy. Hence 'child_of' is set to 0
 * if more than one taxonomy is passed in $taxonomies, because multiple taxonomies
 * make term ancestry ambiguous.
 *
 * The 'parent' argument, when used, should be set to the integer of a term ID. Its default is
 * the empty string '', which has a different meaning from the integer 0.
 * If set to an integer value, all returned terms will have as an immediate
 * ancestor the term whose ID is specified by that integer according to the given taxonomy.
 * The 'parent' argument is different from 'child_of' in that a term X is considered a 'parent'
 * of term Y only if term X is the father of term Y, not its grandfather or great-grandfather, etc.
 *
 * The 'cache_domain' argument enables a unique cache key to be produced when this query is stored
 * in object cache. For instance, if you are using one of this function's filters to modify the
 * query (such as 'terms_clauses'), setting 'cache_domain' to a unique value will not overwrite
 * the cache for similar queries. Default value is 'core'.
 *
 * @package WordPress
 * @subpackage Taxonomy
 * @since 2.3.0
 *
 * @uses $wpdb
 * @uses wp_parse_args() Merges the defaults with those defined by $args and allows for strings.
 *
 * @param string|array $taxonomies Taxonomy name or list of Taxonomy names
 * @param string|array $args The values of what to search for when returning terms
 * @return array|WP_Error List of Term Objects 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)) {
            $error = new WP_Error('invalid_taxonomy', __('Invalid taxonomy'));
            return $error;
        }
    }
    $defaults = array('orderby' => 'name', 'order' => 'ASC', 'hide_empty' => true, 'exclude' => array(), 'exclude_tree' => array(), 'include' => array(), 'number' => '', 'fields' => 'all', 'slug' => '', 'parent' => '', 'hierarchical' => true, 'child_of' => 0, 'get' => '', 'name__like' => '', 'description__like' => '', 'pad_counts' => false, 'offset' => '', 'search' => '', 'cache_domain' => 'core');
    $args = wp_parse_args($args, $defaults);
    $args['number'] = absint($args['number']);
    $args['offset'] = absint($args['offset']);
    if (!$single_taxonomy || !is_taxonomy_hierarchical(reset($taxonomies)) || '' !== $args['parent']) {
        $args['child_of'] = 0;
        $args['hierarchical'] = false;
        $args['pad_counts'] = false;
    }
    if ('all' == $args['get']) {
        $args['child_of'] = 0;
        $args['hide_empty'] = 0;
        $args['hierarchical'] = false;
        $args['pad_counts'] = false;
    }
    $args = apply_filters('get_terms_args', $args, $taxonomies);
    extract($args, EXTR_SKIP);
    if ($child_of) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$child_of])) {
            return $empty_array;
        }
    }
    if ($parent) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$parent])) {
            return $empty_array;
        }
    }
    // $args can be whatever, only use the args defined in defaults to compute the key
    $filter_key = has_filter('list_terms_exclusions') ? serialize($GLOBALS['wp_filter']['list_terms_exclusions']) : '';
    $key = md5(serialize(compact(array_keys($defaults))) . serialize($taxonomies) . $filter_key);
    $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) {
        $cache = apply_filters('get_terms', $cache, $taxonomies, $args);
        return $cache;
    }
    $_orderby = strtolower($orderby);
    if ('count' == $_orderby) {
        $orderby = 'tt.count';
    } else if ('name' == $_orderby) {
        $orderby = 't.name';
    } else if ('slug' == $_orderby) {
        $orderby = 't.slug';
    } else if ('term_group' == $_orderby) {
        $orderby = 't.term_group';
    } else if ('none' == $_orderby) {
        $orderby = '';
    } elseif (empty($_orderby) || 'id' == $_orderby) {
        $orderby = 't.term_id';
    } else {
        $orderby = 't.name';
    }
    $orderby = apply_filters('get_terms_orderby', $orderby, $args, $taxonomies);
    if (!empty($orderby)) {
        $orderby = "ORDER BY {$orderby}";
    } else {
        $order = '';
    }
    $order = strtoupper($order);
    if ('' !== $order && !in_array($order, array('ASC', 'DESC'))) {
        $order = 'ASC';
    }
    $where = "tt.taxonomy IN ('" . implode("', '", array_map('esc_sql', $taxonomies)) . "')";
    $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 = '';
    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 = implode(',', array_map('intval', $excluded_children));
    }
    if (!empty($exclude)) {
        $exterms = wp_parse_id_list($exclude);
        if (empty($exclusions)) {
            $exclusions = implode(',', $exterms);
        } else {
            $exclusions .= ', ' . implode(',', $exterms);
        }
    }
    if (!empty($exclusions)) {
        $exclusions = ' AND t.term_id NOT IN (' . $exclusions . ')';
    }
    $exclusions = apply_filters('list_terms_exclusions', $exclusions, $args, $taxonomies);
    if (!empty($exclusions)) {
        $where .= $exclusions;
    }
    if (!empty($slug)) {
        $slug = sanitize_title($slug);
        $where .= " AND t.slug = '{$slug}'";
    }
    if (!empty($name__like)) {
        $name__like = like_escape($name__like);
        $where .= $wpdb->prepare(" AND t.name LIKE %s", '%' . $name__like . '%');
    }
    if (!empty($description__like)) {
        $description__like = like_escape($description__like);
        $where .= $wpdb->prepare(" AND tt.description LIKE %s", '%' . $description__like . '%');
    }
    if ('' !== $parent) {
        $parent = (int) $parent;
        $where .= " AND tt.parent = '{$parent}'";
    }
    if ('count' == $fields) {
        $hierarchical = false;
    }
    if ($hide_empty && !$hierarchical) {
        $where .= ' AND tt.count > 0';
    }
    // 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($search)) {
        $search = like_escape($search);
        $where .= $wpdb->prepare(' AND ((t.name LIKE %s) OR (t.slug LIKE %s))', '%' . $search . '%', '%' . $search . '%');
    }
    $selects = array();
    switch ($fields) {
        case 'all':
            $selects = array('t.*', 'tt.*');
            break;
        case 'ids':
        case 'id=>parent':
            $selects = array('t.term_id', 'tt.parent', 'tt.count');
            break;
        case 'names':
            $selects = array('t.term_id', 'tt.parent', 'tt.count', 't.name');
            break;
        case 'count':
            $orderby = '';
            $order = '';
            $selects = array('COUNT(*)');
            break;
        case 'id=>name':
            $selects = array('t.term_id', 't.name');
            break;
        case 'id=>slug':
            $selects = array('t.term_id', 't.slug');
            break;
    }
    $_fields = $fields;
    $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', 'orderby', 'order', 'limits');
    $clauses = apply_filters('terms_clauses', compact($pieces), $taxonomies, $args);
    foreach ($pieces as $piece) {
        ${$piece} = isset($clauses[$piece]) ? $clauses[$piece] : '';
    }
    $query = "SELECT {$fields} FROM {$wpdb->terms} AS t {$join} WHERE {$where} {$orderby} {$order} {$limits}";
    $fields = $_fields;
    if ('count' == $fields) {
        $term_count = $wpdb->get_var($query);
        return $term_count;
    }
    $terms = $wpdb->get_results($query);
    if ('all' == $fields) {
        update_term_cache($terms);
    }
    if (empty($terms)) {
        wp_cache_add($cache_key, array(), 'terms', DAY_IN_SECONDS);
        $terms = apply_filters('get_terms', array(), $taxonomies, $args);
        return $terms;
    }
    if ($child_of) {
        $children = _get_term_hierarchy(reset($taxonomies));
        if (!empty($children)) {
            $terms = _get_term_children($child_of, $terms, reset($taxonomies));
        }
    }
    // Update term counts to include children.
    if ($pad_counts && 'all' == $fields) {
        _pad_term_counts($terms, reset($taxonomies));
    }
    // Make sure we show empty categories that have children.
    if ($hierarchical && $hide_empty && is_array($terms)) {
        foreach ($terms as $k => $term) {
            if (!$term->count) {
                $children = _get_term_children($term->term_id, $terms, reset($taxonomies));
                if (is_array($children)) {
                    foreach ($children as $child) {
                        if ($child->count) {
                            continue 2;
                        }
                    }
                }
                // It really is empty
                unset($terms[$k]);
            }
        }
    }
    reset($terms);
    $_terms = array();
    if ('id=>parent' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->parent;
        }
    } elseif ('ids' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->term_id;
        }
    } elseif ('names' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->name;
        }
    } elseif ('id=>name' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->name;
        }
    } elseif ('id=>slug' == $fields) {
        while ($term = array_shift($terms)) {
            $_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);
    }
    wp_cache_add($cache_key, $terms, 'terms', DAY_IN_SECONDS);
    $terms = apply_filters('get_terms', $terms, $taxonomies, $args);
    return $terms;
}

WordPress Version: 7.5

/**
 * 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 '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 'list_terms_exclusions' filter passes the compiled exclusions along with
 * the $args.
 *
 * The 'get_terms_orderby' filter passes the ORDER BY clause for the query
 * along with the $args array.
 *
 * The 'get_terms_fields' filter passes the fields for the SELECT query
 * along with the $args array.
 *
 * The list of arguments that $args can contain, which will overwrite the defaults:
 *
 * orderby - Default is 'name'. Can be name, count, term_group, slug or nothing
 * (will use term_id), Passing a custom value other than these will cause it to
 * order based on the custom value.
 *
 * order - Default is ASC. Can use DESC.
 *
 * hide_empty - Default is true. Will not return empty terms, which means
 * terms whose count is 0 according to the given taxonomy.
 *
 * exclude - Default is an empty array. An array, comma- or space-delimited string
 * of term ids to exclude from the return array. If 'include' is non-empty,
 * 'exclude' is ignored.
 *
 * exclude_tree - Default is an empty array. An array, comma- or space-delimited
 * string of term ids to exclude from the return array, along with all of their
 * descendant terms according to the primary taxonomy. If 'include' is non-empty,
 * 'exclude_tree' is ignored.
 *
 * include - Default is an empty array. An array, comma- or space-delimited string
 * of term ids to include in the return array.
 *
 * number - The maximum number of terms to return. Default is to return them all.
 *
 * offset - The number by which to offset the terms query.
 *
 * fields - Default is 'all', which returns an array of term objects.
 * If 'fields' is 'ids' or 'names', returns an array of
 * integers or strings, respectively.
 *
 * slug - Returns terms whose "slug" matches this value. Default is empty string.
 *
 * hierarchical - Whether to include terms that have non-empty descendants
 * (even if 'hide_empty' is set to true).
 *
 * search - Returned terms' names will contain the value of 'search',
 * case-insensitive. Default is an empty string.
 *
 * name__like - Returned terms' names will contain the value of 'name__like',
 * case-insensitive. Default is empty string.
 *
 * description__like - Returned terms' descriptions will contain the value of
 *  'description__like', case-insensitive. Default is empty string.
 *
 * The argument 'pad_counts', if set to true will include the quantity of a term's
 * children in the quantity of each term's "count" object variable.
 *
 * The 'get' argument, if set to 'all' instead of its default empty string,
 * returns terms regardless of ancestry or whether the terms are empty.
 *
 * The 'child_of' argument, when used, should be set to the integer of a term ID. Its default
 * is 0. If set to a non-zero value, all returned terms will be descendants
 * of that term according to the given taxonomy. Hence 'child_of' is set to 0
 * if more than one taxonomy is passed in $taxonomies, because multiple taxonomies
 * make term ancestry ambiguous.
 *
 * The 'parent' argument, when used, should be set to the integer of a term ID. Its default is
 * the empty string '', which has a different meaning from the integer 0.
 * If set to an integer value, all returned terms will have as an immediate
 * ancestor the term whose ID is specified by that integer according to the given taxonomy.
 * The 'parent' argument is different from 'child_of' in that a term X is considered a 'parent'
 * of term Y only if term X is the father of term Y, not its grandfather or great-grandfather, etc.
 *
 * The 'cache_domain' argument enables a unique cache key to be produced when this query is stored
 * in object cache. For instance, if you are using one of this function's filters to modify the
 * query (such as 'terms_clauses'), setting 'cache_domain' to a unique value will not overwrite
 * the cache for similar queries. Default value is 'core'.
 *
 * @package WordPress
 * @subpackage Taxonomy
 * @since 2.3.0
 *
 * @uses $wpdb
 * @uses wp_parse_args() Merges the defaults with those defined by $args and allows for strings.
 *
 * @param string|array $taxonomies Taxonomy name or list of Taxonomy names
 * @param string|array $args The values of what to search for when returning terms
 * @return array|WP_Error List of Term Objects 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)) {
            $error = new WP_Error('invalid_taxonomy', __('Invalid taxonomy'));
            return $error;
        }
    }
    $defaults = array('orderby' => 'name', 'order' => 'ASC', 'hide_empty' => true, 'exclude' => array(), 'exclude_tree' => array(), 'include' => array(), 'number' => '', 'fields' => 'all', 'slug' => '', 'parent' => '', 'hierarchical' => true, 'child_of' => 0, 'get' => '', 'name__like' => '', 'description__like' => '', 'pad_counts' => false, 'offset' => '', 'search' => '', 'cache_domain' => 'core');
    $args = wp_parse_args($args, $defaults);
    $args['number'] = absint($args['number']);
    $args['offset'] = absint($args['offset']);
    if (!$single_taxonomy || !is_taxonomy_hierarchical(reset($taxonomies)) || '' !== $args['parent']) {
        $args['child_of'] = 0;
        $args['hierarchical'] = false;
        $args['pad_counts'] = false;
    }
    if ('all' == $args['get']) {
        $args['child_of'] = 0;
        $args['hide_empty'] = 0;
        $args['hierarchical'] = false;
        $args['pad_counts'] = false;
    }
    $args = apply_filters('get_terms_args', $args, $taxonomies);
    extract($args, EXTR_SKIP);
    if ($child_of) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$child_of])) {
            return $empty_array;
        }
    }
    if ($parent) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$parent])) {
            return $empty_array;
        }
    }
    // $args can be whatever, only use the args defined in defaults to compute the key
    $filter_key = has_filter('list_terms_exclusions') ? serialize($GLOBALS['wp_filter']['list_terms_exclusions']) : '';
    $key = md5(serialize(compact(array_keys($defaults))) . serialize($taxonomies) . $filter_key);
    $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) {
        $cache = apply_filters('get_terms', $cache, $taxonomies, $args);
        return $cache;
    }
    $_orderby = strtolower($orderby);
    if ('count' == $_orderby) {
        $orderby = 'tt.count';
    } else if ('name' == $_orderby) {
        $orderby = 't.name';
    } else if ('slug' == $_orderby) {
        $orderby = 't.slug';
    } else if ('term_group' == $_orderby) {
        $orderby = 't.term_group';
    } else if ('none' == $_orderby) {
        $orderby = '';
    } elseif (empty($_orderby) || 'id' == $_orderby) {
        $orderby = 't.term_id';
    } else {
        $orderby = 't.name';
    }
    $orderby = apply_filters('get_terms_orderby', $orderby, $args, $taxonomies);
    if (!empty($orderby)) {
        $orderby = "ORDER BY {$orderby}";
    } else {
        $order = '';
    }
    $order = strtoupper($order);
    if ('' !== $order && !in_array($order, array('ASC', 'DESC'))) {
        $order = 'ASC';
    }
    $where = "tt.taxonomy IN ('" . implode("', '", $taxonomies) . "')";
    $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 = '';
    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 = implode(',', array_map('intval', $excluded_children));
    }
    if (!empty($exclude)) {
        $exterms = wp_parse_id_list($exclude);
        if (empty($exclusions)) {
            $exclusions = implode(',', $exterms);
        } else {
            $exclusions .= ', ' . implode(',', $exterms);
        }
    }
    if (!empty($exclusions)) {
        $exclusions = ' AND t.term_id NOT IN (' . $exclusions . ')';
    }
    $exclusions = apply_filters('list_terms_exclusions', $exclusions, $args, $taxonomies);
    if (!empty($exclusions)) {
        $where .= $exclusions;
    }
    if (!empty($slug)) {
        $slug = sanitize_title($slug);
        $where .= " AND t.slug = '{$slug}'";
    }
    if (!empty($name__like)) {
        $name__like = like_escape($name__like);
        $where .= $wpdb->prepare(" AND t.name LIKE %s", '%' . $name__like . '%');
    }
    if (!empty($description__like)) {
        $description__like = like_escape($description__like);
        $where .= $wpdb->prepare(" AND tt.description LIKE %s", '%' . $description__like . '%');
    }
    if ('' !== $parent) {
        $parent = (int) $parent;
        $where .= " AND tt.parent = '{$parent}'";
    }
    if ('count' == $fields) {
        $hierarchical = false;
    }
    if ($hide_empty && !$hierarchical) {
        $where .= ' AND tt.count > 0';
    }
    // 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($search)) {
        $search = like_escape($search);
        $where .= $wpdb->prepare(' AND ((t.name LIKE %s) OR (t.slug LIKE %s))', '%' . $search . '%', '%' . $search . '%');
    }
    $selects = array();
    switch ($fields) {
        case 'all':
            $selects = array('t.*', 'tt.*');
            break;
        case 'ids':
        case 'id=>parent':
            $selects = array('t.term_id', 'tt.parent', 'tt.count');
            break;
        case 'names':
            $selects = array('t.term_id', 'tt.parent', 'tt.count', 't.name');
            break;
        case 'count':
            $orderby = '';
            $order = '';
            $selects = array('COUNT(*)');
            break;
        case 'id=>name':
            $selects = array('t.term_id', 't.name');
            break;
        case 'id=>slug':
            $selects = array('t.term_id', 't.slug');
            break;
    }
    $_fields = $fields;
    $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', 'orderby', 'order', 'limits');
    $clauses = apply_filters('terms_clauses', compact($pieces), $taxonomies, $args);
    foreach ($pieces as $piece) {
        ${$piece} = isset($clauses[$piece]) ? $clauses[$piece] : '';
    }
    $query = "SELECT {$fields} FROM {$wpdb->terms} AS t {$join} WHERE {$where} {$orderby} {$order} {$limits}";
    $fields = $_fields;
    if ('count' == $fields) {
        $term_count = $wpdb->get_var($query);
        return $term_count;
    }
    $terms = $wpdb->get_results($query);
    if ('all' == $fields) {
        update_term_cache($terms);
    }
    if (empty($terms)) {
        wp_cache_add($cache_key, array(), 'terms', DAY_IN_SECONDS);
        $terms = apply_filters('get_terms', array(), $taxonomies, $args);
        return $terms;
    }
    if ($child_of) {
        $children = _get_term_hierarchy(reset($taxonomies));
        if (!empty($children)) {
            $terms = _get_term_children($child_of, $terms, reset($taxonomies));
        }
    }
    // Update term counts to include children.
    if ($pad_counts && 'all' == $fields) {
        _pad_term_counts($terms, reset($taxonomies));
    }
    // Make sure we show empty categories that have children.
    if ($hierarchical && $hide_empty && is_array($terms)) {
        foreach ($terms as $k => $term) {
            if (!$term->count) {
                $children = _get_term_children($term->term_id, $terms, reset($taxonomies));
                if (is_array($children)) {
                    foreach ($children as $child) {
                        if ($child->count) {
                            continue 2;
                        }
                    }
                }
                // It really is empty
                unset($terms[$k]);
            }
        }
    }
    reset($terms);
    $_terms = array();
    if ('id=>parent' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->parent;
        }
    } elseif ('ids' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->term_id;
        }
    } elseif ('names' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->name;
        }
    } elseif ('id=>name' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->name;
        }
    } elseif ('id=>slug' == $fields) {
        while ($term = array_shift($terms)) {
            $_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);
    }
    wp_cache_add($cache_key, $terms, 'terms', DAY_IN_SECONDS);
    $terms = apply_filters('get_terms', $terms, $taxonomies, $args);
    return $terms;
}

WordPress Version: .40

/**
 * 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 '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 'list_terms_exclusions' filter passes the compiled exclusions along with
 * the $args.
 *
 * The 'get_terms_orderby' filter passes the ORDER BY clause for the query
 * along with the $args array.
 *
 * The 'get_terms_fields' filter passes the fields for the SELECT query
 * along with the $args array.
 *
 * The list of arguments that $args can contain, which will overwrite the defaults:
 *
 * orderby - Default is 'name'. Can be name, count, term_group, slug or nothing
 * (will use term_id), Passing a custom value other than these will cause it to
 * order based on the custom value.
 *
 * order - Default is ASC. Can use DESC.
 *
 * hide_empty - Default is true. Will not return empty terms, which means
 * terms whose count is 0 according to the given taxonomy.
 *
 * exclude - Default is an empty array. An array, comma- or space-delimited string
 * of term ids to exclude from the return array. If 'include' is non-empty,
 * 'exclude' is ignored.
 *
 * exclude_tree - Default is an empty array. An array, comma- or space-delimited
 * string of term ids to exclude from the return array, along with all of their
 * descendant terms according to the primary taxonomy. If 'include' is non-empty,
 * 'exclude_tree' is ignored.
 *
 * include - Default is an empty array. An array, comma- or space-delimited string
 * of term ids to include in the return array.
 *
 * number - The maximum number of terms to return. Default is to return them all.
 *
 * offset - The number by which to offset the terms query.
 *
 * fields - Default is 'all', which returns an array of term objects.
 * If 'fields' is 'ids' or 'names', returns an array of
 * integers or strings, respectively.
 *
 * slug - Returns terms whose "slug" matches this value. Default is empty string.
 *
 * hierarchical - Whether to include terms that have non-empty descendants
 * (even if 'hide_empty' is set to true).
 *
 * search - Returned terms' names will contain the value of 'search',
 * case-insensitive. Default is an empty string.
 *
 * name__like - Returned terms' names will contain the value of 'name__like',
 * case-insensitive. Default is empty string.
 *
 * description__like - Returned terms' descriptions will contain the value of
 *  'description__like', case-insensitive. Default is empty string.
 *
 * The argument 'pad_counts', if set to true will include the quantity of a term's
 * children in the quantity of each term's "count" object variable.
 *
 * The 'get' argument, if set to 'all' instead of its default empty string,
 * returns terms regardless of ancestry or whether the terms are empty.
 *
 * The 'child_of' argument, when used, should be set to the integer of a term ID. Its default
 * is 0. If set to a non-zero value, all returned terms will be descendants
 * of that term according to the given taxonomy. Hence 'child_of' is set to 0
 * if more than one taxonomy is passed in $taxonomies, because multiple taxonomies
 * make term ancestry ambiguous.
 *
 * The 'parent' argument, when used, should be set to the integer of a term ID. Its default is
 * the empty string '', which has a different meaning from the integer 0.
 * If set to an integer value, all returned terms will have as an immediate
 * ancestor the term whose ID is specified by that integer according to the given taxonomy.
 * The 'parent' argument is different from 'child_of' in that a term X is considered a 'parent'
 * of term Y only if term X is the father of term Y, not its grandfather or great-grandfather, etc.
 *
 * The 'cache_domain' argument enables a unique cache key to be produced when this query is stored
 * in object cache. For instance, if you are using one of this function's filters to modify the
 * query (such as 'terms_clauses'), setting 'cache_domain' to a unique value will not overwrite
 * the cache for similar queries. Default value is 'core'.
 *
 * @package WordPress
 * @subpackage Taxonomy
 * @since 2.3.0
 *
 * @uses $wpdb
 * @uses wp_parse_args() Merges the defaults with those defined by $args and allows for strings.
 *
 * @param string|array $taxonomies Taxonomy name or list of Taxonomy names
 * @param string|array $args The values of what to search for when returning terms
 * @return array|WP_Error List of Term Objects 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)) {
            $error = new WP_Error('invalid_taxonomy', __('Invalid taxonomy'));
            return $error;
        }
    }
    $defaults = array('orderby' => 'name', 'order' => 'ASC', 'hide_empty' => true, 'exclude' => array(), 'exclude_tree' => array(), 'include' => array(), 'number' => '', 'fields' => 'all', 'slug' => '', 'parent' => '', 'hierarchical' => true, 'child_of' => 0, 'get' => '', 'name__like' => '', 'description__like' => '', 'pad_counts' => false, 'offset' => '', 'search' => '', 'cache_domain' => 'core');
    $args = wp_parse_args($args, $defaults);
    $args['number'] = absint($args['number']);
    $args['offset'] = absint($args['offset']);
    if (!$single_taxonomy || !is_taxonomy_hierarchical(reset($taxonomies)) || '' !== $args['parent']) {
        $args['child_of'] = 0;
        $args['hierarchical'] = false;
        $args['pad_counts'] = false;
    }
    if ('all' == $args['get']) {
        $args['child_of'] = 0;
        $args['hide_empty'] = 0;
        $args['hierarchical'] = false;
        $args['pad_counts'] = false;
    }
    $args = apply_filters('get_terms_args', $args, $taxonomies);
    extract($args, EXTR_SKIP);
    if ($child_of) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$child_of])) {
            return $empty_array;
        }
    }
    if ($parent) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$parent])) {
            return $empty_array;
        }
    }
    // $args can be whatever, only use the args defined in defaults to compute the key
    $filter_key = has_filter('list_terms_exclusions') ? serialize($GLOBALS['wp_filter']['list_terms_exclusions']) : '';
    $key = md5(serialize(compact(array_keys($defaults))) . serialize($taxonomies) . $filter_key);
    $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) {
        $cache = apply_filters('get_terms', $cache, $taxonomies, $args);
        return $cache;
    }
    $_orderby = strtolower($orderby);
    if ('count' == $_orderby) {
        $orderby = 'tt.count';
    } else if ('name' == $_orderby) {
        $orderby = 't.name';
    } else if ('slug' == $_orderby) {
        $orderby = 't.slug';
    } else if ('term_group' == $_orderby) {
        $orderby = 't.term_group';
    } else if ('none' == $_orderby) {
        $orderby = '';
    } elseif (empty($_orderby) || 'id' == $_orderby) {
        $orderby = 't.term_id';
    } else {
        $orderby = 't.name';
    }
    $orderby = apply_filters('get_terms_orderby', $orderby, $args, $taxonomies);
    if (!empty($orderby)) {
        $orderby = "ORDER BY {$orderby}";
    } else {
        $order = '';
    }
    $order = strtoupper($order);
    if ('' !== $order && !in_array($order, array('ASC', 'DESC'))) {
        $order = 'ASC';
    }
    $where = "tt.taxonomy IN ('" . implode("', '", array_map('esc_sql', $taxonomies)) . "')";
    $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 = '';
    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 = implode(',', array_map('intval', $excluded_children));
    }
    if (!empty($exclude)) {
        $exterms = wp_parse_id_list($exclude);
        if (empty($exclusions)) {
            $exclusions = implode(',', $exterms);
        } else {
            $exclusions .= ', ' . implode(',', $exterms);
        }
    }
    if (!empty($exclusions)) {
        $exclusions = ' AND t.term_id NOT IN (' . $exclusions . ')';
    }
    $exclusions = apply_filters('list_terms_exclusions', $exclusions, $args, $taxonomies);
    if (!empty($exclusions)) {
        $where .= $exclusions;
    }
    if (!empty($slug)) {
        $slug = sanitize_title($slug);
        $where .= " AND t.slug = '{$slug}'";
    }
    if (!empty($name__like)) {
        $name__like = like_escape($name__like);
        $where .= $wpdb->prepare(" AND t.name LIKE %s", '%' . $name__like . '%');
    }
    if (!empty($description__like)) {
        $description__like = like_escape($description__like);
        $where .= $wpdb->prepare(" AND tt.description LIKE %s", '%' . $description__like . '%');
    }
    if ('' !== $parent) {
        $parent = (int) $parent;
        $where .= " AND tt.parent = '{$parent}'";
    }
    if ('count' == $fields) {
        $hierarchical = false;
    }
    if ($hide_empty && !$hierarchical) {
        $where .= ' AND tt.count > 0';
    }
    // 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($search)) {
        $search = like_escape($search);
        $where .= $wpdb->prepare(' AND ((t.name LIKE %s) OR (t.slug LIKE %s))', '%' . $search . '%', '%' . $search . '%');
    }
    $selects = array();
    switch ($fields) {
        case 'all':
            $selects = array('t.*', 'tt.*');
            break;
        case 'ids':
        case 'id=>parent':
            $selects = array('t.term_id', 'tt.parent', 'tt.count');
            break;
        case 'names':
            $selects = array('t.term_id', 'tt.parent', 'tt.count', 't.name');
            break;
        case 'count':
            $orderby = '';
            $order = '';
            $selects = array('COUNT(*)');
            break;
        case 'id=>name':
            $selects = array('t.term_id', 't.name');
            break;
        case 'id=>slug':
            $selects = array('t.term_id', 't.slug');
            break;
    }
    $_fields = $fields;
    $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', 'orderby', 'order', 'limits');
    $clauses = apply_filters('terms_clauses', compact($pieces), $taxonomies, $args);
    foreach ($pieces as $piece) {
        ${$piece} = isset($clauses[$piece]) ? $clauses[$piece] : '';
    }
    $query = "SELECT {$fields} FROM {$wpdb->terms} AS t {$join} WHERE {$where} {$orderby} {$order} {$limits}";
    $fields = $_fields;
    if ('count' == $fields) {
        $term_count = $wpdb->get_var($query);
        return $term_count;
    }
    $terms = $wpdb->get_results($query);
    if ('all' == $fields) {
        update_term_cache($terms);
    }
    if (empty($terms)) {
        wp_cache_add($cache_key, array(), 'terms', DAY_IN_SECONDS);
        $terms = apply_filters('get_terms', array(), $taxonomies, $args);
        return $terms;
    }
    if ($child_of) {
        $children = _get_term_hierarchy(reset($taxonomies));
        if (!empty($children)) {
            $terms = _get_term_children($child_of, $terms, reset($taxonomies));
        }
    }
    // Update term counts to include children.
    if ($pad_counts && 'all' == $fields) {
        _pad_term_counts($terms, reset($taxonomies));
    }
    // Make sure we show empty categories that have children.
    if ($hierarchical && $hide_empty && is_array($terms)) {
        foreach ($terms as $k => $term) {
            if (!$term->count) {
                $children = _get_term_children($term->term_id, $terms, reset($taxonomies));
                if (is_array($children)) {
                    foreach ($children as $child) {
                        if ($child->count) {
                            continue 2;
                        }
                    }
                }
                // It really is empty
                unset($terms[$k]);
            }
        }
    }
    reset($terms);
    $_terms = array();
    if ('id=>parent' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->parent;
        }
    } elseif ('ids' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->term_id;
        }
    } elseif ('names' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->name;
        }
    } elseif ('id=>name' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->name;
        }
    } elseif ('id=>slug' == $fields) {
        while ($term = array_shift($terms)) {
            $_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);
    }
    wp_cache_add($cache_key, $terms, 'terms', DAY_IN_SECONDS);
    $terms = apply_filters('get_terms', $terms, $taxonomies, $args);
    return $terms;
}

WordPress Version: 7.4

/**
 * 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 '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 'list_terms_exclusions' filter passes the compiled exclusions along with
 * the $args.
 *
 * The 'get_terms_orderby' filter passes the ORDER BY clause for the query
 * along with the $args array.
 *
 * The 'get_terms_fields' filter passes the fields for the SELECT query
 * along with the $args array.
 *
 * The list of arguments that $args can contain, which will overwrite the defaults:
 *
 * orderby - Default is 'name'. Can be name, count, term_group, slug or nothing
 * (will use term_id), Passing a custom value other than these will cause it to
 * order based on the custom value.
 *
 * order - Default is ASC. Can use DESC.
 *
 * hide_empty - Default is true. Will not return empty terms, which means
 * terms whose count is 0 according to the given taxonomy.
 *
 * exclude - Default is an empty array. An array, comma- or space-delimited string
 * of term ids to exclude from the return array. If 'include' is non-empty,
 * 'exclude' is ignored.
 *
 * exclude_tree - Default is an empty array. An array, comma- or space-delimited
 * string of term ids to exclude from the return array, along with all of their
 * descendant terms according to the primary taxonomy. If 'include' is non-empty,
 * 'exclude_tree' is ignored.
 *
 * include - Default is an empty array. An array, comma- or space-delimited string
 * of term ids to include in the return array.
 *
 * number - The maximum number of terms to return. Default is to return them all.
 *
 * offset - The number by which to offset the terms query.
 *
 * fields - Default is 'all', which returns an array of term objects.
 * If 'fields' is 'ids' or 'names', returns an array of
 * integers or strings, respectively.
 *
 * slug - Returns terms whose "slug" matches this value. Default is empty string.
 *
 * hierarchical - Whether to include terms that have non-empty descendants
 * (even if 'hide_empty' is set to true).
 *
 * search - Returned terms' names will contain the value of 'search',
 * case-insensitive. Default is an empty string.
 *
 * name__like - Returned terms' names will contain the value of 'name__like',
 * case-insensitive. Default is empty string.
 *
 * description__like - Returned terms' descriptions will contain the value of
 *  'description__like', case-insensitive. Default is empty string.
 *
 * The argument 'pad_counts', if set to true will include the quantity of a term's
 * children in the quantity of each term's "count" object variable.
 *
 * The 'get' argument, if set to 'all' instead of its default empty string,
 * returns terms regardless of ancestry or whether the terms are empty.
 *
 * The 'child_of' argument, when used, should be set to the integer of a term ID. Its default
 * is 0. If set to a non-zero value, all returned terms will be descendants
 * of that term according to the given taxonomy. Hence 'child_of' is set to 0
 * if more than one taxonomy is passed in $taxonomies, because multiple taxonomies
 * make term ancestry ambiguous.
 *
 * The 'parent' argument, when used, should be set to the integer of a term ID. Its default is
 * the empty string '', which has a different meaning from the integer 0.
 * If set to an integer value, all returned terms will have as an immediate
 * ancestor the term whose ID is specified by that integer according to the given taxonomy.
 * The 'parent' argument is different from 'child_of' in that a term X is considered a 'parent'
 * of term Y only if term X is the father of term Y, not its grandfather or great-grandfather, etc.
 *
 * The 'cache_domain' argument enables a unique cache key to be produced when this query is stored
 * in object cache. For instance, if you are using one of this function's filters to modify the
 * query (such as 'terms_clauses'), setting 'cache_domain' to a unique value will not overwrite
 * the cache for similar queries. Default value is 'core'.
 *
 * @package WordPress
 * @subpackage Taxonomy
 * @since 2.3.0
 *
 * @uses $wpdb
 * @uses wp_parse_args() Merges the defaults with those defined by $args and allows for strings.
 *
 * @param string|array $taxonomies Taxonomy name or list of Taxonomy names
 * @param string|array $args The values of what to search for when returning terms
 * @return array|WP_Error List of Term Objects 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)) {
            $error = new WP_Error('invalid_taxonomy', __('Invalid taxonomy'));
            return $error;
        }
    }
    $defaults = array('orderby' => 'name', 'order' => 'ASC', 'hide_empty' => true, 'exclude' => array(), 'exclude_tree' => array(), 'include' => array(), 'number' => '', 'fields' => 'all', 'slug' => '', 'parent' => '', 'hierarchical' => true, 'child_of' => 0, 'get' => '', 'name__like' => '', 'description__like' => '', 'pad_counts' => false, 'offset' => '', 'search' => '', 'cache_domain' => 'core');
    $args = wp_parse_args($args, $defaults);
    $args['number'] = absint($args['number']);
    $args['offset'] = absint($args['offset']);
    if (!$single_taxonomy || !is_taxonomy_hierarchical(reset($taxonomies)) || '' !== $args['parent']) {
        $args['child_of'] = 0;
        $args['hierarchical'] = false;
        $args['pad_counts'] = false;
    }
    if ('all' == $args['get']) {
        $args['child_of'] = 0;
        $args['hide_empty'] = 0;
        $args['hierarchical'] = false;
        $args['pad_counts'] = false;
    }
    $args = apply_filters('get_terms_args', $args, $taxonomies);
    extract($args, EXTR_SKIP);
    if ($child_of) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$child_of])) {
            return $empty_array;
        }
    }
    if ($parent) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$parent])) {
            return $empty_array;
        }
    }
    // $args can be whatever, only use the args defined in defaults to compute the key
    $filter_key = has_filter('list_terms_exclusions') ? serialize($GLOBALS['wp_filter']['list_terms_exclusions']) : '';
    $key = md5(serialize(compact(array_keys($defaults))) . serialize($taxonomies) . $filter_key);
    $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) {
        $cache = apply_filters('get_terms', $cache, $taxonomies, $args);
        return $cache;
    }
    $_orderby = strtolower($orderby);
    if ('count' == $_orderby) {
        $orderby = 'tt.count';
    } else if ('name' == $_orderby) {
        $orderby = 't.name';
    } else if ('slug' == $_orderby) {
        $orderby = 't.slug';
    } else if ('term_group' == $_orderby) {
        $orderby = 't.term_group';
    } else if ('none' == $_orderby) {
        $orderby = '';
    } elseif (empty($_orderby) || 'id' == $_orderby) {
        $orderby = 't.term_id';
    } else {
        $orderby = 't.name';
    }
    $orderby = apply_filters('get_terms_orderby', $orderby, $args, $taxonomies);
    if (!empty($orderby)) {
        $orderby = "ORDER BY {$orderby}";
    } else {
        $order = '';
    }
    $order = strtoupper($order);
    if ('' !== $order && !in_array($order, array('ASC', 'DESC'))) {
        $order = 'ASC';
    }
    $where = "tt.taxonomy IN ('" . implode("', '", $taxonomies) . "')";
    $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 = '';
    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 = implode(',', array_map('intval', $excluded_children));
    }
    if (!empty($exclude)) {
        $exterms = wp_parse_id_list($exclude);
        if (empty($exclusions)) {
            $exclusions = implode(',', $exterms);
        } else {
            $exclusions .= ', ' . implode(',', $exterms);
        }
    }
    if (!empty($exclusions)) {
        $exclusions = ' AND t.term_id NOT IN (' . $exclusions . ')';
    }
    $exclusions = apply_filters('list_terms_exclusions', $exclusions, $args, $taxonomies);
    if (!empty($exclusions)) {
        $where .= $exclusions;
    }
    if (!empty($slug)) {
        $slug = sanitize_title($slug);
        $where .= " AND t.slug = '{$slug}'";
    }
    if (!empty($name__like)) {
        $name__like = like_escape($name__like);
        $where .= $wpdb->prepare(" AND t.name LIKE %s", '%' . $name__like . '%');
    }
    if (!empty($description__like)) {
        $description__like = like_escape($description__like);
        $where .= $wpdb->prepare(" AND tt.description LIKE %s", '%' . $description__like . '%');
    }
    if ('' !== $parent) {
        $parent = (int) $parent;
        $where .= " AND tt.parent = '{$parent}'";
    }
    if ('count' == $fields) {
        $hierarchical = false;
    }
    if ($hide_empty && !$hierarchical) {
        $where .= ' AND tt.count > 0';
    }
    // 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($search)) {
        $search = like_escape($search);
        $where .= $wpdb->prepare(' AND ((t.name LIKE %s) OR (t.slug LIKE %s))', '%' . $search . '%', '%' . $search . '%');
    }
    $selects = array();
    switch ($fields) {
        case 'all':
            $selects = array('t.*', 'tt.*');
            break;
        case 'ids':
        case 'id=>parent':
            $selects = array('t.term_id', 'tt.parent', 'tt.count');
            break;
        case 'names':
            $selects = array('t.term_id', 'tt.parent', 'tt.count', 't.name');
            break;
        case 'count':
            $orderby = '';
            $order = '';
            $selects = array('COUNT(*)');
            break;
        case 'id=>name':
            $selects = array('t.term_id', 't.name');
            break;
        case 'id=>slug':
            $selects = array('t.term_id', 't.slug');
            break;
    }
    $_fields = $fields;
    $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', 'orderby', 'order', 'limits');
    $clauses = apply_filters('terms_clauses', compact($pieces), $taxonomies, $args);
    foreach ($pieces as $piece) {
        ${$piece} = isset($clauses[$piece]) ? $clauses[$piece] : '';
    }
    $query = "SELECT {$fields} FROM {$wpdb->terms} AS t {$join} WHERE {$where} {$orderby} {$order} {$limits}";
    $fields = $_fields;
    if ('count' == $fields) {
        $term_count = $wpdb->get_var($query);
        return $term_count;
    }
    $terms = $wpdb->get_results($query);
    if ('all' == $fields) {
        update_term_cache($terms);
    }
    if (empty($terms)) {
        wp_cache_add($cache_key, array(), 'terms', DAY_IN_SECONDS);
        $terms = apply_filters('get_terms', array(), $taxonomies, $args);
        return $terms;
    }
    if ($child_of) {
        $children = _get_term_hierarchy(reset($taxonomies));
        if (!empty($children)) {
            $terms = _get_term_children($child_of, $terms, reset($taxonomies));
        }
    }
    // Update term counts to include children.
    if ($pad_counts && 'all' == $fields) {
        _pad_term_counts($terms, reset($taxonomies));
    }
    // Make sure we show empty categories that have children.
    if ($hierarchical && $hide_empty && is_array($terms)) {
        foreach ($terms as $k => $term) {
            if (!$term->count) {
                $children = _get_term_children($term->term_id, $terms, reset($taxonomies));
                if (is_array($children)) {
                    foreach ($children as $child) {
                        if ($child->count) {
                            continue 2;
                        }
                    }
                }
                // It really is empty
                unset($terms[$k]);
            }
        }
    }
    reset($terms);
    $_terms = array();
    if ('id=>parent' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->parent;
        }
    } elseif ('ids' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->term_id;
        }
    } elseif ('names' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->name;
        }
    } elseif ('id=>name' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->name;
        }
    } elseif ('id=>slug' == $fields) {
        while ($term = array_shift($terms)) {
            $_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);
    }
    wp_cache_add($cache_key, $terms, 'terms', DAY_IN_SECONDS);
    $terms = apply_filters('get_terms', $terms, $taxonomies, $args);
    return $terms;
}

WordPress Version: .30

/**
 * 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 '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 'list_terms_exclusions' filter passes the compiled exclusions along with
 * the $args.
 *
 * The 'get_terms_orderby' filter passes the ORDER BY clause for the query
 * along with the $args array.
 *
 * The 'get_terms_fields' filter passes the fields for the SELECT query
 * along with the $args array.
 *
 * The list of arguments that $args can contain, which will overwrite the defaults:
 *
 * orderby - Default is 'name'. Can be name, count, term_group, slug or nothing
 * (will use term_id), Passing a custom value other than these will cause it to
 * order based on the custom value.
 *
 * order - Default is ASC. Can use DESC.
 *
 * hide_empty - Default is true. Will not return empty terms, which means
 * terms whose count is 0 according to the given taxonomy.
 *
 * exclude - Default is an empty array. An array, comma- or space-delimited string
 * of term ids to exclude from the return array. If 'include' is non-empty,
 * 'exclude' is ignored.
 *
 * exclude_tree - Default is an empty array. An array, comma- or space-delimited
 * string of term ids to exclude from the return array, along with all of their
 * descendant terms according to the primary taxonomy. If 'include' is non-empty,
 * 'exclude_tree' is ignored.
 *
 * include - Default is an empty array. An array, comma- or space-delimited string
 * of term ids to include in the return array.
 *
 * number - The maximum number of terms to return. Default is to return them all.
 *
 * offset - The number by which to offset the terms query.
 *
 * fields - Default is 'all', which returns an array of term objects.
 * If 'fields' is 'ids' or 'names', returns an array of
 * integers or strings, respectively.
 *
 * slug - Returns terms whose "slug" matches this value. Default is empty string.
 *
 * hierarchical - Whether to include terms that have non-empty descendants
 * (even if 'hide_empty' is set to true).
 *
 * search - Returned terms' names will contain the value of 'search',
 * case-insensitive. Default is an empty string.
 *
 * name__like - Returned terms' names will contain the value of 'name__like',
 * case-insensitive. Default is empty string.
 *
 * description__like - Returned terms' descriptions will contain the value of
 *  'description__like', case-insensitive. Default is empty string.
 *
 * The argument 'pad_counts', if set to true will include the quantity of a term's
 * children in the quantity of each term's "count" object variable.
 *
 * The 'get' argument, if set to 'all' instead of its default empty string,
 * returns terms regardless of ancestry or whether the terms are empty.
 *
 * The 'child_of' argument, when used, should be set to the integer of a term ID. Its default
 * is 0. If set to a non-zero value, all returned terms will be descendants
 * of that term according to the given taxonomy. Hence 'child_of' is set to 0
 * if more than one taxonomy is passed in $taxonomies, because multiple taxonomies
 * make term ancestry ambiguous.
 *
 * The 'parent' argument, when used, should be set to the integer of a term ID. Its default is
 * the empty string '', which has a different meaning from the integer 0.
 * If set to an integer value, all returned terms will have as an immediate
 * ancestor the term whose ID is specified by that integer according to the given taxonomy.
 * The 'parent' argument is different from 'child_of' in that a term X is considered a 'parent'
 * of term Y only if term X is the father of term Y, not its grandfather or great-grandfather, etc.
 *
 * The 'cache_domain' argument enables a unique cache key to be produced when this query is stored
 * in object cache. For instance, if you are using one of this function's filters to modify the
 * query (such as 'terms_clauses'), setting 'cache_domain' to a unique value will not overwrite
 * the cache for similar queries. Default value is 'core'.
 *
 * @package WordPress
 * @subpackage Taxonomy
 * @since 2.3.0
 *
 * @uses $wpdb
 * @uses wp_parse_args() Merges the defaults with those defined by $args and allows for strings.
 *
 * @param string|array $taxonomies Taxonomy name or list of Taxonomy names
 * @param string|array $args The values of what to search for when returning terms
 * @return array|WP_Error List of Term Objects 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)) {
            $error = new WP_Error('invalid_taxonomy', __('Invalid taxonomy'));
            return $error;
        }
    }
    $defaults = array('orderby' => 'name', 'order' => 'ASC', 'hide_empty' => true, 'exclude' => array(), 'exclude_tree' => array(), 'include' => array(), 'number' => '', 'fields' => 'all', 'slug' => '', 'parent' => '', 'hierarchical' => true, 'child_of' => 0, 'get' => '', 'name__like' => '', 'description__like' => '', 'pad_counts' => false, 'offset' => '', 'search' => '', 'cache_domain' => 'core');
    $args = wp_parse_args($args, $defaults);
    $args['number'] = absint($args['number']);
    $args['offset'] = absint($args['offset']);
    if (!$single_taxonomy || !is_taxonomy_hierarchical(reset($taxonomies)) || '' !== $args['parent']) {
        $args['child_of'] = 0;
        $args['hierarchical'] = false;
        $args['pad_counts'] = false;
    }
    if ('all' == $args['get']) {
        $args['child_of'] = 0;
        $args['hide_empty'] = 0;
        $args['hierarchical'] = false;
        $args['pad_counts'] = false;
    }
    $args = apply_filters('get_terms_args', $args, $taxonomies);
    extract($args, EXTR_SKIP);
    if ($child_of) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$child_of])) {
            return $empty_array;
        }
    }
    if ($parent) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$parent])) {
            return $empty_array;
        }
    }
    // $args can be whatever, only use the args defined in defaults to compute the key
    $filter_key = has_filter('list_terms_exclusions') ? serialize($GLOBALS['wp_filter']['list_terms_exclusions']) : '';
    $key = md5(serialize(compact(array_keys($defaults))) . serialize($taxonomies) . $filter_key);
    $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) {
        $cache = apply_filters('get_terms', $cache, $taxonomies, $args);
        return $cache;
    }
    $_orderby = strtolower($orderby);
    if ('count' == $_orderby) {
        $orderby = 'tt.count';
    } else if ('name' == $_orderby) {
        $orderby = 't.name';
    } else if ('slug' == $_orderby) {
        $orderby = 't.slug';
    } else if ('term_group' == $_orderby) {
        $orderby = 't.term_group';
    } else if ('none' == $_orderby) {
        $orderby = '';
    } elseif (empty($_orderby) || 'id' == $_orderby) {
        $orderby = 't.term_id';
    } else {
        $orderby = 't.name';
    }
    $orderby = apply_filters('get_terms_orderby', $orderby, $args, $taxonomies);
    if (!empty($orderby)) {
        $orderby = "ORDER BY {$orderby}";
    } else {
        $order = '';
    }
    $order = strtoupper($order);
    if ('' !== $order && !in_array($order, array('ASC', 'DESC'))) {
        $order = 'ASC';
    }
    $where = "tt.taxonomy IN ('" . implode("', '", array_map('esc_sql', $taxonomies)) . "')";
    $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 = '';
    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 = implode(',', array_map('intval', $excluded_children));
    }
    if (!empty($exclude)) {
        $exterms = wp_parse_id_list($exclude);
        if (empty($exclusions)) {
            $exclusions = implode(',', $exterms);
        } else {
            $exclusions .= ', ' . implode(',', $exterms);
        }
    }
    if (!empty($exclusions)) {
        $exclusions = ' AND t.term_id NOT IN (' . $exclusions . ')';
    }
    $exclusions = apply_filters('list_terms_exclusions', $exclusions, $args, $taxonomies);
    if (!empty($exclusions)) {
        $where .= $exclusions;
    }
    if (!empty($slug)) {
        $slug = sanitize_title($slug);
        $where .= " AND t.slug = '{$slug}'";
    }
    if (!empty($name__like)) {
        $name__like = like_escape($name__like);
        $where .= $wpdb->prepare(" AND t.name LIKE %s", '%' . $name__like . '%');
    }
    if (!empty($description__like)) {
        $description__like = like_escape($description__like);
        $where .= $wpdb->prepare(" AND tt.description LIKE %s", '%' . $description__like . '%');
    }
    if ('' !== $parent) {
        $parent = (int) $parent;
        $where .= " AND tt.parent = '{$parent}'";
    }
    if ('count' == $fields) {
        $hierarchical = false;
    }
    if ($hide_empty && !$hierarchical) {
        $where .= ' AND tt.count > 0';
    }
    // 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($search)) {
        $search = like_escape($search);
        $where .= $wpdb->prepare(' AND ((t.name LIKE %s) OR (t.slug LIKE %s))', '%' . $search . '%', '%' . $search . '%');
    }
    $selects = array();
    switch ($fields) {
        case 'all':
            $selects = array('t.*', 'tt.*');
            break;
        case 'ids':
        case 'id=>parent':
            $selects = array('t.term_id', 'tt.parent', 'tt.count');
            break;
        case 'names':
            $selects = array('t.term_id', 'tt.parent', 'tt.count', 't.name');
            break;
        case 'count':
            $orderby = '';
            $order = '';
            $selects = array('COUNT(*)');
            break;
        case 'id=>name':
            $selects = array('t.term_id', 't.name');
            break;
        case 'id=>slug':
            $selects = array('t.term_id', 't.slug');
            break;
    }
    $_fields = $fields;
    $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', 'orderby', 'order', 'limits');
    $clauses = apply_filters('terms_clauses', compact($pieces), $taxonomies, $args);
    foreach ($pieces as $piece) {
        ${$piece} = isset($clauses[$piece]) ? $clauses[$piece] : '';
    }
    $query = "SELECT {$fields} FROM {$wpdb->terms} AS t {$join} WHERE {$where} {$orderby} {$order} {$limits}";
    $fields = $_fields;
    if ('count' == $fields) {
        $term_count = $wpdb->get_var($query);
        return $term_count;
    }
    $terms = $wpdb->get_results($query);
    if ('all' == $fields) {
        update_term_cache($terms);
    }
    if (empty($terms)) {
        wp_cache_add($cache_key, array(), 'terms', DAY_IN_SECONDS);
        $terms = apply_filters('get_terms', array(), $taxonomies, $args);
        return $terms;
    }
    if ($child_of) {
        $children = _get_term_hierarchy(reset($taxonomies));
        if (!empty($children)) {
            $terms = _get_term_children($child_of, $terms, reset($taxonomies));
        }
    }
    // Update term counts to include children.
    if ($pad_counts && 'all' == $fields) {
        _pad_term_counts($terms, reset($taxonomies));
    }
    // Make sure we show empty categories that have children.
    if ($hierarchical && $hide_empty && is_array($terms)) {
        foreach ($terms as $k => $term) {
            if (!$term->count) {
                $children = _get_term_children($term->term_id, $terms, reset($taxonomies));
                if (is_array($children)) {
                    foreach ($children as $child) {
                        if ($child->count) {
                            continue 2;
                        }
                    }
                }
                // It really is empty
                unset($terms[$k]);
            }
        }
    }
    reset($terms);
    $_terms = array();
    if ('id=>parent' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->parent;
        }
    } elseif ('ids' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->term_id;
        }
    } elseif ('names' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->name;
        }
    } elseif ('id=>name' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->name;
        }
    } elseif ('id=>slug' == $fields) {
        while ($term = array_shift($terms)) {
            $_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);
    }
    wp_cache_add($cache_key, $terms, 'terms', DAY_IN_SECONDS);
    $terms = apply_filters('get_terms', $terms, $taxonomies, $args);
    return $terms;
}

WordPress Version: 7.3

/**
 * 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 '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 'list_terms_exclusions' filter passes the compiled exclusions along with
 * the $args.
 *
 * The 'get_terms_orderby' filter passes the ORDER BY clause for the query
 * along with the $args array.
 *
 * The 'get_terms_fields' filter passes the fields for the SELECT query
 * along with the $args array.
 *
 * The list of arguments that $args can contain, which will overwrite the defaults:
 *
 * orderby - Default is 'name'. Can be name, count, term_group, slug or nothing
 * (will use term_id), Passing a custom value other than these will cause it to
 * order based on the custom value.
 *
 * order - Default is ASC. Can use DESC.
 *
 * hide_empty - Default is true. Will not return empty terms, which means
 * terms whose count is 0 according to the given taxonomy.
 *
 * exclude - Default is an empty array. An array, comma- or space-delimited string
 * of term ids to exclude from the return array. If 'include' is non-empty,
 * 'exclude' is ignored.
 *
 * exclude_tree - Default is an empty array. An array, comma- or space-delimited
 * string of term ids to exclude from the return array, along with all of their
 * descendant terms according to the primary taxonomy. If 'include' is non-empty,
 * 'exclude_tree' is ignored.
 *
 * include - Default is an empty array. An array, comma- or space-delimited string
 * of term ids to include in the return array.
 *
 * number - The maximum number of terms to return. Default is to return them all.
 *
 * offset - The number by which to offset the terms query.
 *
 * fields - Default is 'all', which returns an array of term objects.
 * If 'fields' is 'ids' or 'names', returns an array of
 * integers or strings, respectively.
 *
 * slug - Returns terms whose "slug" matches this value. Default is empty string.
 *
 * hierarchical - Whether to include terms that have non-empty descendants
 * (even if 'hide_empty' is set to true).
 *
 * search - Returned terms' names will contain the value of 'search',
 * case-insensitive. Default is an empty string.
 *
 * name__like - Returned terms' names will contain the value of 'name__like',
 * case-insensitive. Default is empty string.
 *
 * description__like - Returned terms' descriptions will contain the value of
 *  'description__like', case-insensitive. Default is empty string.
 *
 * The argument 'pad_counts', if set to true will include the quantity of a term's
 * children in the quantity of each term's "count" object variable.
 *
 * The 'get' argument, if set to 'all' instead of its default empty string,
 * returns terms regardless of ancestry or whether the terms are empty.
 *
 * The 'child_of' argument, when used, should be set to the integer of a term ID. Its default
 * is 0. If set to a non-zero value, all returned terms will be descendants
 * of that term according to the given taxonomy. Hence 'child_of' is set to 0
 * if more than one taxonomy is passed in $taxonomies, because multiple taxonomies
 * make term ancestry ambiguous.
 *
 * The 'parent' argument, when used, should be set to the integer of a term ID. Its default is
 * the empty string '', which has a different meaning from the integer 0.
 * If set to an integer value, all returned terms will have as an immediate
 * ancestor the term whose ID is specified by that integer according to the given taxonomy.
 * The 'parent' argument is different from 'child_of' in that a term X is considered a 'parent'
 * of term Y only if term X is the father of term Y, not its grandfather or great-grandfather, etc.
 *
 * The 'cache_domain' argument enables a unique cache key to be produced when this query is stored
 * in object cache. For instance, if you are using one of this function's filters to modify the
 * query (such as 'terms_clauses'), setting 'cache_domain' to a unique value will not overwrite
 * the cache for similar queries. Default value is 'core'.
 *
 * @package WordPress
 * @subpackage Taxonomy
 * @since 2.3.0
 *
 * @uses $wpdb
 * @uses wp_parse_args() Merges the defaults with those defined by $args and allows for strings.
 *
 * @param string|array $taxonomies Taxonomy name or list of Taxonomy names
 * @param string|array $args The values of what to search for when returning terms
 * @return array|WP_Error List of Term Objects 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)) {
            $error = new WP_Error('invalid_taxonomy', __('Invalid taxonomy'));
            return $error;
        }
    }
    $defaults = array('orderby' => 'name', 'order' => 'ASC', 'hide_empty' => true, 'exclude' => array(), 'exclude_tree' => array(), 'include' => array(), 'number' => '', 'fields' => 'all', 'slug' => '', 'parent' => '', 'hierarchical' => true, 'child_of' => 0, 'get' => '', 'name__like' => '', 'description__like' => '', 'pad_counts' => false, 'offset' => '', 'search' => '', 'cache_domain' => 'core');
    $args = wp_parse_args($args, $defaults);
    $args['number'] = absint($args['number']);
    $args['offset'] = absint($args['offset']);
    if (!$single_taxonomy || !is_taxonomy_hierarchical(reset($taxonomies)) || '' !== $args['parent']) {
        $args['child_of'] = 0;
        $args['hierarchical'] = false;
        $args['pad_counts'] = false;
    }
    if ('all' == $args['get']) {
        $args['child_of'] = 0;
        $args['hide_empty'] = 0;
        $args['hierarchical'] = false;
        $args['pad_counts'] = false;
    }
    $args = apply_filters('get_terms_args', $args, $taxonomies);
    extract($args, EXTR_SKIP);
    if ($child_of) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$child_of])) {
            return $empty_array;
        }
    }
    if ($parent) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$parent])) {
            return $empty_array;
        }
    }
    // $args can be whatever, only use the args defined in defaults to compute the key
    $filter_key = has_filter('list_terms_exclusions') ? serialize($GLOBALS['wp_filter']['list_terms_exclusions']) : '';
    $key = md5(serialize(compact(array_keys($defaults))) . serialize($taxonomies) . $filter_key);
    $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) {
        $cache = apply_filters('get_terms', $cache, $taxonomies, $args);
        return $cache;
    }
    $_orderby = strtolower($orderby);
    if ('count' == $_orderby) {
        $orderby = 'tt.count';
    } else if ('name' == $_orderby) {
        $orderby = 't.name';
    } else if ('slug' == $_orderby) {
        $orderby = 't.slug';
    } else if ('term_group' == $_orderby) {
        $orderby = 't.term_group';
    } else if ('none' == $_orderby) {
        $orderby = '';
    } elseif (empty($_orderby) || 'id' == $_orderby) {
        $orderby = 't.term_id';
    } else {
        $orderby = 't.name';
    }
    $orderby = apply_filters('get_terms_orderby', $orderby, $args, $taxonomies);
    if (!empty($orderby)) {
        $orderby = "ORDER BY {$orderby}";
    } else {
        $order = '';
    }
    $order = strtoupper($order);
    if ('' !== $order && !in_array($order, array('ASC', 'DESC'))) {
        $order = 'ASC';
    }
    $where = "tt.taxonomy IN ('" . implode("', '", $taxonomies) . "')";
    $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 = '';
    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 = implode(',', array_map('intval', $excluded_children));
    }
    if (!empty($exclude)) {
        $exterms = wp_parse_id_list($exclude);
        if (empty($exclusions)) {
            $exclusions = implode(',', $exterms);
        } else {
            $exclusions .= ', ' . implode(',', $exterms);
        }
    }
    if (!empty($exclusions)) {
        $exclusions = ' AND t.term_id NOT IN (' . $exclusions . ')';
    }
    $exclusions = apply_filters('list_terms_exclusions', $exclusions, $args, $taxonomies);
    if (!empty($exclusions)) {
        $where .= $exclusions;
    }
    if (!empty($slug)) {
        $slug = sanitize_title($slug);
        $where .= " AND t.slug = '{$slug}'";
    }
    if (!empty($name__like)) {
        $name__like = like_escape($name__like);
        $where .= $wpdb->prepare(" AND t.name LIKE %s", '%' . $name__like . '%');
    }
    if (!empty($description__like)) {
        $description__like = like_escape($description__like);
        $where .= $wpdb->prepare(" AND tt.description LIKE %s", '%' . $description__like . '%');
    }
    if ('' !== $parent) {
        $parent = (int) $parent;
        $where .= " AND tt.parent = '{$parent}'";
    }
    if ('count' == $fields) {
        $hierarchical = false;
    }
    if ($hide_empty && !$hierarchical) {
        $where .= ' AND tt.count > 0';
    }
    // 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($search)) {
        $search = like_escape($search);
        $where .= $wpdb->prepare(' AND ((t.name LIKE %s) OR (t.slug LIKE %s))', '%' . $search . '%', '%' . $search . '%');
    }
    $selects = array();
    switch ($fields) {
        case 'all':
            $selects = array('t.*', 'tt.*');
            break;
        case 'ids':
        case 'id=>parent':
            $selects = array('t.term_id', 'tt.parent', 'tt.count');
            break;
        case 'names':
            $selects = array('t.term_id', 'tt.parent', 'tt.count', 't.name');
            break;
        case 'count':
            $orderby = '';
            $order = '';
            $selects = array('COUNT(*)');
            break;
        case 'id=>name':
            $selects = array('t.term_id', 't.name');
            break;
        case 'id=>slug':
            $selects = array('t.term_id', 't.slug');
            break;
    }
    $_fields = $fields;
    $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', 'orderby', 'order', 'limits');
    $clauses = apply_filters('terms_clauses', compact($pieces), $taxonomies, $args);
    foreach ($pieces as $piece) {
        ${$piece} = isset($clauses[$piece]) ? $clauses[$piece] : '';
    }
    $query = "SELECT {$fields} FROM {$wpdb->terms} AS t {$join} WHERE {$where} {$orderby} {$order} {$limits}";
    $fields = $_fields;
    if ('count' == $fields) {
        $term_count = $wpdb->get_var($query);
        return $term_count;
    }
    $terms = $wpdb->get_results($query);
    if ('all' == $fields) {
        update_term_cache($terms);
    }
    if (empty($terms)) {
        wp_cache_add($cache_key, array(), 'terms', DAY_IN_SECONDS);
        $terms = apply_filters('get_terms', array(), $taxonomies, $args);
        return $terms;
    }
    if ($child_of) {
        $children = _get_term_hierarchy(reset($taxonomies));
        if (!empty($children)) {
            $terms = _get_term_children($child_of, $terms, reset($taxonomies));
        }
    }
    // Update term counts to include children.
    if ($pad_counts && 'all' == $fields) {
        _pad_term_counts($terms, reset($taxonomies));
    }
    // Make sure we show empty categories that have children.
    if ($hierarchical && $hide_empty && is_array($terms)) {
        foreach ($terms as $k => $term) {
            if (!$term->count) {
                $children = _get_term_children($term->term_id, $terms, reset($taxonomies));
                if (is_array($children)) {
                    foreach ($children as $child) {
                        if ($child->count) {
                            continue 2;
                        }
                    }
                }
                // It really is empty
                unset($terms[$k]);
            }
        }
    }
    reset($terms);
    $_terms = array();
    if ('id=>parent' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->parent;
        }
    } elseif ('ids' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->term_id;
        }
    } elseif ('names' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->name;
        }
    } elseif ('id=>name' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->name;
        }
    } elseif ('id=>slug' == $fields) {
        while ($term = array_shift($terms)) {
            $_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);
    }
    wp_cache_add($cache_key, $terms, 'terms', DAY_IN_SECONDS);
    $terms = apply_filters('get_terms', $terms, $taxonomies, $args);
    return $terms;
}

WordPress Version: .20

/**
 * 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 '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 'list_terms_exclusions' filter passes the compiled exclusions along with
 * the $args.
 *
 * The 'get_terms_orderby' filter passes the ORDER BY clause for the query
 * along with the $args array.
 *
 * The 'get_terms_fields' filter passes the fields for the SELECT query
 * along with the $args array.
 *
 * The list of arguments that $args can contain, which will overwrite the defaults:
 *
 * orderby - Default is 'name'. Can be name, count, term_group, slug or nothing
 * (will use term_id), Passing a custom value other than these will cause it to
 * order based on the custom value.
 *
 * order - Default is ASC. Can use DESC.
 *
 * hide_empty - Default is true. Will not return empty terms, which means
 * terms whose count is 0 according to the given taxonomy.
 *
 * exclude - Default is an empty array. An array, comma- or space-delimited string
 * of term ids to exclude from the return array. If 'include' is non-empty,
 * 'exclude' is ignored.
 *
 * exclude_tree - Default is an empty array. An array, comma- or space-delimited
 * string of term ids to exclude from the return array, along with all of their
 * descendant terms according to the primary taxonomy. If 'include' is non-empty,
 * 'exclude_tree' is ignored.
 *
 * include - Default is an empty array. An array, comma- or space-delimited string
 * of term ids to include in the return array.
 *
 * number - The maximum number of terms to return. Default is to return them all.
 *
 * offset - The number by which to offset the terms query.
 *
 * fields - Default is 'all', which returns an array of term objects.
 * If 'fields' is 'ids' or 'names', returns an array of
 * integers or strings, respectively.
 *
 * slug - Returns terms whose "slug" matches this value. Default is empty string.
 *
 * hierarchical - Whether to include terms that have non-empty descendants
 * (even if 'hide_empty' is set to true).
 *
 * search - Returned terms' names will contain the value of 'search',
 * case-insensitive. Default is an empty string.
 *
 * name__like - Returned terms' names will contain the value of 'name__like',
 * case-insensitive. Default is empty string.
 *
 * description__like - Returned terms' descriptions will contain the value of
 *  'description__like', case-insensitive. Default is empty string.
 *
 * The argument 'pad_counts', if set to true will include the quantity of a term's
 * children in the quantity of each term's "count" object variable.
 *
 * The 'get' argument, if set to 'all' instead of its default empty string,
 * returns terms regardless of ancestry or whether the terms are empty.
 *
 * The 'child_of' argument, when used, should be set to the integer of a term ID. Its default
 * is 0. If set to a non-zero value, all returned terms will be descendants
 * of that term according to the given taxonomy. Hence 'child_of' is set to 0
 * if more than one taxonomy is passed in $taxonomies, because multiple taxonomies
 * make term ancestry ambiguous.
 *
 * The 'parent' argument, when used, should be set to the integer of a term ID. Its default is
 * the empty string '', which has a different meaning from the integer 0.
 * If set to an integer value, all returned terms will have as an immediate
 * ancestor the term whose ID is specified by that integer according to the given taxonomy.
 * The 'parent' argument is different from 'child_of' in that a term X is considered a 'parent'
 * of term Y only if term X is the father of term Y, not its grandfather or great-grandfather, etc.
 *
 * The 'cache_domain' argument enables a unique cache key to be produced when this query is stored
 * in object cache. For instance, if you are using one of this function's filters to modify the
 * query (such as 'terms_clauses'), setting 'cache_domain' to a unique value will not overwrite
 * the cache for similar queries. Default value is 'core'.
 *
 * @package WordPress
 * @subpackage Taxonomy
 * @since 2.3.0
 *
 * @uses $wpdb
 * @uses wp_parse_args() Merges the defaults with those defined by $args and allows for strings.
 *
 * @param string|array $taxonomies Taxonomy name or list of Taxonomy names
 * @param string|array $args The values of what to search for when returning terms
 * @return array|WP_Error List of Term Objects 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)) {
            $error = new WP_Error('invalid_taxonomy', __('Invalid taxonomy'));
            return $error;
        }
    }
    $defaults = array('orderby' => 'name', 'order' => 'ASC', 'hide_empty' => true, 'exclude' => array(), 'exclude_tree' => array(), 'include' => array(), 'number' => '', 'fields' => 'all', 'slug' => '', 'parent' => '', 'hierarchical' => true, 'child_of' => 0, 'get' => '', 'name__like' => '', 'description__like' => '', 'pad_counts' => false, 'offset' => '', 'search' => '', 'cache_domain' => 'core');
    $args = wp_parse_args($args, $defaults);
    $args['number'] = absint($args['number']);
    $args['offset'] = absint($args['offset']);
    if (!$single_taxonomy || !is_taxonomy_hierarchical(reset($taxonomies)) || '' !== $args['parent']) {
        $args['child_of'] = 0;
        $args['hierarchical'] = false;
        $args['pad_counts'] = false;
    }
    if ('all' == $args['get']) {
        $args['child_of'] = 0;
        $args['hide_empty'] = 0;
        $args['hierarchical'] = false;
        $args['pad_counts'] = false;
    }
    $args = apply_filters('get_terms_args', $args, $taxonomies);
    extract($args, EXTR_SKIP);
    if ($child_of) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$child_of])) {
            return $empty_array;
        }
    }
    if ($parent) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$parent])) {
            return $empty_array;
        }
    }
    // $args can be whatever, only use the args defined in defaults to compute the key
    $filter_key = has_filter('list_terms_exclusions') ? serialize($GLOBALS['wp_filter']['list_terms_exclusions']) : '';
    $key = md5(serialize(compact(array_keys($defaults))) . serialize($taxonomies) . $filter_key);
    $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) {
        $cache = apply_filters('get_terms', $cache, $taxonomies, $args);
        return $cache;
    }
    $_orderby = strtolower($orderby);
    if ('count' == $_orderby) {
        $orderby = 'tt.count';
    } else if ('name' == $_orderby) {
        $orderby = 't.name';
    } else if ('slug' == $_orderby) {
        $orderby = 't.slug';
    } else if ('term_group' == $_orderby) {
        $orderby = 't.term_group';
    } else if ('none' == $_orderby) {
        $orderby = '';
    } elseif (empty($_orderby) || 'id' == $_orderby) {
        $orderby = 't.term_id';
    } else {
        $orderby = 't.name';
    }
    $orderby = apply_filters('get_terms_orderby', $orderby, $args, $taxonomies);
    if (!empty($orderby)) {
        $orderby = "ORDER BY {$orderby}";
    } else {
        $order = '';
    }
    $order = strtoupper($order);
    if ('' !== $order && !in_array($order, array('ASC', 'DESC'))) {
        $order = 'ASC';
    }
    $where = "tt.taxonomy IN ('" . implode("', '", array_map('esc_sql', $taxonomies)) . "')";
    $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 = '';
    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 = implode(',', array_map('intval', $excluded_children));
    }
    if (!empty($exclude)) {
        $exterms = wp_parse_id_list($exclude);
        if (empty($exclusions)) {
            $exclusions = implode(',', $exterms);
        } else {
            $exclusions .= ', ' . implode(',', $exterms);
        }
    }
    if (!empty($exclusions)) {
        $exclusions = ' AND t.term_id NOT IN (' . $exclusions . ')';
    }
    $exclusions = apply_filters('list_terms_exclusions', $exclusions, $args, $taxonomies);
    if (!empty($exclusions)) {
        $where .= $exclusions;
    }
    if (!empty($slug)) {
        $slug = sanitize_title($slug);
        $where .= " AND t.slug = '{$slug}'";
    }
    if (!empty($name__like)) {
        $name__like = like_escape($name__like);
        $where .= $wpdb->prepare(" AND t.name LIKE %s", '%' . $name__like . '%');
    }
    if (!empty($description__like)) {
        $description__like = like_escape($description__like);
        $where .= $wpdb->prepare(" AND tt.description LIKE %s", '%' . $description__like . '%');
    }
    if ('' !== $parent) {
        $parent = (int) $parent;
        $where .= " AND tt.parent = '{$parent}'";
    }
    if ('count' == $fields) {
        $hierarchical = false;
    }
    if ($hide_empty && !$hierarchical) {
        $where .= ' AND tt.count > 0';
    }
    // 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($search)) {
        $search = like_escape($search);
        $where .= $wpdb->prepare(' AND ((t.name LIKE %s) OR (t.slug LIKE %s))', '%' . $search . '%', '%' . $search . '%');
    }
    $selects = array();
    switch ($fields) {
        case 'all':
            $selects = array('t.*', 'tt.*');
            break;
        case 'ids':
        case 'id=>parent':
            $selects = array('t.term_id', 'tt.parent', 'tt.count');
            break;
        case 'names':
            $selects = array('t.term_id', 'tt.parent', 'tt.count', 't.name');
            break;
        case 'count':
            $orderby = '';
            $order = '';
            $selects = array('COUNT(*)');
            break;
        case 'id=>name':
            $selects = array('t.term_id', 't.name');
            break;
        case 'id=>slug':
            $selects = array('t.term_id', 't.slug');
            break;
    }
    $_fields = $fields;
    $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', 'orderby', 'order', 'limits');
    $clauses = apply_filters('terms_clauses', compact($pieces), $taxonomies, $args);
    foreach ($pieces as $piece) {
        ${$piece} = isset($clauses[$piece]) ? $clauses[$piece] : '';
    }
    $query = "SELECT {$fields} FROM {$wpdb->terms} AS t {$join} WHERE {$where} {$orderby} {$order} {$limits}";
    $fields = $_fields;
    if ('count' == $fields) {
        $term_count = $wpdb->get_var($query);
        return $term_count;
    }
    $terms = $wpdb->get_results($query);
    if ('all' == $fields) {
        update_term_cache($terms);
    }
    if (empty($terms)) {
        wp_cache_add($cache_key, array(), 'terms', DAY_IN_SECONDS);
        $terms = apply_filters('get_terms', array(), $taxonomies, $args);
        return $terms;
    }
    if ($child_of) {
        $children = _get_term_hierarchy(reset($taxonomies));
        if (!empty($children)) {
            $terms = _get_term_children($child_of, $terms, reset($taxonomies));
        }
    }
    // Update term counts to include children.
    if ($pad_counts && 'all' == $fields) {
        _pad_term_counts($terms, reset($taxonomies));
    }
    // Make sure we show empty categories that have children.
    if ($hierarchical && $hide_empty && is_array($terms)) {
        foreach ($terms as $k => $term) {
            if (!$term->count) {
                $children = _get_term_children($term->term_id, $terms, reset($taxonomies));
                if (is_array($children)) {
                    foreach ($children as $child) {
                        if ($child->count) {
                            continue 2;
                        }
                    }
                }
                // It really is empty
                unset($terms[$k]);
            }
        }
    }
    reset($terms);
    $_terms = array();
    if ('id=>parent' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->parent;
        }
    } elseif ('ids' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->term_id;
        }
    } elseif ('names' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->name;
        }
    } elseif ('id=>name' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->name;
        }
    } elseif ('id=>slug' == $fields) {
        while ($term = array_shift($terms)) {
            $_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);
    }
    wp_cache_add($cache_key, $terms, 'terms', DAY_IN_SECONDS);
    $terms = apply_filters('get_terms', $terms, $taxonomies, $args);
    return $terms;
}

WordPress Version: 7.2

/**
 * 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 '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 'list_terms_exclusions' filter passes the compiled exclusions along with
 * the $args.
 *
 * The 'get_terms_orderby' filter passes the ORDER BY clause for the query
 * along with the $args array.
 *
 * The 'get_terms_fields' filter passes the fields for the SELECT query
 * along with the $args array.
 *
 * The list of arguments that $args can contain, which will overwrite the defaults:
 *
 * orderby - Default is 'name'. Can be name, count, term_group, slug or nothing
 * (will use term_id), Passing a custom value other than these will cause it to
 * order based on the custom value.
 *
 * order - Default is ASC. Can use DESC.
 *
 * hide_empty - Default is true. Will not return empty terms, which means
 * terms whose count is 0 according to the given taxonomy.
 *
 * exclude - Default is an empty array. An array, comma- or space-delimited string
 * of term ids to exclude from the return array. If 'include' is non-empty,
 * 'exclude' is ignored.
 *
 * exclude_tree - Default is an empty array. An array, comma- or space-delimited
 * string of term ids to exclude from the return array, along with all of their
 * descendant terms according to the primary taxonomy. If 'include' is non-empty,
 * 'exclude_tree' is ignored.
 *
 * include - Default is an empty array. An array, comma- or space-delimited string
 * of term ids to include in the return array.
 *
 * number - The maximum number of terms to return. Default is to return them all.
 *
 * offset - The number by which to offset the terms query.
 *
 * fields - Default is 'all', which returns an array of term objects.
 * If 'fields' is 'ids' or 'names', returns an array of
 * integers or strings, respectively.
 *
 * slug - Returns terms whose "slug" matches this value. Default is empty string.
 *
 * hierarchical - Whether to include terms that have non-empty descendants
 * (even if 'hide_empty' is set to true).
 *
 * search - Returned terms' names will contain the value of 'search',
 * case-insensitive. Default is an empty string.
 *
 * name__like - Returned terms' names will contain the value of 'name__like',
 * case-insensitive. Default is empty string.
 *
 * description__like - Returned terms' descriptions will contain the value of
 *  'description__like', case-insensitive. Default is empty string.
 *
 * The argument 'pad_counts', if set to true will include the quantity of a term's
 * children in the quantity of each term's "count" object variable.
 *
 * The 'get' argument, if set to 'all' instead of its default empty string,
 * returns terms regardless of ancestry or whether the terms are empty.
 *
 * The 'child_of' argument, when used, should be set to the integer of a term ID. Its default
 * is 0. If set to a non-zero value, all returned terms will be descendants
 * of that term according to the given taxonomy. Hence 'child_of' is set to 0
 * if more than one taxonomy is passed in $taxonomies, because multiple taxonomies
 * make term ancestry ambiguous.
 *
 * The 'parent' argument, when used, should be set to the integer of a term ID. Its default is
 * the empty string '', which has a different meaning from the integer 0.
 * If set to an integer value, all returned terms will have as an immediate
 * ancestor the term whose ID is specified by that integer according to the given taxonomy.
 * The 'parent' argument is different from 'child_of' in that a term X is considered a 'parent'
 * of term Y only if term X is the father of term Y, not its grandfather or great-grandfather, etc.
 *
 * The 'cache_domain' argument enables a unique cache key to be produced when this query is stored
 * in object cache. For instance, if you are using one of this function's filters to modify the
 * query (such as 'terms_clauses'), setting 'cache_domain' to a unique value will not overwrite
 * the cache for similar queries. Default value is 'core'.
 *
 * @package WordPress
 * @subpackage Taxonomy
 * @since 2.3.0
 *
 * @uses $wpdb
 * @uses wp_parse_args() Merges the defaults with those defined by $args and allows for strings.
 *
 * @param string|array $taxonomies Taxonomy name or list of Taxonomy names
 * @param string|array $args The values of what to search for when returning terms
 * @return array|WP_Error List of Term Objects 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)) {
            $error = new WP_Error('invalid_taxonomy', __('Invalid taxonomy'));
            return $error;
        }
    }
    $defaults = array('orderby' => 'name', 'order' => 'ASC', 'hide_empty' => true, 'exclude' => array(), 'exclude_tree' => array(), 'include' => array(), 'number' => '', 'fields' => 'all', 'slug' => '', 'parent' => '', 'hierarchical' => true, 'child_of' => 0, 'get' => '', 'name__like' => '', 'description__like' => '', 'pad_counts' => false, 'offset' => '', 'search' => '', 'cache_domain' => 'core');
    $args = wp_parse_args($args, $defaults);
    $args['number'] = absint($args['number']);
    $args['offset'] = absint($args['offset']);
    if (!$single_taxonomy || !is_taxonomy_hierarchical(reset($taxonomies)) || '' !== $args['parent']) {
        $args['child_of'] = 0;
        $args['hierarchical'] = false;
        $args['pad_counts'] = false;
    }
    if ('all' == $args['get']) {
        $args['child_of'] = 0;
        $args['hide_empty'] = 0;
        $args['hierarchical'] = false;
        $args['pad_counts'] = false;
    }
    $args = apply_filters('get_terms_args', $args, $taxonomies);
    extract($args, EXTR_SKIP);
    if ($child_of) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$child_of])) {
            return $empty_array;
        }
    }
    if ($parent) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$parent])) {
            return $empty_array;
        }
    }
    // $args can be whatever, only use the args defined in defaults to compute the key
    $filter_key = has_filter('list_terms_exclusions') ? serialize($GLOBALS['wp_filter']['list_terms_exclusions']) : '';
    $key = md5(serialize(compact(array_keys($defaults))) . serialize($taxonomies) . $filter_key);
    $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) {
        $cache = apply_filters('get_terms', $cache, $taxonomies, $args);
        return $cache;
    }
    $_orderby = strtolower($orderby);
    if ('count' == $_orderby) {
        $orderby = 'tt.count';
    } else if ('name' == $_orderby) {
        $orderby = 't.name';
    } else if ('slug' == $_orderby) {
        $orderby = 't.slug';
    } else if ('term_group' == $_orderby) {
        $orderby = 't.term_group';
    } else if ('none' == $_orderby) {
        $orderby = '';
    } elseif (empty($_orderby) || 'id' == $_orderby) {
        $orderby = 't.term_id';
    } else {
        $orderby = 't.name';
    }
    $orderby = apply_filters('get_terms_orderby', $orderby, $args, $taxonomies);
    if (!empty($orderby)) {
        $orderby = "ORDER BY {$orderby}";
    } else {
        $order = '';
    }
    $order = strtoupper($order);
    if ('' !== $order && !in_array($order, array('ASC', 'DESC'))) {
        $order = 'ASC';
    }
    $where = "tt.taxonomy IN ('" . implode("', '", $taxonomies) . "')";
    $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 = '';
    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 = implode(',', array_map('intval', $excluded_children));
    }
    if (!empty($exclude)) {
        $exterms = wp_parse_id_list($exclude);
        if (empty($exclusions)) {
            $exclusions = implode(',', $exterms);
        } else {
            $exclusions .= ', ' . implode(',', $exterms);
        }
    }
    if (!empty($exclusions)) {
        $exclusions = ' AND t.term_id NOT IN (' . $exclusions . ')';
    }
    $exclusions = apply_filters('list_terms_exclusions', $exclusions, $args, $taxonomies);
    if (!empty($exclusions)) {
        $where .= $exclusions;
    }
    if (!empty($slug)) {
        $slug = sanitize_title($slug);
        $where .= " AND t.slug = '{$slug}'";
    }
    if (!empty($name__like)) {
        $name__like = like_escape($name__like);
        $where .= $wpdb->prepare(" AND t.name LIKE %s", '%' . $name__like . '%');
    }
    if (!empty($description__like)) {
        $description__like = like_escape($description__like);
        $where .= $wpdb->prepare(" AND tt.description LIKE %s", '%' . $description__like . '%');
    }
    if ('' !== $parent) {
        $parent = (int) $parent;
        $where .= " AND tt.parent = '{$parent}'";
    }
    if ('count' == $fields) {
        $hierarchical = false;
    }
    if ($hide_empty && !$hierarchical) {
        $where .= ' AND tt.count > 0';
    }
    // 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($search)) {
        $search = like_escape($search);
        $where .= $wpdb->prepare(' AND ((t.name LIKE %s) OR (t.slug LIKE %s))', '%' . $search . '%', '%' . $search . '%');
    }
    $selects = array();
    switch ($fields) {
        case 'all':
            $selects = array('t.*', 'tt.*');
            break;
        case 'ids':
        case 'id=>parent':
            $selects = array('t.term_id', 'tt.parent', 'tt.count');
            break;
        case 'names':
            $selects = array('t.term_id', 'tt.parent', 'tt.count', 't.name');
            break;
        case 'count':
            $orderby = '';
            $order = '';
            $selects = array('COUNT(*)');
            break;
        case 'id=>name':
            $selects = array('t.term_id', 't.name');
            break;
        case 'id=>slug':
            $selects = array('t.term_id', 't.slug');
            break;
    }
    $_fields = $fields;
    $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', 'orderby', 'order', 'limits');
    $clauses = apply_filters('terms_clauses', compact($pieces), $taxonomies, $args);
    foreach ($pieces as $piece) {
        ${$piece} = isset($clauses[$piece]) ? $clauses[$piece] : '';
    }
    $query = "SELECT {$fields} FROM {$wpdb->terms} AS t {$join} WHERE {$where} {$orderby} {$order} {$limits}";
    $fields = $_fields;
    if ('count' == $fields) {
        $term_count = $wpdb->get_var($query);
        return $term_count;
    }
    $terms = $wpdb->get_results($query);
    if ('all' == $fields) {
        update_term_cache($terms);
    }
    if (empty($terms)) {
        wp_cache_add($cache_key, array(), 'terms', DAY_IN_SECONDS);
        $terms = apply_filters('get_terms', array(), $taxonomies, $args);
        return $terms;
    }
    if ($child_of) {
        $children = _get_term_hierarchy(reset($taxonomies));
        if (!empty($children)) {
            $terms = _get_term_children($child_of, $terms, reset($taxonomies));
        }
    }
    // Update term counts to include children.
    if ($pad_counts && 'all' == $fields) {
        _pad_term_counts($terms, reset($taxonomies));
    }
    // Make sure we show empty categories that have children.
    if ($hierarchical && $hide_empty && is_array($terms)) {
        foreach ($terms as $k => $term) {
            if (!$term->count) {
                $children = _get_term_children($term->term_id, $terms, reset($taxonomies));
                if (is_array($children)) {
                    foreach ($children as $child) {
                        if ($child->count) {
                            continue 2;
                        }
                    }
                }
                // It really is empty
                unset($terms[$k]);
            }
        }
    }
    reset($terms);
    $_terms = array();
    if ('id=>parent' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->parent;
        }
    } elseif ('ids' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->term_id;
        }
    } elseif ('names' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->name;
        }
    } elseif ('id=>name' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->name;
        }
    } elseif ('id=>slug' == $fields) {
        while ($term = array_shift($terms)) {
            $_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);
    }
    wp_cache_add($cache_key, $terms, 'terms', DAY_IN_SECONDS);
    $terms = apply_filters('get_terms', $terms, $taxonomies, $args);
    return $terms;
}

WordPress Version: .14

/**
 * 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 '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 'list_terms_exclusions' filter passes the compiled exclusions along with
 * the $args.
 *
 * The 'get_terms_orderby' filter passes the ORDER BY clause for the query
 * along with the $args array.
 *
 * The 'get_terms_fields' filter passes the fields for the SELECT query
 * along with the $args array.
 *
 * The list of arguments that $args can contain, which will overwrite the defaults:
 *
 * orderby - Default is 'name'. Can be name, count, term_group, slug or nothing
 * (will use term_id), Passing a custom value other than these will cause it to
 * order based on the custom value.
 *
 * order - Default is ASC. Can use DESC.
 *
 * hide_empty - Default is true. Will not return empty terms, which means
 * terms whose count is 0 according to the given taxonomy.
 *
 * exclude - Default is an empty array. An array, comma- or space-delimited string
 * of term ids to exclude from the return array. If 'include' is non-empty,
 * 'exclude' is ignored.
 *
 * exclude_tree - Default is an empty array. An array, comma- or space-delimited
 * string of term ids to exclude from the return array, along with all of their
 * descendant terms according to the primary taxonomy. If 'include' is non-empty,
 * 'exclude_tree' is ignored.
 *
 * include - Default is an empty array. An array, comma- or space-delimited string
 * of term ids to include in the return array.
 *
 * number - The maximum number of terms to return. Default is to return them all.
 *
 * offset - The number by which to offset the terms query.
 *
 * fields - Default is 'all', which returns an array of term objects.
 * If 'fields' is 'ids' or 'names', returns an array of
 * integers or strings, respectively.
 *
 * slug - Returns terms whose "slug" matches this value. Default is empty string.
 *
 * hierarchical - Whether to include terms that have non-empty descendants
 * (even if 'hide_empty' is set to true).
 *
 * search - Returned terms' names will contain the value of 'search',
 * case-insensitive. Default is an empty string.
 *
 * name__like - Returned terms' names will contain the value of 'name__like',
 * case-insensitive. Default is empty string.
 *
 * description__like - Returned terms' descriptions will contain the value of
 *  'description__like', case-insensitive. Default is empty string.
 *
 * The argument 'pad_counts', if set to true will include the quantity of a term's
 * children in the quantity of each term's "count" object variable.
 *
 * The 'get' argument, if set to 'all' instead of its default empty string,
 * returns terms regardless of ancestry or whether the terms are empty.
 *
 * The 'child_of' argument, when used, should be set to the integer of a term ID. Its default
 * is 0. If set to a non-zero value, all returned terms will be descendants
 * of that term according to the given taxonomy. Hence 'child_of' is set to 0
 * if more than one taxonomy is passed in $taxonomies, because multiple taxonomies
 * make term ancestry ambiguous.
 *
 * The 'parent' argument, when used, should be set to the integer of a term ID. Its default is
 * the empty string '', which has a different meaning from the integer 0.
 * If set to an integer value, all returned terms will have as an immediate
 * ancestor the term whose ID is specified by that integer according to the given taxonomy.
 * The 'parent' argument is different from 'child_of' in that a term X is considered a 'parent'
 * of term Y only if term X is the father of term Y, not its grandfather or great-grandfather, etc.
 *
 * The 'cache_domain' argument enables a unique cache key to be produced when this query is stored
 * in object cache. For instance, if you are using one of this function's filters to modify the
 * query (such as 'terms_clauses'), setting 'cache_domain' to a unique value will not overwrite
 * the cache for similar queries. Default value is 'core'.
 *
 * @package WordPress
 * @subpackage Taxonomy
 * @since 2.3.0
 *
 * @uses $wpdb
 * @uses wp_parse_args() Merges the defaults with those defined by $args and allows for strings.
 *
 * @param string|array $taxonomies Taxonomy name or list of Taxonomy names
 * @param string|array $args The values of what to search for when returning terms
 * @return array|WP_Error List of Term Objects 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)) {
            $error = new WP_Error('invalid_taxonomy', __('Invalid taxonomy'));
            return $error;
        }
    }
    $defaults = array('orderby' => 'name', 'order' => 'ASC', 'hide_empty' => true, 'exclude' => array(), 'exclude_tree' => array(), 'include' => array(), 'number' => '', 'fields' => 'all', 'slug' => '', 'parent' => '', 'hierarchical' => true, 'child_of' => 0, 'get' => '', 'name__like' => '', 'description__like' => '', 'pad_counts' => false, 'offset' => '', 'search' => '', 'cache_domain' => 'core');
    $args = wp_parse_args($args, $defaults);
    $args['number'] = absint($args['number']);
    $args['offset'] = absint($args['offset']);
    if (!$single_taxonomy || !is_taxonomy_hierarchical(reset($taxonomies)) || '' !== $args['parent']) {
        $args['child_of'] = 0;
        $args['hierarchical'] = false;
        $args['pad_counts'] = false;
    }
    if ('all' == $args['get']) {
        $args['child_of'] = 0;
        $args['hide_empty'] = 0;
        $args['hierarchical'] = false;
        $args['pad_counts'] = false;
    }
    $args = apply_filters('get_terms_args', $args, $taxonomies);
    extract($args, EXTR_SKIP);
    if ($child_of) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$child_of])) {
            return $empty_array;
        }
    }
    if ($parent) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$parent])) {
            return $empty_array;
        }
    }
    // $args can be whatever, only use the args defined in defaults to compute the key
    $filter_key = has_filter('list_terms_exclusions') ? serialize($GLOBALS['wp_filter']['list_terms_exclusions']) : '';
    $key = md5(serialize(compact(array_keys($defaults))) . serialize($taxonomies) . $filter_key);
    $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) {
        $cache = apply_filters('get_terms', $cache, $taxonomies, $args);
        return $cache;
    }
    $_orderby = strtolower($orderby);
    if ('count' == $_orderby) {
        $orderby = 'tt.count';
    } else if ('name' == $_orderby) {
        $orderby = 't.name';
    } else if ('slug' == $_orderby) {
        $orderby = 't.slug';
    } else if ('term_group' == $_orderby) {
        $orderby = 't.term_group';
    } else if ('none' == $_orderby) {
        $orderby = '';
    } elseif (empty($_orderby) || 'id' == $_orderby) {
        $orderby = 't.term_id';
    } else {
        $orderby = 't.name';
    }
    $orderby = apply_filters('get_terms_orderby', $orderby, $args, $taxonomies);
    if (!empty($orderby)) {
        $orderby = "ORDER BY {$orderby}";
    } else {
        $order = '';
    }
    $order = strtoupper($order);
    if ('' !== $order && !in_array($order, array('ASC', 'DESC'))) {
        $order = 'ASC';
    }
    $where = "tt.taxonomy IN ('" . implode("', '", array_map('esc_sql', $taxonomies)) . "')";
    $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 = '';
    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 = implode(',', array_map('intval', $excluded_children));
    }
    if (!empty($exclude)) {
        $exterms = wp_parse_id_list($exclude);
        if (empty($exclusions)) {
            $exclusions = implode(',', $exterms);
        } else {
            $exclusions .= ', ' . implode(',', $exterms);
        }
    }
    if (!empty($exclusions)) {
        $exclusions = ' AND t.term_id NOT IN (' . $exclusions . ')';
    }
    $exclusions = apply_filters('list_terms_exclusions', $exclusions, $args, $taxonomies);
    if (!empty($exclusions)) {
        $where .= $exclusions;
    }
    if (!empty($slug)) {
        $slug = sanitize_title($slug);
        $where .= " AND t.slug = '{$slug}'";
    }
    if (!empty($name__like)) {
        $name__like = like_escape($name__like);
        $where .= $wpdb->prepare(" AND t.name LIKE %s", '%' . $name__like . '%');
    }
    if (!empty($description__like)) {
        $description__like = like_escape($description__like);
        $where .= $wpdb->prepare(" AND tt.description LIKE %s", '%' . $description__like . '%');
    }
    if ('' !== $parent) {
        $parent = (int) $parent;
        $where .= " AND tt.parent = '{$parent}'";
    }
    if ('count' == $fields) {
        $hierarchical = false;
    }
    if ($hide_empty && !$hierarchical) {
        $where .= ' AND tt.count > 0';
    }
    // 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($search)) {
        $search = like_escape($search);
        $where .= $wpdb->prepare(' AND ((t.name LIKE %s) OR (t.slug LIKE %s))', '%' . $search . '%', '%' . $search . '%');
    }
    $selects = array();
    switch ($fields) {
        case 'all':
            $selects = array('t.*', 'tt.*');
            break;
        case 'ids':
        case 'id=>parent':
            $selects = array('t.term_id', 'tt.parent', 'tt.count');
            break;
        case 'names':
            $selects = array('t.term_id', 'tt.parent', 'tt.count', 't.name');
            break;
        case 'count':
            $orderby = '';
            $order = '';
            $selects = array('COUNT(*)');
            break;
        case 'id=>name':
            $selects = array('t.term_id', 't.name');
            break;
        case 'id=>slug':
            $selects = array('t.term_id', 't.slug');
            break;
    }
    $_fields = $fields;
    $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', 'orderby', 'order', 'limits');
    $clauses = apply_filters('terms_clauses', compact($pieces), $taxonomies, $args);
    foreach ($pieces as $piece) {
        ${$piece} = isset($clauses[$piece]) ? $clauses[$piece] : '';
    }
    $query = "SELECT {$fields} FROM {$wpdb->terms} AS t {$join} WHERE {$where} {$orderby} {$order} {$limits}";
    $fields = $_fields;
    if ('count' == $fields) {
        $term_count = $wpdb->get_var($query);
        return $term_count;
    }
    $terms = $wpdb->get_results($query);
    if ('all' == $fields) {
        update_term_cache($terms);
    }
    if (empty($terms)) {
        wp_cache_add($cache_key, array(), 'terms', DAY_IN_SECONDS);
        $terms = apply_filters('get_terms', array(), $taxonomies, $args);
        return $terms;
    }
    if ($child_of) {
        $children = _get_term_hierarchy(reset($taxonomies));
        if (!empty($children)) {
            $terms = _get_term_children($child_of, $terms, reset($taxonomies));
        }
    }
    // Update term counts to include children.
    if ($pad_counts && 'all' == $fields) {
        _pad_term_counts($terms, reset($taxonomies));
    }
    // Make sure we show empty categories that have children.
    if ($hierarchical && $hide_empty && is_array($terms)) {
        foreach ($terms as $k => $term) {
            if (!$term->count) {
                $children = _get_term_children($term->term_id, $terms, reset($taxonomies));
                if (is_array($children)) {
                    foreach ($children as $child) {
                        if ($child->count) {
                            continue 2;
                        }
                    }
                }
                // It really is empty
                unset($terms[$k]);
            }
        }
    }
    reset($terms);
    $_terms = array();
    if ('id=>parent' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->parent;
        }
    } elseif ('ids' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->term_id;
        }
    } elseif ('names' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->name;
        }
    } elseif ('id=>name' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->name;
        }
    } elseif ('id=>slug' == $fields) {
        while ($term = array_shift($terms)) {
            $_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);
    }
    wp_cache_add($cache_key, $terms, 'terms', DAY_IN_SECONDS);
    $terms = apply_filters('get_terms', $terms, $taxonomies, $args);
    return $terms;
}

WordPress Version: 7.1

/**
 * 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 '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 'list_terms_exclusions' filter passes the compiled exclusions along with
 * the $args.
 *
 * The 'get_terms_orderby' filter passes the ORDER BY clause for the query
 * along with the $args array.
 *
 * The 'get_terms_fields' filter passes the fields for the SELECT query
 * along with the $args array.
 *
 * The list of arguments that $args can contain, which will overwrite the defaults:
 *
 * orderby - Default is 'name'. Can be name, count, term_group, slug or nothing
 * (will use term_id), Passing a custom value other than these will cause it to
 * order based on the custom value.
 *
 * order - Default is ASC. Can use DESC.
 *
 * hide_empty - Default is true. Will not return empty terms, which means
 * terms whose count is 0 according to the given taxonomy.
 *
 * exclude - Default is an empty array. An array, comma- or space-delimited string
 * of term ids to exclude from the return array. If 'include' is non-empty,
 * 'exclude' is ignored.
 *
 * exclude_tree - Default is an empty array. An array, comma- or space-delimited
 * string of term ids to exclude from the return array, along with all of their
 * descendant terms according to the primary taxonomy. If 'include' is non-empty,
 * 'exclude_tree' is ignored.
 *
 * include - Default is an empty array. An array, comma- or space-delimited string
 * of term ids to include in the return array.
 *
 * number - The maximum number of terms to return. Default is to return them all.
 *
 * offset - The number by which to offset the terms query.
 *
 * fields - Default is 'all', which returns an array of term objects.
 * If 'fields' is 'ids' or 'names', returns an array of
 * integers or strings, respectively.
 *
 * slug - Returns terms whose "slug" matches this value. Default is empty string.
 *
 * hierarchical - Whether to include terms that have non-empty descendants
 * (even if 'hide_empty' is set to true).
 *
 * search - Returned terms' names will contain the value of 'search',
 * case-insensitive. Default is an empty string.
 *
 * name__like - Returned terms' names will contain the value of 'name__like',
 * case-insensitive. Default is empty string.
 *
 * description__like - Returned terms' descriptions will contain the value of
 *  'description__like', case-insensitive. Default is empty string.
 *
 * The argument 'pad_counts', if set to true will include the quantity of a term's
 * children in the quantity of each term's "count" object variable.
 *
 * The 'get' argument, if set to 'all' instead of its default empty string,
 * returns terms regardless of ancestry or whether the terms are empty.
 *
 * The 'child_of' argument, when used, should be set to the integer of a term ID. Its default
 * is 0. If set to a non-zero value, all returned terms will be descendants
 * of that term according to the given taxonomy. Hence 'child_of' is set to 0
 * if more than one taxonomy is passed in $taxonomies, because multiple taxonomies
 * make term ancestry ambiguous.
 *
 * The 'parent' argument, when used, should be set to the integer of a term ID. Its default is
 * the empty string '', which has a different meaning from the integer 0.
 * If set to an integer value, all returned terms will have as an immediate
 * ancestor the term whose ID is specified by that integer according to the given taxonomy.
 * The 'parent' argument is different from 'child_of' in that a term X is considered a 'parent'
 * of term Y only if term X is the father of term Y, not its grandfather or great-grandfather, etc.
 *
 * The 'cache_domain' argument enables a unique cache key to be produced when this query is stored
 * in object cache. For instance, if you are using one of this function's filters to modify the
 * query (such as 'terms_clauses'), setting 'cache_domain' to a unique value will not overwrite
 * the cache for similar queries. Default value is 'core'.
 *
 * @package WordPress
 * @subpackage Taxonomy
 * @since 2.3.0
 *
 * @uses $wpdb
 * @uses wp_parse_args() Merges the defaults with those defined by $args and allows for strings.
 *
 * @param string|array $taxonomies Taxonomy name or list of Taxonomy names
 * @param string|array $args The values of what to search for when returning terms
 * @return array|WP_Error List of Term Objects 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)) {
            $error = new WP_Error('invalid_taxonomy', __('Invalid taxonomy'));
            return $error;
        }
    }
    $defaults = array('orderby' => 'name', 'order' => 'ASC', 'hide_empty' => true, 'exclude' => array(), 'exclude_tree' => array(), 'include' => array(), 'number' => '', 'fields' => 'all', 'slug' => '', 'parent' => '', 'hierarchical' => true, 'child_of' => 0, 'get' => '', 'name__like' => '', 'description__like' => '', 'pad_counts' => false, 'offset' => '', 'search' => '', 'cache_domain' => 'core');
    $args = wp_parse_args($args, $defaults);
    $args['number'] = absint($args['number']);
    $args['offset'] = absint($args['offset']);
    if (!$single_taxonomy || !is_taxonomy_hierarchical(reset($taxonomies)) || '' !== $args['parent']) {
        $args['child_of'] = 0;
        $args['hierarchical'] = false;
        $args['pad_counts'] = false;
    }
    if ('all' == $args['get']) {
        $args['child_of'] = 0;
        $args['hide_empty'] = 0;
        $args['hierarchical'] = false;
        $args['pad_counts'] = false;
    }
    $args = apply_filters('get_terms_args', $args, $taxonomies);
    extract($args, EXTR_SKIP);
    if ($child_of) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$child_of])) {
            return $empty_array;
        }
    }
    if ($parent) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$parent])) {
            return $empty_array;
        }
    }
    // $args can be whatever, only use the args defined in defaults to compute the key
    $filter_key = has_filter('list_terms_exclusions') ? serialize($GLOBALS['wp_filter']['list_terms_exclusions']) : '';
    $key = md5(serialize(compact(array_keys($defaults))) . serialize($taxonomies) . $filter_key);
    $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) {
        $cache = apply_filters('get_terms', $cache, $taxonomies, $args);
        return $cache;
    }
    $_orderby = strtolower($orderby);
    if ('count' == $_orderby) {
        $orderby = 'tt.count';
    } else if ('name' == $_orderby) {
        $orderby = 't.name';
    } else if ('slug' == $_orderby) {
        $orderby = 't.slug';
    } else if ('term_group' == $_orderby) {
        $orderby = 't.term_group';
    } else if ('none' == $_orderby) {
        $orderby = '';
    } elseif (empty($_orderby) || 'id' == $_orderby) {
        $orderby = 't.term_id';
    } else {
        $orderby = 't.name';
    }
    $orderby = apply_filters('get_terms_orderby', $orderby, $args, $taxonomies);
    if (!empty($orderby)) {
        $orderby = "ORDER BY {$orderby}";
    } else {
        $order = '';
    }
    $order = strtoupper($order);
    if ('' !== $order && !in_array($order, array('ASC', 'DESC'))) {
        $order = 'ASC';
    }
    $where = "tt.taxonomy IN ('" . implode("', '", $taxonomies) . "')";
    $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 = '';
    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 = implode(',', array_map('intval', $excluded_children));
    }
    if (!empty($exclude)) {
        $exterms = wp_parse_id_list($exclude);
        if (empty($exclusions)) {
            $exclusions = implode(',', $exterms);
        } else {
            $exclusions .= ', ' . implode(',', $exterms);
        }
    }
    if (!empty($exclusions)) {
        $exclusions = ' AND t.term_id NOT IN (' . $exclusions . ')';
    }
    $exclusions = apply_filters('list_terms_exclusions', $exclusions, $args, $taxonomies);
    if (!empty($exclusions)) {
        $where .= $exclusions;
    }
    if (!empty($slug)) {
        $slug = sanitize_title($slug);
        $where .= " AND t.slug = '{$slug}'";
    }
    if (!empty($name__like)) {
        $name__like = like_escape($name__like);
        $where .= $wpdb->prepare(" AND t.name LIKE %s", '%' . $name__like . '%');
    }
    if (!empty($description__like)) {
        $description__like = like_escape($description__like);
        $where .= $wpdb->prepare(" AND tt.description LIKE %s", '%' . $description__like . '%');
    }
    if ('' !== $parent) {
        $parent = (int) $parent;
        $where .= " AND tt.parent = '{$parent}'";
    }
    if ('count' == $fields) {
        $hierarchical = false;
    }
    if ($hide_empty && !$hierarchical) {
        $where .= ' AND tt.count > 0';
    }
    // 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($search)) {
        $search = like_escape($search);
        $where .= $wpdb->prepare(' AND ((t.name LIKE %s) OR (t.slug LIKE %s))', '%' . $search . '%', '%' . $search . '%');
    }
    $selects = array();
    switch ($fields) {
        case 'all':
            $selects = array('t.*', 'tt.*');
            break;
        case 'ids':
        case 'id=>parent':
            $selects = array('t.term_id', 'tt.parent', 'tt.count');
            break;
        case 'names':
            $selects = array('t.term_id', 'tt.parent', 'tt.count', 't.name');
            break;
        case 'count':
            $orderby = '';
            $order = '';
            $selects = array('COUNT(*)');
            break;
        case 'id=>name':
            $selects = array('t.term_id', 't.name');
            break;
        case 'id=>slug':
            $selects = array('t.term_id', 't.slug');
            break;
    }
    $_fields = $fields;
    $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', 'orderby', 'order', 'limits');
    $clauses = apply_filters('terms_clauses', compact($pieces), $taxonomies, $args);
    foreach ($pieces as $piece) {
        ${$piece} = isset($clauses[$piece]) ? $clauses[$piece] : '';
    }
    $query = "SELECT {$fields} FROM {$wpdb->terms} AS t {$join} WHERE {$where} {$orderby} {$order} {$limits}";
    $fields = $_fields;
    if ('count' == $fields) {
        $term_count = $wpdb->get_var($query);
        return $term_count;
    }
    $terms = $wpdb->get_results($query);
    if ('all' == $fields) {
        update_term_cache($terms);
    }
    if (empty($terms)) {
        wp_cache_add($cache_key, array(), 'terms', DAY_IN_SECONDS);
        $terms = apply_filters('get_terms', array(), $taxonomies, $args);
        return $terms;
    }
    if ($child_of) {
        $children = _get_term_hierarchy(reset($taxonomies));
        if (!empty($children)) {
            $terms = _get_term_children($child_of, $terms, reset($taxonomies));
        }
    }
    // Update term counts to include children.
    if ($pad_counts && 'all' == $fields) {
        _pad_term_counts($terms, reset($taxonomies));
    }
    // Make sure we show empty categories that have children.
    if ($hierarchical && $hide_empty && is_array($terms)) {
        foreach ($terms as $k => $term) {
            if (!$term->count) {
                $children = _get_term_children($term->term_id, $terms, reset($taxonomies));
                if (is_array($children)) {
                    foreach ($children as $child) {
                        if ($child->count) {
                            continue 2;
                        }
                    }
                }
                // It really is empty
                unset($terms[$k]);
            }
        }
    }
    reset($terms);
    $_terms = array();
    if ('id=>parent' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->parent;
        }
    } elseif ('ids' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->term_id;
        }
    } elseif ('names' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->name;
        }
    } elseif ('id=>name' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->name;
        }
    } elseif ('id=>slug' == $fields) {
        while ($term = array_shift($terms)) {
            $_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);
    }
    wp_cache_add($cache_key, $terms, 'terms', DAY_IN_SECONDS);
    $terms = apply_filters('get_terms', $terms, $taxonomies, $args);
    return $terms;
}

WordPress Version: 3.7

/**
 * 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 '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 'list_terms_exclusions' filter passes the compiled exclusions along with
 * the $args.
 *
 * The 'get_terms_orderby' filter passes the ORDER BY clause for the query
 * along with the $args array.
 *
 * The 'get_terms_fields' filter passes the fields for the SELECT query
 * along with the $args array.
 *
 * The list of arguments that $args can contain, which will overwrite the defaults:
 *
 * orderby - Default is 'name'. Can be name, count, term_group, slug or nothing
 * (will use term_id), Passing a custom value other than these will cause it to
 * order based on the custom value.
 *
 * order - Default is ASC. Can use DESC.
 *
 * hide_empty - Default is true. Will not return empty terms, which means
 * terms whose count is 0 according to the given taxonomy.
 *
 * exclude - Default is an empty array. An array, comma- or space-delimited string
 * of term ids to exclude from the return array. If 'include' is non-empty,
 * 'exclude' is ignored.
 *
 * exclude_tree - Default is an empty array. An array, comma- or space-delimited
 * string of term ids to exclude from the return array, along with all of their
 * descendant terms according to the primary taxonomy. If 'include' is non-empty,
 * 'exclude_tree' is ignored.
 *
 * include - Default is an empty array. An array, comma- or space-delimited string
 * of term ids to include in the return array.
 *
 * number - The maximum number of terms to return. Default is to return them all.
 *
 * offset - The number by which to offset the terms query.
 *
 * fields - Default is 'all', which returns an array of term objects.
 * If 'fields' is 'ids' or 'names', returns an array of
 * integers or strings, respectively.
 *
 * slug - Returns terms whose "slug" matches this value. Default is empty string.
 *
 * hierarchical - Whether to include terms that have non-empty descendants
 * (even if 'hide_empty' is set to true).
 *
 * search - Returned terms' names will contain the value of 'search',
 * case-insensitive. Default is an empty string.
 *
 * name__like - Returned terms' names will contain the value of 'name__like',
 * case-insensitive. Default is empty string.
 *
 * description__like - Returned terms' descriptions will contain the value of
 *  'description__like', case-insensitive. Default is empty string.
 *
 * The argument 'pad_counts', if set to true will include the quantity of a term's
 * children in the quantity of each term's "count" object variable.
 *
 * The 'get' argument, if set to 'all' instead of its default empty string,
 * returns terms regardless of ancestry or whether the terms are empty.
 *
 * The 'child_of' argument, when used, should be set to the integer of a term ID. Its default
 * is 0. If set to a non-zero value, all returned terms will be descendants
 * of that term according to the given taxonomy. Hence 'child_of' is set to 0
 * if more than one taxonomy is passed in $taxonomies, because multiple taxonomies
 * make term ancestry ambiguous.
 *
 * The 'parent' argument, when used, should be set to the integer of a term ID. Its default is
 * the empty string '', which has a different meaning from the integer 0.
 * If set to an integer value, all returned terms will have as an immediate
 * ancestor the term whose ID is specified by that integer according to the given taxonomy.
 * The 'parent' argument is different from 'child_of' in that a term X is considered a 'parent'
 * of term Y only if term X is the father of term Y, not its grandfather or great-grandfather, etc.
 *
 * The 'cache_domain' argument enables a unique cache key to be produced when this query is stored
 * in object cache. For instance, if you are using one of this function's filters to modify the
 * query (such as 'terms_clauses'), setting 'cache_domain' to a unique value will not overwrite
 * the cache for similar queries. Default value is 'core'.
 *
 * @package WordPress
 * @subpackage Taxonomy
 * @since 2.3.0
 *
 * @uses $wpdb
 * @uses wp_parse_args() Merges the defaults with those defined by $args and allows for strings.
 *
 * @param string|array $taxonomies Taxonomy name or list of Taxonomy names
 * @param string|array $args The values of what to search for when returning terms
 * @return array|WP_Error List of Term Objects 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)) {
            $error = new WP_Error('invalid_taxonomy', __('Invalid taxonomy'));
            return $error;
        }
    }
    $defaults = array('orderby' => 'name', 'order' => 'ASC', 'hide_empty' => true, 'exclude' => array(), 'exclude_tree' => array(), 'include' => array(), 'number' => '', 'fields' => 'all', 'slug' => '', 'parent' => '', 'hierarchical' => true, 'child_of' => 0, 'get' => '', 'name__like' => '', 'description__like' => '', 'pad_counts' => false, 'offset' => '', 'search' => '', 'cache_domain' => 'core');
    $args = wp_parse_args($args, $defaults);
    $args['number'] = absint($args['number']);
    $args['offset'] = absint($args['offset']);
    if (!$single_taxonomy || !is_taxonomy_hierarchical(reset($taxonomies)) || '' !== $args['parent']) {
        $args['child_of'] = 0;
        $args['hierarchical'] = false;
        $args['pad_counts'] = false;
    }
    if ('all' == $args['get']) {
        $args['child_of'] = 0;
        $args['hide_empty'] = 0;
        $args['hierarchical'] = false;
        $args['pad_counts'] = false;
    }
    $args = apply_filters('get_terms_args', $args, $taxonomies);
    extract($args, EXTR_SKIP);
    if ($child_of) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$child_of])) {
            return $empty_array;
        }
    }
    if ($parent) {
        $hierarchy = _get_term_hierarchy(reset($taxonomies));
        if (!isset($hierarchy[$parent])) {
            return $empty_array;
        }
    }
    // $args can be whatever, only use the args defined in defaults to compute the key
    $filter_key = has_filter('list_terms_exclusions') ? serialize($GLOBALS['wp_filter']['list_terms_exclusions']) : '';
    $key = md5(serialize(compact(array_keys($defaults))) . serialize($taxonomies) . $filter_key);
    $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) {
        $cache = apply_filters('get_terms', $cache, $taxonomies, $args);
        return $cache;
    }
    $_orderby = strtolower($orderby);
    if ('count' == $_orderby) {
        $orderby = 'tt.count';
    } else if ('name' == $_orderby) {
        $orderby = 't.name';
    } else if ('slug' == $_orderby) {
        $orderby = 't.slug';
    } else if ('term_group' == $_orderby) {
        $orderby = 't.term_group';
    } else if ('none' == $_orderby) {
        $orderby = '';
    } elseif (empty($_orderby) || 'id' == $_orderby) {
        $orderby = 't.term_id';
    } else {
        $orderby = 't.name';
    }
    $orderby = apply_filters('get_terms_orderby', $orderby, $args, $taxonomies);
    if (!empty($orderby)) {
        $orderby = "ORDER BY {$orderby}";
    } else {
        $order = '';
    }
    $order = strtoupper($order);
    if ('' !== $order && !in_array($order, array('ASC', 'DESC'))) {
        $order = 'ASC';
    }
    $where = "tt.taxonomy IN ('" . implode("', '", $taxonomies) . "')";
    $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 = '';
    if (!empty($exclude_tree)) {
        $exclude_tree = wp_parse_id_list($exclude_tree);
        $excluded_children = array();
        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 = implode(',', array_map('intval', $excluded_children));
    }
    if (!empty($exclude)) {
        $exterms = wp_parse_id_list($exclude);
        if (empty($exclusions)) {
            $exclusions = implode(',', $exterms);
        } else {
            $exclusions .= ', ' . implode(',', $exterms);
        }
    }
    if (!empty($exclusions)) {
        $exclusions = ' AND t.term_id NOT IN (' . $exclusions . ')';
    }
    $exclusions = apply_filters('list_terms_exclusions', $exclusions, $args, $taxonomies);
    if (!empty($exclusions)) {
        $where .= $exclusions;
    }
    if (!empty($slug)) {
        $slug = sanitize_title($slug);
        $where .= " AND t.slug = '{$slug}'";
    }
    if (!empty($name__like)) {
        $name__like = like_escape($name__like);
        $where .= $wpdb->prepare(" AND t.name LIKE %s", '%' . $name__like . '%');
    }
    if (!empty($description__like)) {
        $description__like = like_escape($description__like);
        $where .= $wpdb->prepare(" AND tt.description LIKE %s", '%' . $description__like . '%');
    }
    if ('' !== $parent) {
        $parent = (int) $parent;
        $where .= " AND tt.parent = '{$parent}'";
    }
    if ('count' == $fields) {
        $hierarchical = false;
    }
    if ($hide_empty && !$hierarchical) {
        $where .= ' AND tt.count > 0';
    }
    // 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($search)) {
        $search = like_escape($search);
        $where .= $wpdb->prepare(' AND ((t.name LIKE %s) OR (t.slug LIKE %s))', '%' . $search . '%', '%' . $search . '%');
    }
    $selects = array();
    switch ($fields) {
        case 'all':
            $selects = array('t.*', 'tt.*');
            break;
        case 'ids':
        case 'id=>parent':
            $selects = array('t.term_id', 'tt.parent', 'tt.count');
            break;
        case 'names':
            $selects = array('t.term_id', 'tt.parent', 'tt.count', 't.name');
            break;
        case 'count':
            $orderby = '';
            $order = '';
            $selects = array('COUNT(*)');
            break;
        case 'id=>name':
            $selects = array('t.term_id', 't.name');
            break;
        case 'id=>slug':
            $selects = array('t.term_id', 't.slug');
            break;
    }
    $_fields = $fields;
    $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', 'orderby', 'order', 'limits');
    $clauses = apply_filters('terms_clauses', compact($pieces), $taxonomies, $args);
    foreach ($pieces as $piece) {
        ${$piece} = isset($clauses[$piece]) ? $clauses[$piece] : '';
    }
    $query = "SELECT {$fields} FROM {$wpdb->terms} AS t {$join} WHERE {$where} {$orderby} {$order} {$limits}";
    $fields = $_fields;
    if ('count' == $fields) {
        $term_count = $wpdb->get_var($query);
        return $term_count;
    }
    $terms = $wpdb->get_results($query);
    if ('all' == $fields) {
        update_term_cache($terms);
    }
    if (empty($terms)) {
        wp_cache_add($cache_key, array(), 'terms', DAY_IN_SECONDS);
        $terms = apply_filters('get_terms', array(), $taxonomies, $args);
        return $terms;
    }
    if ($child_of) {
        $children = _get_term_hierarchy(reset($taxonomies));
        if (!empty($children)) {
            $terms = _get_term_children($child_of, $terms, reset($taxonomies));
        }
    }
    // Update term counts to include children.
    if ($pad_counts && 'all' == $fields) {
        _pad_term_counts($terms, reset($taxonomies));
    }
    // Make sure we show empty categories that have children.
    if ($hierarchical && $hide_empty && is_array($terms)) {
        foreach ($terms as $k => $term) {
            if (!$term->count) {
                $children = _get_term_children($term->term_id, $terms, reset($taxonomies));
                if (is_array($children)) {
                    foreach ($children as $child) {
                        if ($child->count) {
                            continue 2;
                        }
                    }
                }
                // It really is empty
                unset($terms[$k]);
            }
        }
    }
    reset($terms);
    $_terms = array();
    if ('id=>parent' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->parent;
        }
    } elseif ('ids' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->term_id;
        }
    } elseif ('names' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[] = $term->name;
        }
    } elseif ('id=>name' == $fields) {
        while ($term = array_shift($terms)) {
            $_terms[$term->term_id] = $term->name;
        }
    } elseif ('id=>slug' == $fields) {
        while ($term = array_shift($terms)) {
            $_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);
    }
    wp_cache_add($cache_key, $terms, 'terms', DAY_IN_SECONDS);
    $terms = apply_filters('get_terms', $terms, $taxonomies, $args);
    return $terms;
}