PluginProbe
Docket Cache – Object Cache Accelerator / 22.07.01
Docket Cache – Object Cache Accelerator v22.07.01
26.04.05 trunk 22.07.01 22.07.02 22.07.03 22.07.04 22.07.05 23.08.01 23.08.02 24.07.01 24.07.02 24.07.03 24.07.04 24.07.05 24.07.06 24.07.07 26.04.03 26.04.04
docket-cache / includes / src / PostCache.php

PostCache.php in Docket Cache – Object Cache Accelerator 22.07.01, at includes/src/PostCache.php

407 lines 12.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Docket Cache.
4 *
5 * @author Nawawi Jamili
6 * @license MIT
7 *
8 * @see https://github.com/nawawi/docket-cache
9 */
10
11 namespace Nawawi\DocketCache;
12
13 \defined('ABSPATH') || exit;
14
15 final class PostCache
16 {
17 public $prefix = 'docketcache-post';
18 public $group_prefix;
19 public $cache_incr = 0;
20 public $cache_group = '';
21 public $cache_key = '';
22 public $all_post_ids = false;
23 public $cached_post_ids = [];
24 public $cached_posts = [];
25 public $found_posts = false;
26 public $cache_func = 'wp_cache_add';
27 public $cache_func_expiry = 0; // let WP_Object_Cache::maybe_expire handles it
28 public $stalecache_list = [];
29 public $allow_posttype = ['post', 'page', 'attachment'];
30 public $blacklist_posttype = ['scheduled-action'];
31 public $allow_posttype_all = false;
32
33 public function __construct()
34 {
35 $this->group_prefix = $this->prefix.'-';
36 }
37
38 public function register()
39 {
40 $this->setup_for_blog();
41 $this->setup_hooks();
42
43 $this->allow_posttype_all = nwdcx_construe('ADVCPOST_POSTTYPE_ALL');
44 if (!$this->allow_posttype_all) {
45 $types = nwdcx_constval('ADVCPOST_POSTTYPE');
46
47 if (\is_string($types) && 'all' === $types || \in_array($types, $this->allow_posttype)) {
48 $this->allow_posttype = $types;
49 } elseif (!empty($types) && \is_array($types)) {
50 $this->allow_posttype = array_merge($this->allow_posttype, $types);
51 }
52 }
53 }
54
55 private function setup_hooks()
56 {
57 add_action('switch_blog', [$this, 'setup_for_blog'], 10, 2);
58 add_filter('posts_request', [&$this, 'posts_request'], 10, 2);
59 add_filter('posts_results', [&$this, 'posts_results'], 10, 2);
60 add_filter('post_limits_request', [&$this, 'post_limits_request'], 999, 2);
61 add_filter('found_posts_query', [&$this, 'found_posts_query'], 10, 2);
62 add_filter('found_posts', [&$this, 'found_posts'], 10, 2);
63
64 // https://developer.wordpress.org/reference/functions/clean_term_cache/
65 add_action('clean_term_cache', [$this, 'invalidate_cache']);
66 // https://developer.wordpress.org/reference/functions/clean_post_cache/
67 add_action('clean_post_cache', [$this, 'invalidate_cache']);
68
69 add_filter(
70 'dashboard_recent_posts_query_args',
71 function ($query_args) {
72 $query_args['cache_results'] = true;
73 $query_args['suppress_filters'] = false;
74
75 return $query_args;
76 },
77 10,
78 1
79 );
80
81 add_filter(
82 'dashboard_recent_drafts_query_args',
83 function ($query_args) {
84 $query_args['suppress_filters'] = false;
85
86 return $query_args;
87 },
88 10,
89 1
90 );
91
92 add_filter(
93 'wp_count_comments',
94 function ($counts = false, $post_id = 0) {
95 if (0 !== $post_id) {
96 return $counts;
97 }
98
99 $cache_key = 'comments-0';
100 $stats_object = wp_cache_get($cache_key, $this->prefix);
101
102 if (false === $stats_object) {
103 $stats = get_comment_count(0);
104 $stats['moderated'] = $stats['awaiting_moderation'];
105 unset($stats['awaiting_moderation']);
106 $stats_object = (object) $stats;
107
108 wp_cache_set($cache_key, $stats_object, $this->prefix, 1800); // 1800 = 30min
109 }
110
111 return $stats_object;
112 },
113 10,
114 2
115 );
116
117 // core
118 foreach (['comment_post', 'wp_set_comment_status'] as $fx) {
119 add_action(
120 $fx,
121 function () {
122 wp_cache_delete('comments-0', $this->prefix);
123 }
124 );
125 }
126
127 // jetpack
128 foreach (['unapproved_to_approved', 'approved_to_unapproved', 'spam_to_approved', 'approved_to_spam'] as $fx) {
129 add_action(
130 'comment_'.$fx,
131 function () {
132 wp_cache_delete('comments-0', $this->prefix);
133 }
134 );
135 }
136
137 add_filter(
138 'media_library_months_with_files',
139 function () {
140 $cache_group = $this->prefix.'-media';
141
142 $months = wp_cache_get('media_library_months_with_files', $cache_group);
143
144 if (false === $months) {
145 if (!nwdcx_wpdb($wpdb)) {
146 return $months;
147 }
148
149 $months = $wpdb->get_results(
150 $wpdb->prepare(
151 "SELECT DISTINCT YEAR( post_date ) AS year, MONTH( post_date ) AS month FROM `{$wpdb->posts}` WHERE post_type = %s ORDER BY post_date DESC",
152 'attachment'
153 )
154 );
155 wp_cache_set('media_library_months_with_files', $months, $cache_group, 2592000); // 2592000 = 1month
156 }
157
158 return $months;
159 }
160 );
161
162 add_action(
163 'add_attachment',
164 function ($post_id) {
165 if (\defined('WP_IMPORTING') && WP_IMPORTING) {
166 return;
167 }
168
169 if (!nwdcx_wpdb($wpdb)) {
170 return;
171 }
172
173 $months = $wpdb->get_results(
174 $wpdb->prepare(
175 "SELECT DISTINCT YEAR( post_date ) AS year, MONTH( post_date ) AS month FROM `{$wpdb->posts}` WHERE post_type = %s ORDER BY post_date DESC LIMIT 1",
176 'attachment'
177 ),
178 ARRAY_A
179 );
180
181 if (empty($months) || !\is_array($months)) {
182 return;
183 }
184
185 $cache_group = $this->prefix.'-media';
186 $months = array_values($months);
187 $months = array_shift($months);
188
189 $months = (object) $months;
190
191 if (!$months->year == get_the_time('Y', $post_id) && !$months->month == get_the_time('m', $post_id)) {
192 wp_cache_delete('media_library_months_with_files', $cache_group);
193 }
194 }
195 );
196
197 if (nwdcx_construe('FLUSH_STALECACHE')) {
198 add_action('shutdown', [$this, 'stalecache_set'], \PHP_INT_MAX);
199 }
200 }
201
202 public function setup_for_blog($new_blog_id = false, $previous_blog_id = false)
203 {
204 if ($new_blog_id && $new_blog_id === $previous_blog_id) {
205 return;
206 }
207
208 $this->cache_incr = wp_cache_get('cache_incr', $this->prefix);
209 if (!is_numeric($this->cache_incr)) {
210 $now = time();
211 wp_cache_set('cache_incr', $now, $this->prefix);
212 $this->cache_incr = $now;
213 }
214 $this->cache_group = $this->group_prefix.$this->cache_incr;
215 }
216
217 public function invalidate_cache()
218 {
219 if (is_admin() && isset($_POST['wp-preview']) && 'dopreview' === $_POST['wp-preview']) {
220 return;
221 }
222
223 if (\defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
224 return;
225 }
226
227 // 03052022: flush
228 if (nwdcx_construe('FLUSH_STALECACHE')) {
229 $this->stalecache_list[md5($this->cache_group)] = $this->cache_group;
230 }
231
232 $this->cache_incr = wp_cache_incr('cache_incr', 1, $this->prefix);
233
234 if (10 < \strlen($this->cache_incr)) {
235 wp_cache_set('cache_incr', 0, $this->prefix);
236 $this->cache_incr = 0;
237 }
238 $this->cache_group = $this->group_prefix.$this->cache_incr;
239 }
240
241 private function allow_post_type($post_type)
242 {
243 if ($this->allow_posttype_all && !\in_array($post_type, $this->blacklist_posttype)) {
244 return true;
245 }
246
247 $this->allow_posttype = apply_filters('docketcache/filter/postcache/posttype', $this->allow_posttype);
248 if (\is_string($this->allow_posttype) && ('all' === $this->allow_posttype || $post_type === $this->allow_posttype)) {
249 return true;
250 }
251
252 if (\in_array($post_type, $this->allow_posttype) || \in_array('all', $this->allow_posttype)) {
253 return true;
254 }
255
256 return false;
257 }
258
259 public function posts_request($sql, $query)
260 {
261 if (!nwdcx_wpdb($wpdb)) {
262 return $sql;
263 }
264
265 if (!$this->allow_post_type($query->get('post_type'))) {
266 return $sql;
267 }
268
269 $this->cache_key = 'query-'.md5($sql);
270 $this->all_post_ids = wp_cache_get($this->cache_key, $this->cache_group);
271
272 if ('NA' !== $this->found_posts) {
273 $cache_key = $this->cache_key.'-found';
274 $this->found_posts = wp_cache_get($cache_key, $this->cache_group);
275 }
276
277 if ($this->all_post_ids xor $this->found_posts) {
278 $this->cache_func = 'wp_cache_set';
279 } else {
280 $this->cache_func = 'wp_cache_add';
281 }
282
283 $this->cached_post_ids = [];
284 $this->cached_posts = [];
285
286 if ($this->found_posts && \is_array($this->all_post_ids)) {
287 if (\function_exists('wp_cache_get_multi')) {
288 $this->cached_posts = wp_cache_get_multi(['posts' => $this->all_post_ids]);
289 } else {
290 $this->cached_posts = [];
291 foreach ($this->all_post_ids as $key) {
292 $this->cached_posts[$key] = wp_cache_get($key, 'posts');
293 }
294 }
295
296 // array_filter = remove "empty" array
297 $this->cached_posts = array_filter($this->cached_posts);
298
299 foreach ($this->cached_posts as $post) {
300 if (!empty($post)) {
301 $this->cached_post_ids[] = $post->ID;
302 }
303 }
304 $uncached_post_ids = array_diff($this->all_post_ids, $this->cached_post_ids);
305
306 if ($uncached_post_ids) {
307 return "SELECT * FROM `{$wpdb->posts}` WHERE ID IN(".implode(',', array_map('absint', $uncached_post_ids)).')';
308 }
309
310 return '';
311 }
312
313 return $sql;
314 }
315
316 public function posts_results($posts, $query)
317 {
318 if (!$this->allow_post_type($query->get('post_type'))) {
319 return $posts;
320 }
321
322 if ($this->found_posts && \is_array($this->all_post_ids)) {
323 $collated_posts = [];
324 foreach ($this->cached_posts as $post) {
325 $posts[] = $post;
326 }
327
328 foreach ($posts as $post) {
329 $loc = array_search($post->ID, $this->all_post_ids);
330 if (is_numeric($loc) && -1 < $loc) {
331 $collated_posts[$loc] = $post;
332 }
333 }
334 ksort($collated_posts);
335
336 return array_map('get_post', array_values($collated_posts));
337 }
338
339 $post_ids = [];
340 foreach ((array) $posts as $post) {
341 $post_ids[] = $post->ID;
342 }
343
344 if (!$post_ids) {
345 return [];
346 }
347
348 \call_user_func($this->cache_func, $this->cache_key, $post_ids, $this->cache_group, $this->cache_func_expiry);
349
350 return array_map('get_post', $posts);
351 }
352
353 public function post_limits_request($limits, $query)
354 {
355 if (!$this->allow_post_type($query->get('post_type'))) {
356 return $limits;
357 }
358
359 if (empty($limits) || (isset($query->query_vars['no_found_rows']) && $query->query_vars['no_found_rows'])) {
360 $this->found_posts = 'NA';
361 } else {
362 $this->found_posts = false;
363 }
364
365 return $limits;
366 }
367
368 public function found_posts_query($sql, $query)
369 {
370 if (!$this->allow_post_type($query->get('post_type'))) {
371 return $sql;
372 }
373
374 if ($this->found_posts && \is_array($this->all_post_ids)) {
375 return '';
376 }
377
378 return $sql;
379 }
380
381 public function found_posts($found_posts, $query)
382 {
383 if (!$this->allow_post_type($query->get('post_type'))) {
384 return $found_posts;
385 }
386
387 if ($this->found_posts && \is_array($this->all_post_ids)) {
388 return (int) $this->found_posts;
389 }
390
391 $cache_key = $this->cache_key.'-found';
392 \call_user_func($this->cache_func, $cache_key, (int) $found_posts, $this->cache_group, $this->cache_func_expiry);
393
394 return $found_posts;
395 }
396
397 // send cache list to WP_Object_Cache and flush it at wp_cache_close right after "shutdown" hook.
398 public function stalecache_set()
399 {
400 if (!empty($this->stalecache_list) && \function_exists('wp_cache_add_stalecache')) {
401 $key_last = array_key_last($this->stalecache_list);
402 $list = [$key_last => $this->stalecache_list[$key_last]];
403 wp_cache_add_stalecache($list);
404 }
405 }
406 }
407