新增 Exact Cache 模块,为已有的 ExactCache 核心类提供完整的管理界面和统计功能: - 模块骨架:module.json + ExactCacheModule(实现 ModuleInterface 全部 7 个方法) - AJAX 控制器:4 个 handler(保存设置/清空缓存/重置统计/获取统计),含安全三要素 - 成本估算器:基于 UsageTracker 历史数据估算缓存节省金额 - 日统计:7 天滚动统计,shutdown 批量写入,retry-once transient lock - 管理界面:统计卡片 + Chart.js 7 天趋势图 + 设置表单 + 缓存管理 - settings-page.php:添加精确缓存 tab(与 GEO/API Gateway 相同模式) - ExactCache.php:仅添加 get_entries() 只读方法,零核心影响 - uninstall.php:添加 option + transient 批量清理 经过 3 轮 Codex 架构审查(v1.0→v2.0→v2.1→v2.2),所有 P0/P1 问题已解决。 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
114 lines
No EOL
3.3 KiB
PHP
114 lines
No EOL
3.3 KiB
PHP
<?php
|
|
/**
|
|
* Cache AJAX Controller
|
|
*
|
|
* Handles AJAX requests for the Exact Cache module.
|
|
*
|
|
* @package WPMind\Modules\ExactCache
|
|
* @since 1.0.0
|
|
*/
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace WPMind\Modules\ExactCache;
|
|
|
|
/**
|
|
* Class CacheAjaxController
|
|
*
|
|
* Provides 4 AJAX handlers for cache management.
|
|
*/
|
|
final class CacheAjaxController {
|
|
|
|
/**
|
|
* Register AJAX hooks.
|
|
*/
|
|
public function register_hooks(): void {
|
|
add_action( 'wp_ajax_wpmind_save_cache_settings', [ $this, 'ajax_save_cache_settings' ] );
|
|
add_action( 'wp_ajax_wpmind_flush_cache', [ $this, 'ajax_flush_cache' ] );
|
|
add_action( 'wp_ajax_wpmind_reset_cache_stats', [ $this, 'ajax_reset_cache_stats' ] );
|
|
add_action( 'wp_ajax_wpmind_get_cache_stats', [ $this, 'ajax_get_cache_stats' ] );
|
|
}
|
|
|
|
/**
|
|
* Verify request security (shared by all handlers).
|
|
*
|
|
* Uses wpmind_ajax nonce (already issued globally by AdminAssets).
|
|
*
|
|
* @param bool $require_post Whether to enforce POST method.
|
|
*/
|
|
private function verify_request( bool $require_post = false ): void {
|
|
if ( $require_post && 'POST' !== ( $_SERVER['REQUEST_METHOD'] ?? '' ) ) {
|
|
wp_send_json_error( [ 'message' => 'Method not allowed' ], 405 );
|
|
}
|
|
check_ajax_referer( 'wpmind_ajax', 'nonce' );
|
|
if ( ! current_user_can( 'manage_options' ) ) {
|
|
wp_send_json_error( [ 'message' => 'Forbidden' ], 403 );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Save cache settings (POST only).
|
|
*/
|
|
public function ajax_save_cache_settings(): void {
|
|
$this->verify_request( true );
|
|
|
|
// Whitelist fields + boundary validation + wp_unslash.
|
|
$raw = wp_unslash( $_POST );
|
|
$enabled = in_array( $raw['enabled'] ?? '', [ '1', '0' ], true ) ? $raw['enabled'] : '1';
|
|
$default_ttl = max( 0, min( 86400, (int) ( $raw['default_ttl'] ?? 900 ) ) );
|
|
$max_entries = max( 10, min( 5000, (int) ( $raw['max_entries'] ?? 500 ) ) );
|
|
$scope_mode = in_array( $raw['scope_mode'] ?? '', [ 'role', 'user', 'none' ], true )
|
|
? $raw['scope_mode'] : 'role';
|
|
|
|
update_option( 'wpmind_exact_cache_enabled', $enabled, false );
|
|
update_option( 'wpmind_exact_cache_default_ttl', $default_ttl, false );
|
|
update_option( 'wpmind_exact_cache_max_entries', $max_entries, false );
|
|
update_option( 'wpmind_exact_cache_scope_mode', $scope_mode, false );
|
|
|
|
wp_send_json_success();
|
|
}
|
|
|
|
/**
|
|
* Flush all cache entries (POST only, destructive).
|
|
*/
|
|
public function ajax_flush_cache(): void {
|
|
$this->verify_request( true );
|
|
|
|
\WPMind\Cache\ExactCache::instance()->flush();
|
|
wp_send_json_success();
|
|
}
|
|
|
|
/**
|
|
* Reset cache statistics (POST only, destructive).
|
|
*/
|
|
public function ajax_reset_cache_stats(): void {
|
|
$this->verify_request( true );
|
|
|
|
// Reset core stats.
|
|
update_option( 'wpmind_exact_cache_stats', [
|
|
'hits' => 0,
|
|
'misses' => 0,
|
|
'writes' => 0,
|
|
'last_hit_at' => 0,
|
|
'last_miss_at' => 0,
|
|
'last_write_at' => 0,
|
|
'last_key' => '',
|
|
], false );
|
|
// Reset daily stats.
|
|
DailyStats::reset();
|
|
wp_send_json_success();
|
|
}
|
|
|
|
/**
|
|
* Get cache statistics (GET allowed, read-only).
|
|
*/
|
|
public function ajax_get_cache_stats(): void {
|
|
$this->verify_request( false );
|
|
|
|
wp_send_json_success( [
|
|
'stats' => \WPMind\Cache\ExactCache::instance()->get_stats(),
|
|
'daily' => DailyStats::get_daily_data(),
|
|
'savings' => CostEstimator::get_estimated_savings(),
|
|
] );
|
|
}
|
|
} |