--- a/wp/wp-includes/class-wp-term-query.php Thu Sep 29 08:06:27 2022 +0200
+++ b/wp/wp-includes/class-wp-term-query.php Fri Sep 05 18:40:08 2025 +0200
@@ -15,6 +15,7 @@
*
* @see WP_Term_Query::__construct() for accepted arguments.
*/
+#[AllowDynamicProperties]
class WP_Term_Query {
/**
@@ -90,6 +91,7 @@
* @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.
+ * @since 6.4.0 Introduced the 'cache_results' parameter.
*
* @param string|array $query {
* Optional. Array or query string of term query parameters. Default empty.
@@ -177,19 +179,20 @@
* 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 $cache_results Whether to cache term information. Default true.
* @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.
+ * 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.
+ * 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.
+ * 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.
+ * 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.
+ * See WP_Meta_Query::__construct() for accepted values.
* }
*/
public function __construct( $query = '' ) {
@@ -219,6 +222,7 @@
'parent' => '',
'childless' => false,
'cache_domain' => 'core',
+ 'cache_results' => true,
'update_term_meta_cache' => true,
'meta_query' => '',
'meta_key' => '',
@@ -237,7 +241,7 @@
*
* @since 4.6.0
*
- * @param string|array $query WP_Term_Query arguments. See WP_Term_Query::__construct()
+ * @param string|array $query WP_Term_Query arguments. See WP_Term_Query::__construct() for accepted arguments.
*/
public function parse_query( $query = '' ) {
if ( empty( $query ) ) {
@@ -484,6 +488,7 @@
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,
@@ -497,6 +502,7 @@
)
);
}
+
$exclusions = array_merge( $excluded_children, $exclusions );
}
@@ -531,7 +537,7 @@
$exclusions = apply_filters( 'list_terms_exclusions', $exclusions, $args, $taxonomies );
if ( ! empty( $exclusions ) ) {
- // Must do string manipulation here for backward compatibility with filter.
+ // Strip leading 'AND'. Must do string manipulation here for backward compatibility with filter.
$this->sql_clauses['where']['exclusions'] = preg_replace( '/^\s*AND\s*/', '', $exclusions );
}
@@ -543,6 +549,7 @@
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' ) );
@@ -650,9 +657,12 @@
$meta_clauses = $this->meta_query->get_clauses();
if ( ! empty( $meta_clauses ) ) {
- $join .= $mq_sql['join'];
+ $join .= $mq_sql['join'];
+
+ // Strip leading 'AND'.
$this->sql_clauses['where']['meta_query'] = preg_replace( '/^\s*AND\s*/', '', $mq_sql['where'] );
- $distinct .= 'DISTINCT';
+
+ $distinct .= 'DISTINCT';
}
@@ -700,7 +710,7 @@
$where = implode( ' AND ', $this->sql_clauses['where'] );
- $clauses = array( 'fields', 'join', 'where', 'distinct', 'orderby', 'order', 'limits' );
+ $pieces = array( 'fields', 'join', 'where', 'distinct', 'orderby', 'order', 'limits' );
/**
* Filters the terms query SQL clauses.
@@ -721,7 +731,7 @@
* @param string[] $taxonomies An array of taxonomy names.
* @param array $args An array of term query arguments.
*/
- $clauses = apply_filters( 'terms_clauses', compact( $clauses ), $taxonomies, $args );
+ $clauses = apply_filters( 'terms_clauses', compact( $pieces ), $taxonomies, $args );
$fields = isset( $clauses['fields'] ) ? $clauses['fields'] : '';
$join = isset( $clauses['join'] ) ? $clauses['join'] : '';
@@ -731,6 +741,8 @@
$order = isset( $clauses['order'] ) ? $clauses['order'] : '';
$limits = isset( $clauses['limits'] ) ? $clauses['limits'] : '';
+ $fields_is_filtered = implode( ', ', $selects ) !== $fields;
+
if ( $where ) {
$where = "WHERE $where";
}
@@ -740,13 +752,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']}
- ";
+ // Beginning of the string is on a new line to prevent leading whitespace. See https://core.trac.wordpress.org/ticket/56841.
+ $this->request =
+ "{$this->sql_clauses['select']}
+ {$this->sql_clauses['from']}
+ {$where}
+ {$this->sql_clauses['orderby']}
+ {$this->sql_clauses['limits']}";
$this->terms = null;
@@ -767,48 +779,47 @@
return $this->terms;
}
- // $args can be anything. Only use the args defined in defaults to compute the key.
- $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 ( $args['cache_results'] ) {
+ $cache_key = $this->generate_cache_key( $args, $this->request );
+ $cache = wp_cache_get( $cache_key, 'term-queries' );
- if ( false !== $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 );
+ if ( false !== $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'] || $fields_is_filtered )
+ ) {
+ $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 );
}
- _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;
+ return $this->terms;
}
-
- $this->terms = $cache;
- return $this->terms;
}
if ( 'count' === $_fields ) {
$count = $wpdb->get_var( $this->request ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
- wp_cache_set( $cache_key, $count, 'terms' );
+ if ( $args['cache_results'] ) {
+ wp_cache_set( $cache_key, $count, 'term-queries' );
+ }
return $count;
}
$terms = $wpdb->get_results( $this->request ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
if ( empty( $terms ) ) {
- wp_cache_add( $cache_key, array(), 'terms' );
+ if ( $args['cache_results'] ) {
+ wp_cache_add( $cache_key, array(), 'term-queries' );
+ }
return array();
}
@@ -837,6 +848,7 @@
foreach ( $term_objects 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 );
@@ -864,7 +876,7 @@
// Prime termmeta cache.
if ( $args['update_term_meta_cache'] ) {
$term_ids = wp_list_pluck( $term_objects, 'term_id' );
- update_termmeta_cache( $term_ids );
+ wp_lazyload_term_meta( $term_ids );
}
if ( 'all_with_object_id' === $_fields && ! empty( $args['object_ids'] ) ) {
@@ -883,10 +895,16 @@
$object->count = $term->count;
$term_cache[] = $object;
}
+ } elseif ( $fields_is_filtered ) {
+ $term_cache = $term_objects;
} else {
$term_cache = wp_list_pluck( $term_objects, 'term_id' );
}
- wp_cache_add( $cache_key, $term_cache, 'terms' );
+
+ if ( $args['cache_results'] ) {
+ wp_cache_add( $cache_key, $term_cache, 'term-queries' );
+ }
+
$this->terms = $this->format_terms( $term_objects, $_fields );
return $this->terms;
@@ -897,8 +915,6 @@
*
* @since 4.6.0
*
- * @global wpdb $wpdb WordPress database abstraction object.
- *
* @param string $orderby_raw Alias for the field to order by.
* @return string|false Value to used in the ORDER clause. False otherwise.
*/
@@ -1131,4 +1147,36 @@
return $term_objects;
}
+
+ /**
+ * Generate cache key.
+ *
+ * @since 6.2.0
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param array $args WP_Term_Query arguments.
+ * @param string $sql SQL statement.
+ *
+ * @return string Cache key.
+ */
+ protected function generate_cache_key( array $args, $sql ) {
+ global $wpdb;
+ // $args can be anything. Only use the args defined in defaults to compute the key.
+ $cache_args = wp_array_slice_assoc( $args, array_keys( $this->query_var_defaults ) );
+
+ unset( $cache_args['cache_results'], $cache_args['update_term_meta_cache'] );
+
+ if ( 'count' !== $args['fields'] && 'all_with_object_id' !== $args['fields'] ) {
+ $cache_args['fields'] = 'all';
+ }
+ $taxonomies = (array) $args['taxonomy'];
+
+ // Replace wpdb placeholder in the SQL statement used by the cache key.
+ $sql = $wpdb->remove_placeholder_escape( $sql );
+
+ $key = md5( serialize( $cache_args ) . serialize( $taxonomies ) . $sql );
+ $last_changed = wp_cache_get_last_changed( 'terms' );
+ return "get_terms:$key:$last_changed";
+ }
}