WordPress.org

Make WordPress Core

Ticket #35816: 35816.2.diff

File 35816.2.diff, 11.4 KB (added by boonebgorges, 6 years ago)
  • new file src/wp-includes/class-wp-metadata-lazyloader.php

    diff --git src/wp-includes/class-wp-metadata-lazyloader.php src/wp-includes/class-wp-metadata-lazyloader.php
    new file mode 100644
    index 0000000..653a2cb
    - +  
     1<?php
     2
     3/**
     4 * Lazyloader for object metadata.
     5 *
     6 * @since 4.5.0
     7 */
     8class WP_Metadata_Lazyloader {
     9        protected $pending_posts;
     10
     11        /**
     12         * Add posts to the metadata lazyload queue.
     13         *
     14         * @since 4.5.0
     15         *
     16         * @param array  $posts     Array of WP_Post objects.
     17         * @param string $meta_type Type of meta to be lazyloaded. Accepts 'term' or 'comment'.
     18         */
     19        public function add_posts( $posts, $meta_type ) {
     20                if ( empty( $posts ) ) {
     21                        return;
     22                }
     23
     24                foreach ( $posts as $post ) {
     25                        if ( ! ( $post instanceof WP_Post ) || isset( $this->pending_posts[ $meta_type ][ $post->ID ] ) ) {
     26                                continue;
     27                        }
     28
     29                        $this->pending_posts[ $meta_type ][ $post->ID ] = $post->post_type;
     30                }
     31
     32                // @todo Comment meta support.
     33                add_action( 'get_term_metadata', array( $this, 'lazyload_term_meta' ), 10, 2 );
     34        }
     35
     36        /**
     37         * Lazy-loads termmeta for located posts.
     38         *
     39         * As a rule, term queries (`get_terms()` and `wp_get_object_terms()`) prime the metadata cache for matched
     40         * terms by default. However, this can cause a slight performance penalty, especially when that metadata is
     41         * not actually used. In the context of a `WP_Query` instance, we're able to avoid this potential penalty.
     42         * `update_object_term_cache()`, called from `update_post_caches()`, does not 'update_term_meta_cache'.
     43         * Instead, the first time `get_term_meta()` is called from within a `WP_Query` loop, the current method
     44         * detects the fact, and then primes the metadata cache for all terms attached to all posts in the loop,
     45         * with a single database query.
     46         *
     47         * This method is public so that it can be used as a filter callback. As a rule, there is no need to invoke it
     48         * directly, from either inside or outside the `WP_Query` object.
     49         *
     50         * @since 4.5.0
     51         * @access public
     52         *
     53         * @param mixed $check  The `$check` param passed from the 'get_term_metadata' hook.
     54         * @param int  $term_id ID of the term whose metadata is being cached.
     55         * @return mixed In order not to short-circuit `get_metadata()`. Generally, this is `null`, but it could be
     56         *               another value if filtered by a plugin.
     57         */
     58        public function lazyload_term_meta( $check, $term_id ) {
     59                $post_type_taxonomies = $term_ids = array();
     60                foreach ( $this->pending_posts['term'] as $post_id => $post_type ) {
     61                        if ( ! isset( $post_type_taxonomies[ $post_type ] ) ) {
     62                                $post_type_taxonomies[ $post_type ] = get_object_taxonomies( $post_type );
     63
     64                        }
     65
     66                        foreach ( $post_type_taxonomies[ $post_type ] as $taxonomy ) {
     67                                // Term cache should already be primed by 'update_post_term_cache'.
     68                                $terms = get_object_term_cache( $post_id, $taxonomy );
     69                                if ( false !== $terms ) {
     70                                        foreach ( $terms as $term ) {
     71                                                if ( ! isset( $term_ids[ $term->term_id ] ) ) {
     72                                                        $term_ids[ $term->term_id ] = 1;
     73                                                }
     74                                        }
     75                                }
     76                        }
     77                }
     78
     79                if ( $term_ids ) {
     80                        update_termmeta_cache( array_keys( $term_ids ) );
     81                }
     82
     83                // No need to run again for this set of posts.
     84                $this->pending_posts = array();
     85                remove_filter( 'get_term_metadata', array( $this, 'lazyload_term_meta' ), 10, 2 );
     86
     87                return $check;
     88        }
     89}
  • src/wp-includes/post.php

    diff --git src/wp-includes/post.php src/wp-includes/post.php
    index 43361d4..7035ead 100644
    function wp_delete_auto_drafts() { 
    59485948}
    59495949
    59505950/**
     5951 * Queue posts for metadata lazyloading.
     5952 *
     5953 * @since 4.5.0
     5954 *
     5955 * @param array  $posts     Array of WP_Post objects.
     5956 * @param string $meta_type Type of meta to be lazyloaded. Accepts 'term' or 'comment'.
     5957 */
     5958function wp_queue_posts_for_metadata_lazyloading( $posts, $meta_type ) {
     5959        static $wp_metadata_lazyloader;
     5960
     5961        if ( null === $wp_metadata_lazyloader ) {
     5962                $wp_metadata_lazyloader = new WP_Metadata_Lazyloader();
     5963        }
     5964
     5965        $wp_metadata_lazyloader->add_posts( $posts, $meta_type );
     5966}
     5967
     5968/**
    59515969 * Update the custom taxonomies' term counts when a post's status is changed.
    59525970 *
    59535971 * For example, default posts term counts (for custom taxonomies) don't include
  • src/wp-includes/query.php

    diff --git src/wp-includes/query.php src/wp-includes/query.php
    index a8fbbc5..e6d7aec 100644
    class WP_Query { 
    36073607
    36083608
    36093609                if ( $q['update_post_term_cache'] ) {
    3610                         add_filter( 'get_term_metadata', array( $this, 'lazyload_term_meta' ), 10, 2 );
     3610                        wp_queue_posts_for_metadata_lazyloading( $this->posts, 'term' );
    36113611                }
    36123612
    36133613                if ( ! $q['suppress_filters'] ) {
    class WP_Query { 
    48344834        }
    48354835
    48364836        /**
    4837          * Lazy-loads termmeta for located posts.
    4838          *
    4839          * As a rule, term queries (`get_terms()` and `wp_get_object_terms()`) prime the metadata cache for matched
    4840          * terms by default. However, this can cause a slight performance penalty, especially when that metadata is
    4841          * not actually used. In the context of a `WP_Query` instance, we're able to avoid this potential penalty.
    4842          * `update_object_term_cache()`, called from `update_post_caches()`, does not 'update_term_meta_cache'.
    4843          * Instead, the first time `get_term_meta()` is called from within a `WP_Query` loop, the current method
    4844          * detects the fact, and then primes the metadata cache for all terms attached to all posts in the loop,
    4845          * with a single database query.
    4846          *
    4847          * This method is public so that it can be used as a filter callback. As a rule, there is no need to invoke it
    4848          * directly, from either inside or outside the `WP_Query` object.
    4849          *
    4850          * @since 4.4.0
    4851          * @access public
    4852          *
    4853          * @param mixed $check  The `$check` param passed from the 'get_term_metadata' hook.
    4854          * @param int  $term_id ID of the term whose metadata is being cached.
    4855          * @return mixed In order not to short-circuit `get_metadata()`. Generally, this is `null`, but it could be
    4856          *               another value if filtered by a plugin.
    4857          */
    4858         public function lazyload_term_meta( $check, $term_id ) {
    4859                 // We can only lazyload if the entire post object is present.
    4860                 $posts = array();
    4861                 foreach ( $this->posts as $post ) {
    4862                         if ( $post instanceof WP_Post ) {
    4863                                 $posts[] = $post;
    4864                         }
    4865                 }
    4866 
    4867                 if ( ! empty( $posts ) ) {
    4868                         // Fetch cached term_ids for each post. Keyed by term_id for faster lookup.
    4869                         $term_ids = array();
    4870                         foreach ( $posts as $post ) {
    4871                                 $taxonomies = get_object_taxonomies( $post->post_type );
    4872                                 foreach ( $taxonomies as $taxonomy ) {
    4873                                         // Term cache should already be primed by 'update_post_term_cache'.
    4874                                         $terms = get_object_term_cache( $post->ID, $taxonomy );
    4875                                         if ( false !== $terms ) {
    4876                                                 foreach ( $terms as $term ) {
    4877                                                         if ( ! isset( $term_ids[ $term->term_id ] ) ) {
    4878                                                                 $term_ids[ $term->term_id ] = 1;
    4879                                                         }
    4880                                                 }
    4881                                         }
    4882                                 }
    4883                         }
    4884 
    4885                         /*
    4886                          * Only update the metadata cache for terms belonging to these posts if the term_id passed
    4887                          * to `get_term_meta()` matches one of those terms. This prevents a single call to
    4888                          * `get_term_meta()` from priming metadata for all `WP_Query` objects.
    4889                          */
    4890                         if ( isset( $term_ids[ $term_id ] ) ) {
    4891                                 update_termmeta_cache( array_keys( $term_ids ) );
    4892                                 remove_filter( 'get_term_metadata', array( $this, 'lazyload_term_meta' ), 10, 2 );
    4893                         }
    4894                 }
    4895 
    4896                 // If no terms were found, there's no need to run this again.
    4897                 if ( empty( $term_ids ) ) {
    4898                         remove_filter( 'get_term_metadata', array( $this, 'lazyload_term_meta' ), 10, 2 );
    4899                 }
    4900 
    4901                 return $check;
    4902         }
    4903 
    4904         /**
    49054837         * Lazy-load comment meta when inside of a `WP_Query` loop.
    49064838         *
    49074839         * This method is public so that it can be used as a filter callback. As a rule, there is no need to invoke it
  • src/wp-settings.php

    diff --git src/wp-settings.php src/wp-settings.php
    index ef1d2cd..3c21597 100644
    require( ABSPATH . WPINC . '/class-wp-roles.php' ); 
    126126require( ABSPATH . WPINC . '/class-wp-role.php' );
    127127require( ABSPATH . WPINC . '/class-wp-user.php' );
    128128require( ABSPATH . WPINC . '/query.php' );
     129require( ABSPATH . WPINC . '/class-wp-metadata-lazyloader.php' );
    129130require( ABSPATH . WPINC . '/date.php' );
    130131require( ABSPATH . WPINC . '/theme.php' );
    131132require( ABSPATH . WPINC . '/class-wp-theme.php' );
  • tests/phpunit/tests/term/meta.php

    diff --git tests/phpunit/tests/term/meta.php tests/phpunit/tests/term/meta.php
    index f6c7b90..da387e4 100644
    class Tests_Term_Meta extends WP_UnitTestCase { 
    148148                }
    149149        }
    150150
    151         /**
    152          * @ticket 34073
    153          */
    154         public function test_term_meta_should_be_lazy_loaded_only_for_the_queries_in_which_the_term_has_posts() {
    155                 global $wpdb;
    156 
    157                 $posts = self::factory()->post->create_many( 3, array( 'post_status' => 'publish' ) );
    158                 register_taxonomy( 'wptests_tax', 'post' );
    159                 $terms = self::factory()->term->create_many( 6, array( 'taxonomy' => 'wptests_tax' ) );
    160 
    161                 wp_set_object_terms( $posts[0], array( $terms[0], $terms[1] ), 'wptests_tax' );
    162                 wp_set_object_terms( $posts[1], array( $terms[2], $terms[3] ), 'wptests_tax' );
    163                 wp_set_object_terms( $posts[2], array( $terms[0], $terms[4], $terms[5] ), 'wptests_tax' );
    164 
    165                 foreach ( $terms as $t ) {
    166                         add_term_meta( $t, 'foo', 'bar' );
    167                 }
    168 
    169                 $q0 = new WP_Query( array( 'p' => $posts[0], 'cache_results' => true ) );
    170                 $q1 = new WP_Query( array( 'p' => $posts[1], 'cache_results' => true ) );
    171                 $q2 = new WP_Query( array( 'p' => $posts[2], 'cache_results' => true ) );
    172 
    173                 /*
    174                  * $terms[0] belongs to both $posts[0] and $posts[2], so `get_term_meta( $terms[0] )` should prime
    175                  * the cache for term matched by $q0 and $q2.
    176                  */
    177 
    178                 // First request will hit the database.
    179                 $num_queries = $wpdb->num_queries;
    180 
    181                 // Prime caches.
    182                 $this->assertSame( 'bar', get_term_meta( $terms[0], 'foo', true ) );
    183 
    184                 // Two queries: one for $q0 and one for $q2.
    185                 $num_queries += 2;
    186                 $this->assertSame( $num_queries, $wpdb->num_queries );
    187 
    188                 // Next requests should be in cache.
    189                 $this->assertSame( 'bar', get_term_meta( $terms[1], 'foo', true ) );
    190                 $this->assertSame( 'bar', get_term_meta( $terms[4], 'foo', true ) );
    191                 $this->assertSame( 'bar', get_term_meta( $terms[5], 'foo', true ) );
    192                 $this->assertSame( $num_queries, $wpdb->num_queries );
    193 
    194                 // Querying for $terms[2] will prime $terms[3] as well.
    195                 $this->assertSame( 'bar', get_term_meta( $terms[2], 'foo', true ) );
    196                 $num_queries++;
    197                 $this->assertSame( $num_queries, $wpdb->num_queries );
    198 
    199                 $this->assertSame( 'bar', get_term_meta( $terms[3], 'foo', true ) );
    200                 $this->assertSame( $num_queries, $wpdb->num_queries );
    201         }
    202 
    203         public function test_adding_term_meta_should_bust_get_terms_cache() {
    204                 $terms = self::factory()->term->create_many( 2, array( 'taxonomy' => 'wptests_tax' ) );
    205 
    206                 add_term_meta( $terms[0], 'foo', 'bar' );
    207 
    208                 // Prime cache.
    209                 $found = get_terms( 'wptests_tax', array(
    210                         'hide_empty' => false,
    211                         'fields' => 'ids',
    212                         'meta_query' => array(
    213                                 array(
    214                                         'key' => 'foo',
    215                                         'value' => 'bar',
    216                                 ),
    217                         ),
    218                 ) );
    219 
    220                 $this->assertEqualSets( array( $terms[0] ), $found );
    221 
    222                 add_term_meta( $terms[1], 'foo', 'bar' );
    223 
    224                 $found = get_terms( 'wptests_tax', array(
    225                         'hide_empty' => false,
    226                         'fields' => 'ids',
    227                         'meta_query' => array(
    228                                 array(
    229                                         'key' => 'foo',
    230                                         'value' => 'bar',
    231                                 ),
    232                         ),
    233                 ) );
    234 
    235                 $this->assertEqualSets( array( $terms[0], $terms[1] ), $found );
    236         }
    237 
    238151        public function test_updating_term_meta_should_bust_get_terms_cache() {
    239152                $terms = self::factory()->term->create_many( 2, array( 'taxonomy' => 'wptests_tax' ) );
    240153