wp/wp-includes/class-wp-term-query.php
changeset 19 3d72ae0968f4
parent 18 be944660c56a
child 21 48c4eec2b7e6
--- a/wp/wp-includes/class-wp-term-query.php	Wed Sep 21 18:19:35 2022 +0200
+++ b/wp/wp-includes/class-wp-term-query.php	Tue Sep 27 16:37:53 2022 +0200
@@ -88,102 +88,108 @@
 	 * @since 4.6.0 Introduced 'term_taxonomy_id' parameter.
 	 * @since 4.7.0 Introduced 'object_ids' parameter.
 	 * @since 4.9.0 Added 'slug__in' support for 'orderby'.
+	 * @since 5.1.0 Introduced the 'meta_compare_key' parameter.
+	 * @since 5.3.0 Introduced the 'meta_type_key' parameter.
 	 *
 	 * @param string|array $query {
 	 *     Optional. Array or query string of term query parameters. Default empty.
 	 *
-	 *     @type string|array $taxonomy               Taxonomy name, or array of taxonomies, to which results should
-	 *                                                be limited.
-	 *     @type int|int[]    $object_ids             Optional. Object ID, or array of object IDs. Results will be
-	 *                                                limited to terms associated with these objects.
-	 *     @type string       $orderby                Field(s) to order terms by. Accepts:
-	 *                                                * term fields ('name', 'slug', 'term_group', 'term_id', 'id',
-	 *                                                  'description', 'parent', 'term_order'). Unless `$object_ids`
-	 *                                                  is not empty, 'term_order' is treated the same as 'term_id'.
-	 *                                                * 'count' to use the number of objects associated with the term.
-	 *                                                * 'include' to match the 'order' of the $include param.
-	 *                                                * 'slug__in' to match the 'order' of the $slug param.
-	 *                                                * 'meta_value', 'meta_value_num'.
-	 *                                                  the value of `$meta_key`.
-	 *                                                  the array keys of `$meta_query`.
-	 *                                                * '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 int[]|string $include                Array or comma/space-separated string of term IDs to include.
-	 *                                                Default empty array.
-	 *     @type int[]|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 int[]|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). Note that $number may
-	 *                                                not return accurate results when coupled with $object_ids.
-	 *                                                See #41796 for details.
-	 *     @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 (`WP_Term[]`).
-	 *                                                * 'all_with_object_id' Returns an array of term objects
-	 *                                                  with the 'object_id' param (`WP_Term[]`). Works only
-	 *                                                  when the `$object_ids` parameter is populated.
-	 *                                                * 'ids' Returns an array of term IDs (`int[]`).
-	 *                                                * 'tt_ids' Returns an array of term taxonomy IDs (`int[]`).
-	 *                                                * 'names' Returns an array of term names (`string[]`).
-	 *                                                * 'slugs' Returns an array of term slugs (`string[]`).
-	 *                                                * 'count' Returns the number of matching terms (`int`).
-	 *                                                * 'id=>parent' Returns an associative array of parent term IDs,
-	 *                                                   keyed by term ID (`int[]`).
-	 *                                                * 'id=>name' Returns an associative array of term names,
-	 *                                                   keyed by term ID (`string[]`).
-	 *                                                * 'id=>slug' Returns an associative array of term slugs,
-	 *                                                   keyed by term ID (`string[]`).
-	 *                                                Default 'all'.
-	 *     @type bool         $count                  Whether to return a term count. If true, will take precedence
-	 *                                                over `$fields`. Default false.
-	 *     @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 int|int[]    $term_taxonomy_id       Optional. Term taxonomy ID, or array of term taxonomy IDs,
-	 *                                                to match when querying terms.
-	 *     @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             Optional. 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`. Default empty.
-	 *     @type string       $meta_value             Limit terms to those matching a specific metadata value.
-	 *                                                Usually used in conjunction with `$meta_key`. Default empty.
-	 *     @type string       $meta_type              MySQL data type that the `$meta_value` will be CAST to for
-	 *                                                comparisons. Default empty.
-	 *     @type string       $meta_compare           Comparison operator to test the 'meta_value'. Default empty.
+	 *     @type string|string[] $taxonomy               Taxonomy name, or array of taxonomy names, to which results
+	 *                                                   should be limited.
+	 *     @type int|int[]       $object_ids             Object ID, or array of object IDs. Results will be
+	 *                                                   limited to terms associated with these objects.
+	 *     @type string          $orderby                Field(s) to order terms by. Accepts:
+	 *                                                   - Term fields ('name', 'slug', 'term_group', 'term_id', 'id',
+	 *                                                     'description', 'parent', 'term_order'). Unless `$object_ids`
+	 *                                                     is not empty, 'term_order' is treated the same as 'term_id'.
+	 *                                                   - 'count' to use the number of objects associated with the term.
+	 *                                                   - 'include' to match the 'order' of the `$include` param.
+	 *                                                   - 'slug__in' to match the 'order' of the `$slug` param.
+	 *                                                   - 'meta_value'
+	 *                                                   - 'meta_value_num'.
+	 *                                                   - The value of `$meta_key`.
+	 *                                                   - The array keys of `$meta_query`.
+	 *                                                   - 'none' to omit the ORDER BY clause.
+	 *                                                   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 int[]|string    $include                Array or comma/space-separated string of term IDs to include.
+	 *                                                   Default empty array.
+	 *     @type int[]|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 int[]|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). Note that `$number` may
+	 *                                                   not return accurate results when coupled with `$object_ids`.
+	 *                                                   See #41796 for details.
+	 *     @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 (`WP_Term[]`).
+	 *                                                   - 'all_with_object_id' Returns an array of term objects
+	 *                                                     with the 'object_id' param (`WP_Term[]`). Works only
+	 *                                                     when the `$object_ids` parameter is populated.
+	 *                                                   - 'ids' Returns an array of term IDs (`int[]`).
+	 *                                                   - 'tt_ids' Returns an array of term taxonomy IDs (`int[]`).
+	 *                                                   - 'names' Returns an array of term names (`string[]`).
+	 *                                                   - 'slugs' Returns an array of term slugs (`string[]`).
+	 *                                                   - 'count' Returns the number of matching terms (`int`).
+	 *                                                   - 'id=>parent' Returns an associative array of parent term IDs,
+	 *                                                      keyed by term ID (`int[]`).
+	 *                                                   - 'id=>name' Returns an associative array of term names,
+	 *                                                      keyed by term ID (`string[]`).
+	 *                                                   - 'id=>slug' Returns an associative array of term slugs,
+	 *                                                      keyed by term ID (`string[]`).
+	 *                                                   Default 'all'.
+	 *     @type bool            $count                  Whether to return a term count. If true, will take precedence
+	 *                                                   over `$fields`. Default false.
+	 *     @type string|string[] $name                   Name or array of names to return term(s) for.
+	 *                                                   Default empty.
+	 *     @type string|string[] $slug                   Slug or array of slugs to return term(s) for.
+	 *                                                   Default empty.
+	 *     @type int|int[]       $term_taxonomy_id       Term taxonomy ID, or array of term taxonomy IDs,
+	 *                                                   to match when querying terms.
+	 *     @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 '' (disabled).
+	 *                                                   Default ''.
+	 *     @type int             $child_of               Term ID to retrieve child terms of. If multiple taxonomies
+	 *                                                   are passed, `$child_of` is ignored. Default 0.
+	 *     @type int             $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 'core'.
+	 *     @type bool            $update_term_meta_cache Whether to prime meta caches for matched terms. Default true.
+	 *     @type string|string[] $meta_key               Meta key or keys to filter by.
+	 *     @type string|string[] $meta_value             Meta value or values to filter by.
+	 *     @type string          $meta_compare           MySQL operator used for comparing the meta value.
+	 *                                                   See WP_Meta_Query::__construct for accepted values and default value.
+	 *     @type string          $meta_compare_key       MySQL operator used for comparing the meta key.
+	 *                                                   See WP_Meta_Query::__construct for accepted values and default value.
+	 *     @type string          $meta_type              MySQL data type that the meta_value column will be CAST to for comparisons.
+	 *                                                   See WP_Meta_Query::__construct for accepted values and default value.
+	 *     @type string          $meta_type_key          MySQL data type that the meta_key column will be CAST to for comparisons.
+	 *                                                   See WP_Meta_Query::__construct for accepted values and default value.
+	 *     @type array           $meta_query             An associative array of WP_Meta_Query arguments.
+	 *                                                   See WP_Meta_Query::__construct for accepted values.
 	 * }
 	 */
 	public function __construct( $query = '' ) {
@@ -279,7 +285,7 @@
 		 *
 		 * @since 4.6.0
 		 *
-		 * @param WP_Term_Query $this Current instance of WP_Term_Query.
+		 * @param WP_Term_Query $query Current instance of WP_Term_Query.
 		 */
 		do_action( 'parse_term_query', $this );
 	}
@@ -353,7 +359,7 @@
 		 *
 		 * @since 4.6.0
 		 *
-		 * @param WP_Term_Query $this Current instance of WP_Term_Query (passed by reference).
+		 * @param WP_Term_Query $query Current instance of WP_Term_Query (passed by reference).
 		 */
 		do_action_ref_array( 'pre_get_terms', array( &$this ) );
 
@@ -447,7 +453,16 @@
 		$order = $this->parse_order( $this->query_vars['order'] );
 
 		if ( $taxonomies ) {
-			$this->sql_clauses['where']['taxonomy'] = "tt.taxonomy IN ('" . implode( "', '", array_map( 'esc_sql', $taxonomies ) ) . "')";
+			$this->sql_clauses['where']['taxonomy'] =
+				"tt.taxonomy IN ('" . implode( "', '", array_map( 'esc_sql', $taxonomies ) ) . "')";
+		}
+
+		if ( empty( $args['exclude'] ) ) {
+			$args['exclude'] = array();
+		}
+
+		if ( empty( $args['include'] ) ) {
+			$args['include'] = array();
 		}
 
 		$exclude      = $args['exclude'];
@@ -520,11 +535,14 @@
 			$this->sql_clauses['where']['exclusions'] = preg_replace( '/^\s*AND\s*/', '', $exclusions );
 		}
 
-		if (
-			( ! empty( $args['name'] ) ) ||
-			( is_string( $args['name'] ) && 0 !== strlen( $args['name'] ) )
-		) {
-			$names = (array) $args['name'];
+		if ( '' === $args['name'] ) {
+			$args['name'] = array();
+		} else {
+			$args['name'] = (array) $args['name'];
+		}
+
+		if ( ! empty( $args['name'] ) ) {
+			$names = $args['name'];
 			foreach ( $names as &$_name ) {
 				// `sanitize_term_field()` returns slashed data.
 				$_name = stripslashes( sanitize_term_field( 'name', $_name, 0, reset( $taxonomies ), 'db' ) );
@@ -533,43 +551,53 @@
 			$this->sql_clauses['where']['name'] = "t.name IN ('" . implode( "', '", array_map( 'esc_sql', $names ) ) . "')";
 		}
 
-		if (
-			( ! empty( $args['slug'] ) ) ||
-			( is_string( $args['slug'] ) && 0 !== strlen( $args['slug'] ) )
-		) {
-			if ( is_array( $args['slug'] ) ) {
-				$slug                               = array_map( 'sanitize_title', $args['slug'] );
-				$this->sql_clauses['where']['slug'] = "t.slug IN ('" . implode( "', '", $slug ) . "')";
-			} else {
-				$slug                               = sanitize_title( $args['slug'] );
-				$this->sql_clauses['where']['slug'] = "t.slug = '$slug'";
-			}
+		if ( '' === $args['slug'] ) {
+			$args['slug'] = array();
+		} else {
+			$args['slug'] = array_map( 'sanitize_title', (array) $args['slug'] );
+		}
+
+		if ( ! empty( $args['slug'] ) ) {
+			$slug = implode( "', '", $args['slug'] );
+
+			$this->sql_clauses['where']['slug'] = "t.slug IN ('" . $slug . "')";
+		}
+
+		if ( '' === $args['term_taxonomy_id'] ) {
+			$args['term_taxonomy_id'] = array();
+		} else {
+			$args['term_taxonomy_id'] = array_map( 'intval', (array) $args['term_taxonomy_id'] );
 		}
 
 		if ( ! empty( $args['term_taxonomy_id'] ) ) {
-			if ( is_array( $args['term_taxonomy_id'] ) ) {
-				$tt_ids = implode( ',', array_map( 'intval', $args['term_taxonomy_id'] ) );
-				$this->sql_clauses['where']['term_taxonomy_id'] = "tt.term_taxonomy_id IN ({$tt_ids})";
-			} else {
-				$this->sql_clauses['where']['term_taxonomy_id'] = $wpdb->prepare( 'tt.term_taxonomy_id = %d', $args['term_taxonomy_id'] );
-			}
+			$tt_ids = implode( ',', $args['term_taxonomy_id'] );
+
+			$this->sql_clauses['where']['term_taxonomy_id'] = "tt.term_taxonomy_id IN ({$tt_ids})";
 		}
 
 		if ( ! empty( $args['name__like'] ) ) {
-			$this->sql_clauses['where']['name__like'] = $wpdb->prepare( 't.name LIKE %s', '%' . $wpdb->esc_like( $args['name__like'] ) . '%' );
+			$this->sql_clauses['where']['name__like'] = $wpdb->prepare(
+				't.name LIKE %s',
+				'%' . $wpdb->esc_like( $args['name__like'] ) . '%'
+			);
 		}
 
 		if ( ! empty( $args['description__like'] ) ) {
-			$this->sql_clauses['where']['description__like'] = $wpdb->prepare( 'tt.description LIKE %s', '%' . $wpdb->esc_like( $args['description__like'] ) . '%' );
+			$this->sql_clauses['where']['description__like'] = $wpdb->prepare(
+				'tt.description LIKE %s',
+				'%' . $wpdb->esc_like( $args['description__like'] ) . '%'
+			);
+		}
+
+		if ( '' === $args['object_ids'] ) {
+			$args['object_ids'] = array();
+		} else {
+			$args['object_ids'] = array_map( 'intval', (array) $args['object_ids'] );
 		}
 
 		if ( ! empty( $args['object_ids'] ) ) {
-			$object_ids = $args['object_ids'];
-			if ( ! is_array( $object_ids ) ) {
-				$object_ids = array( $object_ids );
-			}
+			$object_ids = implode( ', ', $args['object_ids'] );
 
-			$object_ids                               = implode( ', ', array_map( 'intval', $object_ids ) );
 			$this->sql_clauses['where']['object_ids'] = "tr.object_id IN ($object_ids)";
 		}
 
@@ -630,32 +658,16 @@
 
 		$selects = array();
 		switch ( $args['fields'] ) {
-			case 'all':
-			case 'all_with_object_id':
-			case 'tt_ids':
-			case 'slugs':
-				$selects = array( 't.*', 'tt.*' );
-				if ( 'all_with_object_id' === $args['fields'] && ! empty( $args['object_ids'] ) ) {
-					$selects[] = 'tr.object_id';
-				}
-				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.parent', 'tt.count', 'tt.taxonomy' );
-				break;
-			case 'id=>slug':
-				$selects = array( 't.term_id', 't.slug', 'tt.parent', 'tt.count', 'tt.taxonomy' );
+			default:
+				$selects = array( 't.term_id' );
+				if ( 'all_with_object_id' === $args['fields'] && ! empty( $args['object_ids'] ) ) {
+					$selects[] = 'tr.object_id';
+				}
 				break;
 		}
 
@@ -682,21 +694,34 @@
 		$join .= " INNER JOIN $wpdb->term_taxonomy AS tt ON t.term_id = tt.term_id";
 
 		if ( ! empty( $this->query_vars['object_ids'] ) ) {
-			$join .= " INNER JOIN {$wpdb->term_relationships} AS tr ON tr.term_taxonomy_id = tt.term_taxonomy_id";
+			$join    .= " INNER JOIN {$wpdb->term_relationships} AS tr ON tr.term_taxonomy_id = tt.term_taxonomy_id";
+			$distinct = 'DISTINCT';
 		}
 
 		$where = implode( ' AND ', $this->sql_clauses['where'] );
 
+		$clauses = array( 'fields', 'join', 'where', 'distinct', 'orderby', 'order', 'limits' );
+
 		/**
 		 * Filters the terms query SQL clauses.
 		 *
 		 * @since 3.1.0
 		 *
-		 * @param string[] $pieces     Array of query SQL clauses.
+		 * @param string[] $clauses {
+		 *     Associative array of the clauses for the query.
+		 *
+		 *     @type string $fields   The SELECT clause of the query.
+		 *     @type string $join     The JOIN clause of the query.
+		 *     @type string $where    The WHERE clause of the query.
+		 *     @type string $distinct The DISTINCT clause of the query.
+		 *     @type string $orderby  The ORDER BY clause of the query.
+		 *     @type string $order    The ORDER clause of the query.
+		 *     @type string $limits   The LIMIT clause of the query.
+		 * }
 		 * @param string[] $taxonomies An array of taxonomy names.
 		 * @param array    $args       An array of term query arguments.
 		 */
-		$clauses = apply_filters( 'terms_clauses', compact( 'fields', 'join', 'where', 'distinct', 'orderby', 'order', 'limits' ), $taxonomies, $args );
+		$clauses = apply_filters( 'terms_clauses', compact( $clauses ), $taxonomies, $args );
 
 		$fields   = isset( $clauses['fields'] ) ? $clauses['fields'] : '';
 		$join     = isset( $clauses['join'] ) ? $clauses['join'] : '';
@@ -715,7 +740,13 @@
 		$this->sql_clauses['orderby'] = $orderby ? "$orderby $order" : '';
 		$this->sql_clauses['limits']  = $limits;
 
-		$this->request = "{$this->sql_clauses['select']} {$this->sql_clauses['from']} {$where} {$this->sql_clauses['orderby']} {$this->sql_clauses['limits']}";
+		$this->request = "
+			{$this->sql_clauses['select']}
+			{$this->sql_clauses['from']}
+			{$where}
+			{$this->sql_clauses['orderby']}
+			{$this->sql_clauses['limits']}
+		";
 
 		$this->terms = null;
 
@@ -737,13 +768,31 @@
 		}
 
 		// $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( $this->query_var_defaults ) ) ) . serialize( $taxonomies ) . $this->request );
+		$cache_args = wp_array_slice_assoc( $args, array_keys( $this->query_var_defaults ) );
+
+		unset( $cache_args['update_term_meta_cache'] );
+
+		if ( 'count' !== $_fields && 'all_with_object_id' !== $_fields ) {
+			$cache_args['fields'] = 'all';
+		}
+
+		$key          = md5( serialize( $cache_args ) . serialize( $taxonomies ) . $this->request );
 		$last_changed = wp_cache_get_last_changed( 'terms' );
 		$cache_key    = "get_terms:$key:$last_changed";
 		$cache        = wp_cache_get( $cache_key, 'terms' );
+
 		if ( false !== $cache ) {
-			if ( 'all' === $_fields || 'all_with_object_id' === $_fields ) {
-				$cache = $this->populate_terms( $cache );
+			if ( 'ids' === $_fields ) {
+				$cache = array_map( 'intval', $cache );
+			} elseif ( 'count' !== $_fields ) {
+				if ( ( 'all_with_object_id' === $_fields && ! empty( $args['object_ids'] ) ) || ( 'all' === $_fields && $args['pad_counts'] ) ) {
+					$term_ids = wp_list_pluck( $cache, 'term_id' );
+				} else {
+					$term_ids = array_map( 'intval', $cache );
+				}
+				_prime_term_caches( $term_ids, $args['update_term_meta_cache'] );
+				$term_objects = $this->populate_terms( $cache );
+				$cache        = $this->format_terms( $term_objects, $_fields );
 			}
 
 			$this->terms = $cache;
@@ -751,33 +800,27 @@
 		}
 
 		if ( 'count' === $_fields ) {
-			$count = $wpdb->get_var( $this->request );
+			$count = $wpdb->get_var( $this->request ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
 			wp_cache_set( $cache_key, $count, 'terms' );
 			return $count;
 		}
 
-		$terms = $wpdb->get_results( $this->request );
+		$terms = $wpdb->get_results( $this->request ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
 
-		if ( 'all' === $_fields || 'all_with_object_id' === $_fields ) {
-			update_term_cache( $terms );
+		if ( empty( $terms ) ) {
+			wp_cache_add( $cache_key, array(), 'terms' );
+			return array();
 		}
 
-		// 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 );
-			return array();
-		}
+		$term_ids = wp_list_pluck( $terms, 'term_id' );
+		_prime_term_caches( $term_ids, false );
+		$term_objects = $this->populate_terms( $terms );
 
 		if ( $child_of ) {
 			foreach ( $taxonomies as $_tax ) {
 				$children = _get_term_hierarchy( $_tax );
 				if ( ! empty( $children ) ) {
-					$terms = _get_term_children( $child_of, $terms, $_tax );
+					$term_objects = _get_term_children( $child_of, $term_objects, $_tax );
 				}
 			}
 		}
@@ -785,13 +828,13 @@
 		// Update term counts to include children.
 		if ( $args['pad_counts'] && 'all' === $_fields ) {
 			foreach ( $taxonomies as $_tax ) {
-				_pad_term_counts( $terms, $_tax );
+				_pad_term_counts( $term_objects, $_tax );
 			}
 		}
 
 		// Make sure we show empty categories that have children.
-		if ( $hierarchical && $args['hide_empty'] && is_array( $terms ) ) {
-			foreach ( $terms as $k => $term ) {
+		if ( $hierarchical && $args['hide_empty'] && is_array( $term_objects ) ) {
+			foreach ( $term_objects as $k => $term ) {
 				if ( ! $term->count ) {
 					$children = get_term_children( $term->term_id, $term->taxonomy );
 					if ( is_array( $children ) ) {
@@ -804,83 +847,48 @@
 					}
 
 					// It really is empty.
-					unset( $terms[ $k ] );
+					unset( $term_objects[ $k ] );
 				}
 			}
 		}
 
-		/*
-		 * When querying for terms connected to objects, we may get
-		 * duplicate results. The duplicates should be preserved if
-		 * `$fields` is 'all_with_object_id', but should otherwise be
-		 * removed.
-		 */
-		if ( ! empty( $args['object_ids'] ) && 'all_with_object_id' !== $_fields ) {
-			$_tt_ids = array();
-			$_terms  = array();
-			foreach ( $terms as $term ) {
-				if ( isset( $_tt_ids[ $term->term_id ] ) ) {
-					continue;
-				}
-
-				$_tt_ids[ $term->term_id ] = 1;
-				$_terms[]                  = $term;
-			}
-
-			$terms = $_terms;
-		}
-
-		$_terms = array();
-		if ( 'id=>parent' === $_fields ) {
-			foreach ( $terms as $term ) {
-				$_terms[ $term->term_id ] = $term->parent;
-			}
-		} elseif ( 'ids' === $_fields ) {
-			foreach ( $terms as $term ) {
-				$_terms[] = (int) $term->term_id;
-			}
-		} elseif ( 'tt_ids' === $_fields ) {
-			foreach ( $terms as $term ) {
-				$_terms[] = (int) $term->term_taxonomy_id;
-			}
-		} elseif ( 'names' === $_fields ) {
-			foreach ( $terms as $term ) {
-				$_terms[] = $term->name;
-			}
-		} elseif ( 'slugs' === $_fields ) {
-			foreach ( $terms as $term ) {
-				$_terms[] = $term->slug;
-			}
-		} 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;
+		// Hierarchical queries are not limited, so 'offset' and 'number' must be handled now.
+		if ( $hierarchical && $number && is_array( $term_objects ) ) {
+			if ( $offset >= count( $term_objects ) ) {
+				$term_objects = array();
+			} else {
+				$term_objects = array_slice( $term_objects, $offset, $number, true );
 			}
 		}
 
-		if ( ! empty( $_terms ) ) {
-			$terms = $_terms;
+		// Prime termmeta cache.
+		if ( $args['update_term_meta_cache'] ) {
+			$term_ids = wp_list_pluck( $term_objects, 'term_id' );
+			update_termmeta_cache( $term_ids );
 		}
 
-		// 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 );
+		if ( 'all_with_object_id' === $_fields && ! empty( $args['object_ids'] ) ) {
+			$term_cache = array();
+			foreach ( $term_objects as $term ) {
+				$object            = new stdClass();
+				$object->term_id   = $term->term_id;
+				$object->object_id = $term->object_id;
+				$term_cache[]      = $object;
 			}
+		} elseif ( 'all' === $_fields && $args['pad_counts'] ) {
+			$term_cache = array();
+			foreach ( $term_objects as $term ) {
+				$object          = new stdClass();
+				$object->term_id = $term->term_id;
+				$object->count   = $term->count;
+				$term_cache[]    = $object;
+			}
+		} else {
+			$term_cache = wp_list_pluck( $term_objects, 'term_id' );
 		}
-
-		wp_cache_add( $cache_key, $terms, 'terms', DAY_IN_SECONDS );
+		wp_cache_add( $cache_key, $term_cache, 'terms' );
+		$this->terms = $this->format_terms( $term_objects, $_fields );
 
-		if ( 'all' === $_fields || 'all_with_object_id' === $_fields ) {
-			$terms = $this->populate_terms( $terms );
-		}
-
-		$this->terms = $terms;
 		return $this->terms;
 	}
 
@@ -944,6 +952,53 @@
 	}
 
 	/**
+	 * Format response depending on field requested.
+	 *
+	 * @since 6.0.0
+	 *
+	 * @param WP_Term[] $term_objects Array of term objects.
+	 * @param string    $_fields      Field to format.
+	 *
+	 * @return WP_Term[]|int[]|string[] Array of terms / strings / ints depending on field requested.
+	 */
+	protected function format_terms( $term_objects, $_fields ) {
+		$_terms = array();
+		if ( 'id=>parent' === $_fields ) {
+			foreach ( $term_objects as $term ) {
+				$_terms[ $term->term_id ] = $term->parent;
+			}
+		} elseif ( 'ids' === $_fields ) {
+			foreach ( $term_objects as $term ) {
+				$_terms[] = (int) $term->term_id;
+			}
+		} elseif ( 'tt_ids' === $_fields ) {
+			foreach ( $term_objects as $term ) {
+				$_terms[] = (int) $term->term_taxonomy_id;
+			}
+		} elseif ( 'names' === $_fields ) {
+			foreach ( $term_objects as $term ) {
+				$_terms[] = $term->name;
+			}
+		} elseif ( 'slugs' === $_fields ) {
+			foreach ( $term_objects as $term ) {
+				$_terms[] = $term->slug;
+			}
+		} elseif ( 'id=>name' === $_fields ) {
+			foreach ( $term_objects as $term ) {
+				$_terms[ $term->term_id ] = $term->name;
+			}
+		} elseif ( 'id=>slug' === $_fields ) {
+			foreach ( $term_objects as $term ) {
+				$_terms[ $term->term_id ] = $term->slug;
+			}
+		} elseif ( 'all' === $_fields || 'all_with_object_id' === $_fields ) {
+			$_terms = $term_objects;
+		}
+
+		return $_terms;
+	}
+
+	/**
 	 * Generate the ORDER BY clause for an 'orderby' param that is potentially related to a meta query.
 	 *
 	 * @since 4.6.0
@@ -1029,13 +1084,13 @@
 	 *
 	 * @global wpdb $wpdb WordPress database abstraction object.
 	 *
-	 * @param string $string
-	 * @return string
+	 * @param string $search Search string.
+	 * @return string Search SQL.
 	 */
-	protected function get_search_sql( $string ) {
+	protected function get_search_sql( $search ) {
 		global $wpdb;
 
-		$like = '%' . $wpdb->esc_like( $string ) . '%';
+		$like = '%' . $wpdb->esc_like( $search ) . '%';
 
 		return $wpdb->prepare( '((t.name LIKE %s) OR (t.slug LIKE %s))', $like, $like );
 	}
@@ -1047,23 +1102,33 @@
 	 *
 	 * @since 4.9.8
 	 *
-	 * @param array $term_ids Term IDs.
-	 * @return array
+	 * @param Object[]|int[] $terms List of objects or term ids.
+	 * @return WP_Term[] Array of `WP_Term` objects.
 	 */
-	protected function populate_terms( $term_ids ) {
-		$terms = array();
-
-		if ( ! is_array( $term_ids ) ) {
-			return $terms;
+	protected function populate_terms( $terms ) {
+		$term_objects = array();
+		if ( ! is_array( $terms ) ) {
+			return $term_objects;
 		}
 
-		foreach ( $term_ids as $key => $term_id ) {
-			$term = get_term( $term_id );
+		foreach ( $terms as $key => $term_data ) {
+			if ( is_object( $term_data ) && property_exists( $term_data, 'term_id' ) ) {
+				$term = get_term( $term_data->term_id );
+				if ( property_exists( $term_data, 'object_id' ) ) {
+					$term->object_id = (int) $term_data->object_id;
+				}
+				if ( property_exists( $term_data, 'count' ) ) {
+					$term->count = (int) $term_data->count;
+				}
+			} else {
+				$term = get_term( $term_data );
+			}
+
 			if ( $term instanceof WP_Term ) {
-				$terms[ $key ] = $term;
+				$term_objects[ $key ] = $term;
 			}
 		}
 
-		return $terms;
+		return $term_objects;
 	}
 }