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 | | |
| 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 | */ |
| 5017 | function 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 | */ |
| 5030 | class 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 |