| 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 |