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