wppaste
WordPress

get_terms( array|string $args = array(), array|string $deprecated = '' ): WP_Term[]|int[]|string[]|string|WP_Error

Since
2.3.0, 4.2.0, 4.4.0, 4.5.0, 4.8.0
Source
wp-includes/taxonomy.php:1316

Queries one or more taxonomies for matching term objects by passing an $args array whose 'taxonomy' key names the taxonomy or taxonomies to search. It wraps WP_Term_Query::query(), accepts the same arguments (hide_empty, orderby, include, and more), and returns a WP_Error if any requested taxonomy is not registered. A legacy two-parameter calling style ($taxonomy, $args) is still supported for old code, which is detected automatically based on the shape of the first argument.

Retrieves the terms in a given taxonomy or list of taxonomies.

Description

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 '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.

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().

Compatibility

WordPress
since 4.8.0
PHP
7.4–8.6-dev
  • 6.7.7
  • 6.8.8
  • 6.9.7
  • 7.0.4
  • 7.1.0

Present in every tracked release (6.7.7 to 7.1.0), and compiles on PHP 7.4 through 8.6-dev.

Parameters

$argsarray|stringoptional
Array or string of arguments. See WP_Term_Query::__construct() for information on accepted arguments. Default empty array.Default: array()
$deprecatedarray|stringoptional
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.Default: ''

Return value

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.

Code examples

Every example is editable and runs in a real WordPress booted in your browser by WordPress Playground. Press Run, then edit the code: clicking away re-runs it. Nothing is sent anywhere until you do.

List all category and tag terms with their post counts

Pull every category and tag registered on the site, including empty ones, and print their names and counts.

$terms = get_terms(
	array(
		'taxonomy'   => array( 'category', 'post_tag' ),
		'hide_empty' => false,
	)
);

if ( is_wp_error( $terms ) ) {
	echo esc_html( $terms->get_error_message() );
} else {
	foreach ( $terms as $term ) {
		echo esc_html( $term->taxonomy . ': ' . $term->name . ' (' . $term->count . ')' ) . '<br>';
	}
}

Fetch category terms using the old two-argument signature

Migrate a pre-4.5.0 call that passed the taxonomy name first and the query args second.

$categories = get_terms(
	'category',
	array(
		'hide_empty' => false,
		'orderby'    => 'name',
	)
);

if ( is_wp_error( $categories ) ) {
	echo esc_html( $categories->get_error_message() );
} else {
	foreach ( $categories as $category ) {
		echo esc_html( $category->name ) . '<br>';
	}
}

This legacy form still works because a non-empty second parameter forces get_terms() onto the old calling convention.

Common problems and fixes · 4

Why does get_terms() come back empty even though I created the terms?

WP_Term_Query, which get_terms() delegates to, defaults to hiding terms whose post count is zero. If the term has not been attached to any published post yet, it is excluded before your foreach ever runs. - Pass 'hide_empty' => false in $args - Or attach the term to a post first with wp_set_object_terms()

Why did get_terms() return a WP_Error instead of a list of terms?

get_terms() loops over every taxonomy in $args['taxonomy'] and calls taxonomy_exists() on each one; the first taxonomy that is not registered triggers an 'invalid_taxonomy' WP_Error. This usually means a typo in the taxonomy slug or a custom taxonomy that has not been registered yet on this request. - Always check is_wp_error( $terms ) before looping - Confirm the custom taxonomy's register_taxonomy() call runs on every page load, not just once

Why doesn't my 'get_terms' filter callback ever run?

Two things in the function body can skip it: setting $args['suppress_filter'] to true returns the raw terms before the filter fires, and the source explicitly skips the filter when the query returns a plain count instead of an array ('Count queries are not filtered, for legacy reasons'). Check whether you passed 'fields' => 'count' or 'suppress_filter' => true.

Why did an array I passed as the first argument get treated as a list of taxonomies instead of as $args?

