+
+ /**
+ * Lazy-loads termmeta for located posts.
+ *
+ * As a rule, term queries (`get_terms()` and `wp_get_object_terms()`) prime the metadata cache for matched
+ * terms by default. However, this can cause a slight performance penalty, especially when that metadata is
+ * not actually used. In the context of a `WP_Query` instance, we're able to avoid this potential penalty.
+ * `update_object_term_cache()`, called from `update_post_caches()`, does not 'update_term_meta_cache'.
+ * Instead, the first time `get_term_meta()` is called from within a `WP_Query` loop, the current method
+ * detects the fact, and then primes the metadata cache for all terms attached to all posts in the loop,
+ * with a single database query.
+ *
+ * This method is public so that it can be used as a filter callback. As a rule, there is no need to invoke it
+ * directly, from either inside or outside the `WP_Query` object.
+ *
+ * @since 4.4.0
+ * @access public
+ *
+ * @param mixed $check The `$check` param passed from the 'get_term_metadata' hook.
+ * @param int $term_id ID of the term whose metadata is being cached.
+ * @return mixed In order not to short-circuit `get_metadata()`. Generally, this is `null`, but it could be
+ * another value if filtered by a plugin.
+ */
+ public function lazyload_term_meta( $check, $term_id ) {
+ /*
+ * We only do this once per `WP_Query` instance.
+ * Can't use `remove_filter()` because of non-unique object hashes.
+ */
+ if ( $this->updated_term_meta_cache ) {
+ return $check;
+ }
+
+ // We can only lazyload if the entire post object is present.
+ $posts = array();
+ foreach ( $this->posts as $post ) {
+ if ( $post instanceof WP_Post ) {
+ $posts[] = $post;
+ }
+ }
+
+ if ( ! empty( $posts ) ) {
+ // Fetch cached term_ids for each post. Keyed by term_id for faster lookup.
+ $term_ids = array();
+ foreach ( $posts as $post ) {
+ $taxonomies = get_object_taxonomies( $post->post_type );
+ foreach ( $taxonomies as $taxonomy ) {
+ // Term cache should already be primed by 'update_post_term_cache'.
+ $terms = get_object_term_cache( $post->ID, $taxonomy );
+ if ( false !== $terms ) {
+ foreach ( $terms as $term ) {
+ if ( ! isset( $term_ids[ $term->term_id ] ) ) {
+ $term_ids[ $term->term_id ] = 1;
+ }
+ }
+ }
+ }
+ }
+
+ /*
+ * Only update the metadata cache for terms belonging to these posts if the term_id passed
+ * to `get_term_meta()` matches one of those terms. This prevents a single call to
+ * `get_term_meta()` from priming metadata for all `WP_Query` objects.
+ */
+ if ( isset( $term_ids[ $term_id ] ) ) {
+ update_termmeta_cache( array_keys( $term_ids ) );
+ $this->updated_term_meta_cache = true;
+ }
+ }
+
+ // If no terms were found, there's no need to run this again.
+ if ( empty( $term_ids ) ) {
+ $this->updated_term_meta_cache = true;
+ }
+
+ return $check;
+ }
+
+ /**
+ * Lazy-load comment meta when inside of a `WP_Query` loop.
+ *
+ * This method is public so that it can be used as a filter callback. As a rule, there is no need to invoke it
+ * directly, from either inside or outside the `WP_Query` object.
+ *
+ * @since 4.4.0
+ *
+ * @param mixed $check The `$check` param passed from the 'get_comment_metadata' hook.
+ * @param int $comment_id ID of the comment whose metadata is being cached.
+ * @return mixed The original value of `$check`, to not affect 'get_comment_metadata'.
+ */
+ public function lazyload_comment_meta( $check, $comment_id ) {
+ /*
+ * We only do this once per `WP_Query` instance.
+ * Can't use `remove_filter()` because of non-unique object hashes.
+ */
+ if ( $this->updated_comment_meta_cache ) {
+ return $check;
+ }
+
+ // Don't use `wp_list_pluck()` to avoid by-reference manipulation.
+ $comment_ids = array();
+ if ( is_array( $this->comments ) ) {
+ foreach ( $this->comments as $comment ) {
+ $comment_ids[] = $comment->comment_ID;
+ }
+ }
+
+ /*
+ * Only update the metadata cache for comments belonging to these posts if the comment_id passed
+ * to `get_comment_meta()` matches one of those comments. This prevents a single call to
+ * `get_comment_meta()` from priming metadata for all `WP_Query` objects.
+ */
+ if ( in_array( $comment_id, $comment_ids ) ) {
+ update_meta_cache( 'comment', $comment_ids );
+ $this->updated_comment_meta_cache = true;
+ } elseif ( empty( $comment_ids ) ) {
+ $this->updated_comment_meta_cache = true;
+ }
+
+ return $check;
+ }