PluginProbe
Yatra – Travel Booking & Tour Operator Software / trunk
Yatra – Travel Booking & Tour Operator Software vtrunk
3.0.14 3.0.14.1 3.0.14.2 3.0.12 3.0.13 3.0.11 3.0.10 3.0.9 3.0.8 3.0.7 3.0.6 3.0.5 3.0.5.1 3.0.4 3.0.3 3.0.2.9 3.0.2.7 3.0.2.8 3.0.2.6 trunk 1.0.0 2.0.0 2.0.1 2.0.10 2.0.11 All 82 releases
yatra / app / Controllers / CacheController.php

CacheController.php in Yatra – Travel Booking & Tour Operator Software trunk, at app/Controllers/CacheController.php

596 lines 19.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 declare(strict_types=1);
4
5 namespace Yatra\Controllers;
6
7 use WP_REST_Request;
8 use WP_REST_Response;
9 use WP_Error;
10 use Yatra\Utils\Logger;
11 use Yatra\Utils\Cache;
12
13 /**
14 * Cache Management Controller
15 * Handles cache operations for admin interface
16 */
17 class CacheController extends BaseController
18 {
19 /**
20 * Register REST API routes
21 */
22 public function register_routes(): void
23 {
24 register_rest_route('yatra/v1', '/cache/stats', [
25 [
26 'methods' => \WP_REST_Server::READABLE,
27 'callback' => [$this, 'getStats'],
28 'permission_callback' => [$this, 'checkPermissions']
29 ]
30 ]);
31
32 register_rest_route('yatra/v1', '/cache/view', [
33 [
34 'methods' => \WP_REST_Server::READABLE,
35 'callback' => [$this, 'getAllCacheData'],
36 'permission_callback' => [$this, 'checkPermissions']
37 ]
38 ]);
39
40 register_rest_route('yatra/v1', '/cache/status', [
41 [
42 'methods' => \WP_REST_Server::READABLE,
43 'callback' => [$this, 'getCacheStatus'],
44 'permission_callback' => [$this, 'checkPermissions']
45 ]
46 ]);
47
48 register_rest_route('yatra/v1', '/cache/enable', [
49 [
50 'methods' => \WP_REST_Server::EDITABLE,
51 'callback' => [$this, 'enableCache'],
52 'permission_callback' => [$this, 'checkPermissions']
53 ]
54 ]);
55
56 register_rest_route('yatra/v1', '/cache/disable', [
57 [
58 'methods' => \WP_REST_Server::EDITABLE,
59 'callback' => [$this, 'disableCache'],
60 'permission_callback' => [$this, 'checkPermissions']
61 ]
62 ]);
63
64 register_rest_route('yatra/v1', '/cache/clear-item', [
65 [
66 'methods' => \WP_REST_Server::DELETABLE,
67 'callback' => [$this, 'clearCacheItem'],
68 'permission_callback' => [$this, 'checkPermissions']
69 ]
70 ]);
71
72 register_rest_route('yatra/v1', '/cache/clear-all', [
73 [
74 'methods' => \WP_REST_Server::DELETABLE,
75 'callback' => [$this, 'clearAll'],
76 'permission_callback' => [$this, 'checkPermissions']
77 ]
78 ]);
79
80 register_rest_route('yatra/v1', '/cache/clear-pattern', [
81 [
82 'methods' => \WP_REST_Server::DELETABLE,
83 'callback' => [$this, 'clearByPattern'],
84 'permission_callback' => [$this, 'checkPermissions']
85 ]
86 ]);
87
88 register_rest_route('yatra/v1', '/cache/toggle', [
89 [
90 'methods' => \WP_REST_Server::CREATABLE,
91 'callback' => [$this, 'toggleCache'],
92 'permission_callback' => [$this, 'checkPermissions']
93 ]
94 ]);
95
96 register_rest_route('yatra/v1', '/cache/warm', [
97 [
98 'methods' => \WP_REST_Server::CREATABLE,
99 'callback' => [$this, 'warmCache'],
100 'permission_callback' => [$this, 'checkPermissions']
101 ]
102 ]);
103 }
104
105 /**
106 * Register REST API routes (alias for compatibility)
107 */
108 public static function registerRoutes(): void
109 {
110 $instance = new self();
111 $instance->register_routes();
112 }
113
114 /**
115 * Cache management endpoints — gated on the settings cap. Cache
116 * flushes / regenerates are an operational task that fits the
117 * same risk profile as other settings changes. WP admins pass
118 * via the Team module's admin-fallback filter.
119 */
120 public function checkPermissions(): bool
121 {
122 return current_user_can('yatra_manage_settings');
123 }
124
125 /**
126 * Get cache statistics
127 */
128 public function getStats()
129 {
130 try {
131 $metrics = get_option('yatra_cache_metrics', []);
132 $status = \Yatra\Utils\Cache::getStatus();
133 $stats = [
134 'total_operations' => count($metrics),
135 'cache_hits' => 0,
136 'cache_misses' => 0,
137 'avg_execution_time' => 0,
138 'cache_enabled' => $status['cache_enabled'],
139 'cache_effective' => $status['effective_status'],
140 'cache_reason_disabled' => $status['reason_disabled'],
141 'cache_backend_available' => function_exists('get_transient'),
142 'recent_operations' => array_slice($metrics, -10)
143 ];
144
145 if (!empty($metrics)) {
146 $totalTime = 0;
147 foreach ($metrics as $metric) {
148 if ($metric['cache_used']) {
149 $stats['cache_hits']++;
150 } else {
151 $stats['cache_misses']++;
152 }
153 $totalTime += $metric['execution_time'];
154 }
155 $stats['avg_execution_time'] = round($totalTime / count($metrics), 2);
156 $stats['hit_rate'] = $stats['total_operations'] > 0
157 ? round(($stats['cache_hits'] / $stats['total_operations']) * 100, 2)
158 : 0;
159 }
160
161 return $this->success_response($stats);
162
163 } catch (\Exception $e) {
164 Logger::error('Failed to get cache statistics', ['error' => $e->getMessage()]);
165 return $this->error_response('Failed to get cache statistics');
166 }
167 }
168
169 /**
170 * Get all cache data for viewing
171 */
172 public function getAllCacheData()
173 {
174 try {
175 $cacheData = [];
176
177 // Get ALL Yatra-related transients
178 global $wpdb;
179
180 // Debug: Check if any transients exist at all
181 $allTransients = $wpdb->get_results(
182 "SELECT option_name, option_value, option_id
183 FROM {$wpdb->options}
184 WHERE option_name LIKE '_transient_%'
185 ORDER BY option_name LIMIT 10"
186 );
187
188 Logger::debug('All transients in database', ['count' => count($allTransients)]);
189 if (!empty($allTransients)) {
190 Logger::debug('Sample transients', array_slice($allTransients, 0, 3));
191 }
192
193 // Get Yatra-specific transients (both free and pro)
194 $transients = $wpdb->get_results(
195 "SELECT option_name, option_value, option_id
196 FROM {$wpdb->options}
197 WHERE (option_name LIKE '_transient_yatra_%' OR option_name LIKE '_transient_yatra_pro_%')
198 ORDER BY option_name"
199 );
200
201 // Debug: Log Yatra transients found
202 Logger::debug('Yatra transients found', ['count' => count($transients)]);
203 Logger::debug('Yatra transient names', array_column($transients, 'option_name'));
204
205 // Filter out timeout transients, keep only data transients
206 $transients = array_filter($transients, function($transient) {
207 return !strpos($transient->option_name, '_transient_timeout_');
208 });
209
210 Logger::debug('Yatra data transients after filtering', ['count' => count($transients)]);
211
212 foreach ($transients as $transient) {
213 $key = str_replace('_transient_', '', $transient->option_name);
214 $value = maybe_unserialize($transient->option_value);
215
216 // Get expiration time from timeout option
217 $timeoutOption = '_transient_timeout_' . $key;
218 $expiration = get_option($timeoutOption);
219 $expiresAt = $expiration ? date('Y-m-d H:i:s', (int)$expiration) : 'Never expires';
220
221 // Format value for better display
222 $displayValue = $this->formatCacheValue($value);
223
224 $cacheData[] = [
225 'key' => $key,
226 'type' => 'transient',
227 'value' => $displayValue,
228 'size' => strlen($transient->option_value),
229 'created_at' => current_time('mysql'),
230 'expires_at' => $expiresAt,
231 'option_id' => $transient->option_id
232 ];
233 }
234
235 // Get memory cache data
236 $memoryCache = Cache::getCacheStats();
237 if (!empty($memoryCache['memory_cache_size'])) {
238 $cacheData[] = [
239 'key' => 'memory_cache',
240 'type' => 'memory',
241 'value' => 'Memory cache data (' . $memoryCache['memory_cache_size'] . ' items)',
242 'size' => $memoryCache['memory_usage'] ?? 0,
243 'created_at' => current_time('mysql'),
244 'expires_at' => 'End of request',
245 'option_id' => null
246 ];
247 }
248
249 return $this->success_response([
250 'success' => true,
251 'message' => 'Cache data retrieved',
252 'data' => [
253 'cache_data' => $cacheData,
254 'total_items' => count($cacheData),
255 'total_size' => array_sum(array_column($cacheData, 'size'))
256 ]
257 ]);
258
259 } catch (\Exception $e) {
260 Logger::error('Failed to get cache data', ['error' => $e->getMessage()]);
261 return $this->error_response('Failed to get cache data');
262 }
263 }
264
265 /**
266 * Clear specific cache item
267 */
268 public function clearCacheItem(WP_REST_Request $request)
269 {
270 try {
271 $key = $request->get_param('key');
272 $type = $request->get_param('type');
273
274 if (empty($key)) {
275 return $this->error_response('Cache key is required');
276 }
277
278 $success = false;
279 $message = '';
280
281 switch ($type) {
282 case 'transient':
283 $success = delete_transient($key);
284 // Also delete the timeout entry
285 delete_option('_transient_timeout_' . $key);
286 $message = $success ? 'Transient cache cleared' : 'Failed to clear transient cache';
287 break;
288 case 'memory':
289 // Memory cache is handled by the Cache class
290 Cache::delete($key);
291 $success = true;
292 $message = 'Memory cache cleared';
293 break;
294 default:
295 // Try to clear from all available backends
296 Cache::delete($key);
297 $success = true;
298 $message = 'Cache cleared from all backends';
299 break;
300 }
301
302 if ($success) {
303 Logger::info("Cache item cleared", ['key' => $key, 'type' => $type]);
304 return $this->success_response([
305 'success' => true,
306 'message' => $message,
307 'data' => ['key' => $key, 'type' => $type]
308 ]);
309 } else {
310 return $this->error_response('Failed to clear cache item');
311 }
312
313 } catch (\Exception $e) {
314 Logger::error('Failed to clear cache item', ['error' => $e->getMessage()]);
315 return $this->error_response('Failed to clear cache item');
316 }
317 }
318
319 /**
320 * Get transient creation time
321 */
322 private function getTransientCreatedTime(string $optionName): string
323 {
324 global $wpdb;
325
326 $result = $wpdb->get_var($wpdb->prepare(
327 "SELECT option_added FROM {$wpdb->options} WHERE option_name = %s",
328 $optionName
329 ));
330
331 return $result ? date('Y-m-d H:i:s', strtotime($result)) : 'Unknown';
332 }
333
334 /**
335 * Get transient expiration time
336 */
337 private function getTransientExpirationTime(string $optionName): string
338 {
339 global $wpdb;
340
341 $timeoutKey = '_transient_timeout_' . str_replace('_transient_', '', $optionName);
342 $timeout = $wpdb->get_var($wpdb->prepare(
343 "SELECT option_value FROM {$wpdb->options} WHERE option_name = %s",
344 $timeoutKey
345 ));
346
347 return $timeout ? date('Y-m-d H:i:s', $timeout) : 'No expiration';
348 }
349
350 /**
351 * Clear all cache
352 */
353 public function clearAll()
354 {
355 try {
356 $startTime = microtime(true);
357
358 // Clear all Yatra cache
359 Cache::clearByPrefix('yatra_');
360
361 $executionTime = round((microtime(true) - $startTime) * 1000, 2);
362
363 Logger::info('All cache cleared', ['execution_time' => $executionTime]);
364
365 return $this->success_response([
366 'success' => true,
367 'message' => 'All cache cleared',
368 'data' => [
369 'execution_time' => $executionTime
370 ]
371 ]);
372
373 } catch (\Exception $e) {
374 Logger::error('Failed to clear all cache', ['error' => $e->getMessage()]);
375 return $this->error_response('Failed to clear all cache');
376 }
377 }
378
379 /**
380 * Clear cache by pattern
381 */
382 public function clearByPattern(WP_REST_Request $request)
383 {
384 try {
385 $pattern = $request->get_param('pattern');
386
387 if (empty($pattern)) {
388 return $this->error_response('Pattern is required');
389 }
390
391 $startTime = microtime(true);
392
393 // Clear cache by pattern
394 Cache::clearByPrefix($pattern);
395
396 $executionTime = round((microtime(true) - $startTime) * 1000, 2);
397
398 Logger::info('Cache cleared by pattern', [
399 'pattern' => $pattern,
400 'execution_time' => $executionTime
401 ]);
402
403 return $this->success_response([
404 'success' => true,
405 'message' => 'Cache cleared by pattern',
406 'data' => [
407 'pattern' => $pattern,
408 'execution_time' => $executionTime
409 ]
410 ]);
411
412 } catch (\Exception $e) {
413 Logger::error('Failed to clear cache by pattern', [
414 'pattern' => $pattern ?? 'unknown',
415 'error' => $e->getMessage()
416 ]);
417 return $this->error_response('Failed to clear cache by pattern');
418 }
419 }
420
421 /**
422 * Toggle cache enabled status
423 */
424 public function toggleCache(WP_REST_Request $request)
425 {
426 try {
427 $enabled = $request->get_param('enabled');
428 $enabled = filter_var($enabled, FILTER_VALIDATE_BOOLEAN);
429
430 update_option('yatra_cache_enabled', $enabled);
431
432 Logger::info('Cache status toggled', ['enabled' => $enabled]);
433
434 return $this->success_response([
435 'success' => true,
436 'message' => 'Cache status updated',
437 'data' => [
438 'cache_enabled' => $enabled
439 ]
440 ]);
441
442 } catch (\Exception $e) {
443 Logger::error('Failed to toggle cache status', ['error' => $e->getMessage()]);
444 return $this->error_response('Failed to toggle cache status');
445 }
446 }
447
448 /**
449 * Warm cache
450 */
451 public function warmCache()
452 {
453 try {
454 $startTime = microtime(true);
455 $warmed = [];
456
457 // Warm common cache patterns
458 $patterns = [
459 'yatra_frontend_attributes',
460 'yatra_filterable_attributes',
461 'trip_listing_filter_options',
462 ];
463
464 foreach ($patterns as $pattern) {
465 // Clear existing cache for this pattern
466 Cache::clearByPrefix($pattern);
467 $warmed[] = $pattern;
468 }
469
470 $executionTime = round((microtime(true) - $startTime) * 1000, 2);
471
472 Logger::info('Cache warmed', [
473 'warmed_patterns' => $warmed,
474 'execution_time' => $executionTime
475 ]);
476
477 return $this->success_response([
478 'success' => true,
479 'message' => 'Cache warmed',
480 'data' => [
481 'warmed_patterns' => $warmed,
482 'execution_time' => $executionTime
483 ]
484 ]);
485
486 } catch (\Exception $e) {
487 Logger::error('Failed to warm cache', ['error' => $e->getMessage()]);
488 return $this->error_response('Failed to warm cache');
489 }
490 }
491
492 /**
493 * Format cache value for better display
494 */
495 private function formatCacheValue($value): string
496 {
497 // Return actual data as JSON for frontend to display
498 if (is_array($value) || is_object($value)) {
499 return json_encode($value, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
500 }
501
502 if (is_bool($value)) {
503 return $value ? 'true' : 'false';
504 }
505
506 if (is_null($value)) {
507 return 'null';
508 }
509
510 if (is_string($value)) {
511 // Return the actual string value
512 return $value;
513 }
514
515 if (is_numeric($value)) {
516 return (string) $value;
517 }
518
519 return 'Unknown type: ' . gettype($value);
520 }
521
522 /**
523 * Get cache status
524 */
525 public function getCacheStatus()
526 {
527 try {
528 $status = \Yatra\Utils\Cache::getStatus();
529
530 Logger::info('Cache status retrieved', $status);
531
532 return $this->success_response([
533 'success' => true,
534 'message' => 'Cache status retrieved',
535 'data' => $status
536 ]);
537
538 } catch (\Exception $e) {
539 Logger::error('Failed to get cache status', ['error' => $e->getMessage()]);
540 return $this->error_response('Failed to get cache status');
541 }
542 }
543
544 /**
545 * Enable cache
546 */
547 public function enableCache()
548 {
549 try {
550 $success = \Yatra\Utils\Cache::enableForTesting();
551
552 if ($success) {
553 Logger::info('Cache enabled successfully');
554 return $this->success_response([
555 'success' => true,
556 'message' => 'Cache enabled successfully',
557 'data' => ['cache_enabled' => true]
558 ]);
559 } else {
560 return $this->error_response('Failed to enable cache');
561 }
562
563 } catch (\Exception $e) {
564 Logger::error('Failed to enable cache', ['error' => $e->getMessage()]);
565 return $this->error_response('Failed to enable cache');
566 }
567 }
568
569 /**
570 * Disable cache
571 */
572 public function disableCache()
573 {
574 try {
575 $success = \Yatra\Utils\Cache::disableForTesting();
576
577 if ($success) {
578 Logger::info('Cache disabled successfully');
579 return $this->success_response([
580 'success' => true,
581 'message' => 'Cache disabled successfully',
582 'data' => ['cache_enabled' => false]
583 ]);
584 } else {
585 return $this->error_response('Failed to disable cache');
586 }
587
588 } catch (\Exception $e) {
589 Logger::error('Failed to disable cache', ['error' => $e->getMessage()]);
590 return $this->error_response('Failed to disable cache');
591 }
592 }
593
594
595 }
596