get_terms() decides between the modern and legacy call signatures by intersecting the keys of your first argument with WP_Term_Query's own query var names. If none of your keys match (for example you passed a plain array of taxonomy slugs, or a typo'd key like 'taxononmy'), it assumes the old ($taxonomy, $args) form and casts the whole array to taxonomies. - Use recognized WP_Term_Query keys such as 'taxonomy', 'hide_empty', or 'orderby' in your $args array - Double check argument key spelling before assuming a bug in get_terms()

Alternatives and related functions

get_term
When you already know a single term's ID and just need that one term object instead of running a query.
wp_get_post_terms
When you need the terms assigned to one specific post rather than every term in a taxonomy.
get_categories
When you specifically want category terms and prefer a shorthand wrapper instead of setting 'taxonomy' yourself.
WP_Term_Query
When you want direct access to the query object, for example to inspect query_vars or reuse the same query multiple times.

Performance profile

How much work a call to get_terms() does, and what it touches: the algorithmic scaling, the Zend instruction count per call across PHP versions, the hooks it hands control to, and the core code that calls it. Measured from the compiled opcodes, not a stopwatch, so every number is identical on any machine running the same PHP version, and every function in core is ranked by cost.

Cost class
Heavy

Reaches the database via ->query().

Scaling
Scales with input

The body loops, so the work grows with what you pass in.

Instructions
38–63

Executed per call on PHP 8.5, depending on the branch taken. The body compiles to 87.

Plugin surface
1 hook

Third-party callbacks on 'get_terms' run inside this call, and their cost is not bounded by anything here.

Called by
33

33 places in core call this, so the cost is paid more often than your own code shows.

What it touches

  • querycontent queryget_terms()this function does it
  • hookthird-party callbacksapply_filters()called directly
  • sqldatabase query->query()called directly

Further down the call graph this can also reach option, cache, serialize and transient. Those are the worst case, several calls deep and usually down an error path, not what a normal call pays.

What one call costs · 3 distinct outcomes

One number would be a lie: the work depends on which branch runs. These are every distinct cost get_terms() can have, taken from its control-flow graph on PHP 8.5.

WhenInstructionsCalls it makes
always38–51wp_parse_args(), array_intersect_key(), wp_parse_args(), ->query()
!empty($args) && !$args && !taxonomy_exists()45–53wp_parse_args(), array_intersect_key(), wp_parse_args(), taxonomy_exists(), __()
is_array($terms) && !$args51–63wp_parse_args(), array_intersect_key(), wp_parse_args(), ->query(), apply_filters()

Across PHP versions

PHPCompiledExecutedBranchesNotes
8.6-dev8738–6310
8.58738–6310
8.48738–6310
8.38738–6310
8.28738–6310
8.18738–63101 fewer instruction than PHP 7.4
7.48838–6410

An instruction is not a fixed amount of time, so a matching count is not necessarily the same speed; what it rules out is a difference in the work itself.

Hooks and filters fired · 1

One hook fires while get_terms() runs, in this order:

  1. apply_filters( get_terms )filterline 1379 (+63 into the body)

    Filters the found terms.

Uses · 6

Used by · 33

Show all 33

Source code

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 );}

Changelog

Introduced in 4.8.0. Unchanged from 6.7.7 through 7.1.0.

  1. 6.7.7
  2. 6.8.8
  3. 6.9.7
  4. 7.0.4
  5. 7.1.0

Signature, return type and hooks compared across 5 parsed releases.

4.8.0
Introduced 'suppress_filter' parameter.from the docblock
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.from the docblock
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.from the docblock
4.2.0
Introduced 'name' and 'childless' parameters.from the docblock
2.3.0
Introduced.from the docblock

About this page

Parsed data
Generated from the wordpress-develop 6.8.8 tag, from src/wp-includes/taxonomy.php, and regenerated for each WordPress release so it tracks the code rather than a snapshot of it.
Corrections
Something wrong on this page? Report it and it gets fixed in the next regeneration.