WordPress.org

Make WordPress Core

Ticket #35816: 35816.diff

File 35816.diff, 7.8 KB (added by lpawlik, 6 years ago)

POC - More complex solution

  • wp-includes/query.php

    diff --git a/wp-includes/query.php b/wp-includes/query.php
    index ad407c1..4ee3353 100644
    a b class WP_Query { 
    36233623                if ( $this->posts )
    36243624                        $this->posts = array_map( 'get_post', $this->posts );
    36253625
    3626 
    3627                 if ( $q['update_post_term_cache'] ) {
    3628                         add_filter( 'get_term_metadata', array( $this, 'lazyload_term_meta' ), 10, 2 );
    3629                 }
    3630 
    36313626                if ( ! $q['suppress_filters'] ) {
    36323627                        /**
    36333628                         * Filter the raw post results array, prior to status checks.
    class WP_Query { 
    37813776
    37823777                        if ( $q['cache_results'] )
    37833778                                update_post_caches($this->posts, $post_type, $q['update_post_term_cache'], $q['update_post_meta_cache']);
     3779                        if ( $q['update_post_term_cache'] ) {
     3780                                handle_lazy_loads( $this->posts );
     3781                        }
    37843782
    37853783                        $this->post = reset( $this->posts );
    37863784                } else {
    class WP_Query { 
    48504848                        $this->setup_postdata( $this->post );
    48514849                }
    48524850        }
    4853 
    4854         /**
    4855          * Lazy-loads termmeta for located posts.
    4856          *
    4857          * As a rule, term queries (`get_terms()` and `wp_get_object_terms()`) prime the metadata cache for matched
    4858          * terms by default. However, this can cause a slight performance penalty, especially when that metadata is
    4859          * not actually used. In the context of a `WP_Query` instance, we're able to avoid this potential penalty.
    4860          * `update_object_term_cache()`, called from `update_post_caches()`, does not 'update_term_meta_cache'.
    4861          * Instead, the first time `get_term_meta()` is called from within a `WP_Query` loop, the current method
    4862          * detects the fact, and then primes the metadata cache for all terms attached to all posts in the loop,
    4863          * with a single database query.
    4864          *
    4865          * This method is public so that it can be used as a filter callback. As a rule, there is no need to invoke it
    4866          * directly, from either inside or outside the `WP_Query` object.
    4867          *
    4868          * @since 4.4.0
    4869          * @access public
    4870          *
    4871          * @param mixed $check  The `$check` param passed from the 'get_term_metadata' hook.
    4872          * @param int  $term_id ID of the term whose metadata is being cached.
    4873          * @return mixed In order not to short-circuit `get_metadata()`. Generally, this is `null`, but it could be
    4874          *               another value if filtered by a plugin.
    4875          */
    4876         public function lazyload_term_meta( $check, $term_id ) {
    4877                 /*
    4878                  * We only do this once per `WP_Query` instance.
    4879                  * Can't use `remove_filter()` because of non-unique object hashes.
    4880                  */
    4881                 if ( $this->updated_term_meta_cache ) {
    4882                         return $check;
    4883                 }
    4884 
    4885                 // We can only lazyload if the entire post object is present.
    4886                 $posts = array();
    4887                 foreach ( $this->posts as $post ) {
    4888                         if ( $post instanceof WP_Post ) {
    4889                                 $posts[] = $post;
    4890                         }
    4891                 }
    4892 
    4893                 if ( ! empty( $posts ) ) {
    4894                         // Fetch cached term_ids for each post. Keyed by term_id for faster lookup.
    4895                         $term_ids = array();
    4896                         foreach ( $posts as $post ) {
    4897                                 $taxonomies = get_object_taxonomies( $post->post_type );
    4898                                 foreach ( $taxonomies as $taxonomy ) {
    4899                                         // Term cache should already be primed by 'update_post_term_cache'.
    4900                                         $terms = get_object_term_cache( $post->ID, $taxonomy );
    4901                                         if ( false !== $terms ) {
    4902                                                 foreach ( $terms as $term ) {
    4903                                                         if ( ! isset( $term_ids[ $term->term_id ] ) ) {
    4904                                                                 $term_ids[ $term->term_id ] = 1;
    4905                                                         }
    4906                                                 }
    4907                                         }
    4908                                 }
    4909                         }
    4910 
    4911                         /*
    4912                          * Only update the metadata cache for terms belonging to these posts if the term_id passed
    4913                          * to `get_term_meta()` matches one of those terms. This prevents a single call to
    4914                          * `get_term_meta()` from priming metadata for all `WP_Query` objects.
    4915                          */
    4916                         if ( isset( $term_ids[ $term_id ] ) ) {
    4917                                 update_termmeta_cache( array_keys( $term_ids ) );
    4918                                 $this->updated_term_meta_cache = true;
    4919                         }
    4920                 }
    4921 
    4922                 // If no terms were found, there's no need to run this again.
    4923                 if ( empty( $term_ids ) ) {
    4924                         $this->updated_term_meta_cache = true;
    4925                 }
    4926 
    4927                 return $check;
    4928         }
    4929 
    49304851        /**
    49314852         * Lazy-load comment meta when inside of a `WP_Query` loop.
    49324853         *
    function setup_postdata( $post ) { 
    50855006
    50865007        return false;
    50875008}
     5009
     5010/**
     5011 * Handle WP_Query term meta data lazy load.
     5012 *
     5013 * @since 4.5.0
     5014 *
     5015 * @param array $posts Collection posts.
     5016 */
     5017function handle_lazy_loads( array $posts ) {
     5018        static $wp_lazy_loader;
     5019        if ( null === $wp_lazy_loader ) {
     5020                $wp_lazy_loader = new WP_Lazy_Loader();
     5021        }
     5022        $wp_lazy_loader->add_posts( $posts );
     5023}
     5024
     5025/**
     5026 * The WordPress Query term meta data lazy load handler class.
     5027 *
     5028 * @since 4.5.0
     5029 */
     5030class WP_Lazy_Loader {
     5031
     5032        /**
     5033         * Collection of managed posts.
     5034         *
     5035         * @since 4.5.0
     5036         * @var array
     5037         */
     5038        protected $posts = array();
     5039
     5040        /**
     5041         * Collection of handled term ids.
     5042         *
     5043         * @since 4.5.0
     5044         * @var array
     5045         */
     5046        protected $updated_term_meta_cache = array();
     5047
     5048        /**
     5049         * WP_Lazy_Loader constructor.
     5050         *
     5051         * @since 4.5.0
     5052         * @access public
     5053         */
     5054        public function __construct() {
     5055                add_filter( 'get_term_metadata', array( $this, 'lazyload_term_meta' ), 10, 2 );
     5056        }
     5057
     5058        /**
     5059         * Add posts to internal collection.
     5060         *
     5061         * @since 4.5.0
     5062         * @access public
     5063         *
     5064         * @param array $posts Collection of posts.
     5065         */
     5066        public function add_posts( array $posts ) {
     5067
     5068                if ( empty( $posts ) ) {
     5069                        return;
     5070                }
     5071                $this->updated_term_meta_cache = array();
     5072
     5073                foreach ( $posts as $post ) {
     5074                        if ( isset( $this->posts[ $post->ID ] ) || ! $post instanceof WP_Post ) {
     5075                                continue;
     5076                        }
     5077                        $this->posts[ $post->ID ] = $post;
     5078                }
     5079
     5080        }
     5081
     5082        /**
     5083         * Lazy-loads termmeta for located posts.
     5084         *
     5085         * As a rule, term queries (`get_terms()` and `wp_get_object_terms()`) prime the metadata cache for matched
     5086         * terms by default. However, this can cause a slight performance penalty, especially when that metadata is
     5087         * not actually used. In the context of a `WP_Query` instance, we're able to avoid this potential penalty.
     5088         * `update_object_term_cache()`, called from `update_post_caches()`, does not 'update_term_meta_cache'.
     5089         * Instead, the first time `get_term_meta()` is called from within a `WP_Query` loop, the current method
     5090         * detects the fact, and then primes the metadata cache for all terms attached to all posts in the loop,
     5091         * with a single database query.
     5092         *
     5093         * This method is public so that it can be used as a filter callback. As a rule, there is no need to invoke it
     5094         * directly, from either inside or outside the `WP_Query` object.
     5095         *
     5096         * @since 4.5.0
     5097         * @access public
     5098         *
     5099         * @param mixed $check  The `$check` param passed from the 'get_term_metadata' hook.
     5100         * @param int  $term_id ID of the term whose metadata is being cached.
     5101         * @return mixed In order not to short-circuit `get_metadata()`. Generally, this is `null`, but it could be
     5102         *               another value if filtered by a plugin.
     5103         */
     5104        public function lazyload_term_meta( $check, $term_id ) {
     5105                /*
     5106                 * We only do this once per term ID.
     5107                 */
     5108                if ( isset( $this->updated_term_meta_cache[ $term_id ] ) ) {
     5109                        return $check;
     5110                }
     5111
     5112                // Fetch cached term_ids for each post. Keyed by term_id for faster lookup.
     5113                $term_ids = array();
     5114                foreach ( $this->posts as $id => $post ) {
     5115                        $taxonomies = get_object_taxonomies( $post->post_type );
     5116                        foreach ( $taxonomies as $taxonomy ) {
     5117                                // Term cache should already be primed by 'update_post_term_cache'.
     5118                                $terms = get_object_term_cache( $post->ID, $taxonomy );
     5119                                if ( false !== $terms ) {
     5120                                        foreach ( $terms as $term ) {
     5121                                                if ( ! isset( $term_ids[ $term->term_id ] ) ) {
     5122                                                        $term_ids[ $term->term_id ] = 1;
     5123                                                }
     5124                                        }
     5125                                }
     5126                        }
     5127                }
     5128                /*
     5129                 * Only update the metadata cache for terms belonging to these posts if the term_id passed
     5130                 * to `get_term_meta()` matches one of those terms. This prevents a single call to
     5131                 * `get_term_meta()` from priming metadata for all `WP_Query` objects.
     5132                 */
     5133                if ( isset( $term_ids[ $term_id ] ) ) {
     5134                        update_termmeta_cache( array_keys( $term_ids ) );
     5135                }
     5136
     5137                $this->updated_term_meta_cache[ $term_id ] = true;
     5138
     5139                return $check;
     5140        }
     5141}
     5142 No newline at end of file