PluginProbe
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO / 1.0.1
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO v1.0.1
2.7.0 2.6.0 2.5.0 2.4.0 2.3.0 2.2.0 2.1.1 2.1.0 2.0.2 2.0.1 2.0.0 1.32.0 1.31.0 1.30.0 1.29.0 1.28.0 1.27.0 1.26.0 1.25.0 trunk 1.0.0 1.0.1 1.0.2 1.1.0 1.10.0 All 48 releases
thinkrank / includes / core / class-query-cache.php

class-query-cache.php in ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO 1.0.1, at includes/core/class-query-cache.php

211 lines 5.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Query Cache Class
4 *
5 * Simple query result caching to reduce database load.
6 * Follows existing caching patterns in the codebase.
7 *
8 * Usage Example:
9 * ```php
10 * use ThinkRank\Core\Query_Cache;
11 *
12 * $cache_key = Query_Cache::generate_key('settings', ['context_type' => 'post', 'context_id' => 123]);
13 * $settings = Query_Cache::get_or_execute($cache_key, function() use ($wpdb) {
14 * return $wpdb->get_results("SELECT * FROM wp_thinkrank_seo_settings WHERE context_type = 'post'");
15 * }, 600); // Cache for 10 minutes
16 * ```
17 *
18 * @package ThinkRank
19 * @subpackage Core
20 * @since 1.0.0
21 */
22
23 declare(strict_types=1);
24
25 namespace ThinkRank\Core;
26
27 /**
28 * Query Cache Class
29 *
30 * @since 1.0.0
31 */
32 class Query_Cache {
33
34 /**
35 * Memory cache for current request
36 *
37 * @since 1.0.0
38 * @var array
39 */
40 private static array $memory_cache = [];
41
42 /**
43 * Cache group for WordPress object cache
44 *
45 * @since 1.0.0
46 * @var string
47 */
48 private const CACHE_GROUP = 'thinkrank_queries';
49
50 /**
51 * Default cache duration in seconds
52 *
53 * @since 1.0.0
54 * @var int
55 */
56 private const DEFAULT_DURATION = 300; // 5 minutes
57
58 /**
59 * Option name for cache group version
60 *
61 * @since 1.0.0
62 * @var string
63 */
64 private const VERSION_OPTION = 'thinkrank_query_cache_version';
65
66 /**
67 * Get current cache group version
68 *
69 * @since 1.0.0
70 * @return int Version number >= 1
71 */
72 private static function get_group_version(): int {
73 $version = (int) get_option(self::VERSION_OPTION, 1);
74 return $version > 0 ? $version : 1;
75 }
76
77 /**
78 * Bump cache group version to invalidate all keys
79 *
80 * @since 1.0.0
81 * @return bool Always true
82 */
83 private static function bump_group_version(): bool {
84 $new = self::get_group_version() + 1;
85 // Ensure option exists and does not autoload
86 $existing = get_option(self::VERSION_OPTION, null);
87 if ($existing === null) {
88 add_option(self::VERSION_OPTION, $new, '', 'no');
89 } else {
90 update_option(self::VERSION_OPTION, $new, false);
91 }
92 return true;
93 }
94
95
96 /**
97 * Apply current group version to a cache key
98 *
99 * @since 1.0.0
100 * @param string $key Base key
101 * @return string Versioned key
102 */
103 private static function apply_version(string $key): string {
104 $v = self::get_group_version();
105 return 'v' . $v . ':' . $key;
106 }
107
108
109 /**
110 * Get cached result or execute callback
111 *
112 * @since 1.0.0
113 *
114 * @param string $key Cache key
115 * @param callable $callback Function to execute if cache miss
116 * @param int $duration Cache duration in seconds
117 * @return mixed Query result
118 */
119 public static function get_or_execute(string $key, callable $callback, int $duration = self::DEFAULT_DURATION) {
120 $versioned_key = self::apply_version($key);
121
122 // Check memory cache first (fastest)
123 if (isset(self::$memory_cache[$versioned_key])) {
124 return self::$memory_cache[$versioned_key];
125 }
126
127 // Check persistent cache
128 $cached = wp_cache_get($versioned_key, self::CACHE_GROUP);
129 if ($cached !== false) {
130 // Store in memory cache for this request
131 self::$memory_cache[$versioned_key] = $cached;
132 return $cached;
133 }
134
135 // Execute callback and cache result
136 $result = call_user_func($callback);
137
138 // Store in both caches
139 self::$memory_cache[$versioned_key] = $result;
140 wp_cache_set($versioned_key, $result, self::CACHE_GROUP, $duration);
141
142 return $result;
143 }
144
145 /**
146 * Generate cache key from query and parameters
147 *
148 * @since 1.0.0
149 *
150 * @param string $query_type Query type identifier
151 * @param array $params Query parameters
152 * @return string Cache key
153 */
154 public static function generate_key(string $query_type, array $params = []): string {
155 // Use JSON encoding for stable cross-runtime key generation
156 return 'query_' . $query_type . '_' . md5(wp_json_encode($params));
157 }
158
159 /**
160 * Clear cache for specific key or pattern
161 *
162 * @since 1.0.0
163 *
164 * @param string $key_or_pattern Cache key or pattern
165 * @return bool Success
166 */
167 public static function clear(string $key_or_pattern): bool {
168 $versioned_key = self::apply_version($key_or_pattern);
169
170 // Clear from memory cache
171 if (isset(self::$memory_cache[$versioned_key])) {
172 unset(self::$memory_cache[$versioned_key]);
173 }
174
175 // Clear from persistent cache
176 return wp_cache_delete($versioned_key, self::CACHE_GROUP);
177 }
178
179 /**
180 * Clear all query cache
181 *
182 * @since 1.0.0
183 *
184 * @return bool Success
185 */
186 public static function clear_all(): bool {
187 // Clear memory cache
188 self::$memory_cache = [];
189
190 // Bump version to invalidate all keys logically
191 return self::bump_group_version();
192 }
193
194 /**
195 * Get cache statistics
196 *
197 * @since 1.0.0
198 *
199 * @return array Cache statistics
200 */
201 public static function get_stats(): array {
202 return [
203 'memory_cache_entries' => count(self::$memory_cache),
204 'memory_usage_bytes' => strlen(serialize(self::$memory_cache)),
205 'cache_group' => self::CACHE_GROUP,
206 'group_version' => self::get_group_version(),
207 'default_duration' => self::DEFAULT_DURATION
208 ];
209 }
210 }
211