HIGH 级别修复: - AdminPage.php: 移除重复的 Logger 导入 - AdminPage.php: 修复 auth_token 双重加密问题 - AdminPage.php: 添加 action 参数白名单验证 - CacheManager.php: get_stats() 使用 $wpdb->prepare() - source-editor.php: 不显示加密后的 token,使用占位符 - Validator.php: DNS 解析失败时视为本地地址(SSRF 防护) MEDIUM 级别修复: - BridgeCommand.php: 使用 WordPress 文件系统 API - SourceModel.php: 改进 auth_token 解密回退逻辑 - SourceModel.php: 添加 URL 协议验证 (http/https) - Encryption.php: 使用随机生成的密钥替代站点 URL 哈希 LOW 级别修复: - BackgroundUpdater.php: 移除 PHP 8.0 联合类型语法 - Plugin.php: 使用 wp_cache_flush_group() 如果可用 - Validator.php: 添加 IPv6 私有地址检查 - uninstall.php: 清理 wpbridge_encryption_key 选项 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
198 lines
4.8 KiB
PHP
198 lines
4.8 KiB
PHP
<?php
|
||
/**
|
||
* 缓存管理器
|
||
*
|
||
* @package WPBridge
|
||
*/
|
||
|
||
namespace WPBridge\Cache;
|
||
|
||
// 防止直接访问
|
||
if ( ! defined( 'ABSPATH' ) ) {
|
||
exit;
|
||
}
|
||
|
||
/**
|
||
* 缓存管理器类
|
||
*/
|
||
class CacheManager {
|
||
|
||
/**
|
||
* 缓存组名
|
||
*
|
||
* @var string
|
||
*/
|
||
const CACHE_GROUP = 'wpbridge';
|
||
|
||
/**
|
||
* 默认 TTL(12 小时)
|
||
*
|
||
* @var int
|
||
*/
|
||
const DEFAULT_TTL = 43200;
|
||
|
||
/**
|
||
* 获取缓存
|
||
*
|
||
* @param string $key 缓存键
|
||
* @return mixed|false
|
||
*/
|
||
public function get( string $key ) {
|
||
// 优先使用对象缓存
|
||
if ( wp_using_ext_object_cache() ) {
|
||
$value = wp_cache_get( $key, self::CACHE_GROUP );
|
||
if ( false !== $value ) {
|
||
return $value;
|
||
}
|
||
}
|
||
|
||
// 降级到 transient
|
||
return get_transient( 'wpbridge_' . $key );
|
||
}
|
||
|
||
/**
|
||
* 设置缓存
|
||
*
|
||
* @param string $key 缓存键
|
||
* @param mixed $value 缓存值
|
||
* @param int $ttl 过期时间(秒)
|
||
* @return bool
|
||
*/
|
||
public function set( string $key, $value, int $ttl = self::DEFAULT_TTL ): bool {
|
||
// 使用对象缓存
|
||
if ( wp_using_ext_object_cache() ) {
|
||
wp_cache_set( $key, $value, self::CACHE_GROUP, $ttl );
|
||
}
|
||
|
||
// 同时存储到 transient
|
||
return set_transient( 'wpbridge_' . $key, $value, $ttl );
|
||
}
|
||
|
||
/**
|
||
* 删除缓存
|
||
*
|
||
* @param string $key 缓存键
|
||
* @return bool
|
||
*/
|
||
public function delete( string $key ): bool {
|
||
if ( wp_using_ext_object_cache() ) {
|
||
wp_cache_delete( $key, self::CACHE_GROUP );
|
||
}
|
||
|
||
return delete_transient( 'wpbridge_' . $key );
|
||
}
|
||
|
||
/**
|
||
* 获取带过期缓存兜底的值
|
||
*
|
||
* @param string $key 缓存键
|
||
* @param callable $callback 获取新值的回调
|
||
* @param int $ttl 正常 TTL
|
||
* @param int $stale_ttl 过期缓存可用时间
|
||
* @return mixed
|
||
*/
|
||
public function get_with_fallback( string $key, callable $callback, int $ttl = self::DEFAULT_TTL, int $stale_ttl = 604800 ) {
|
||
// 尝试获取正常缓存
|
||
$value = $this->get( $key );
|
||
|
||
if ( false !== $value ) {
|
||
return $value;
|
||
}
|
||
|
||
// 尝试获取新值
|
||
try {
|
||
$new_value = $callback();
|
||
|
||
if ( null !== $new_value && false !== $new_value ) {
|
||
$this->set( $key, $new_value, $ttl );
|
||
|
||
// 同时存储一份过期缓存备份
|
||
$this->set( $key . '_stale', $new_value, $stale_ttl );
|
||
|
||
return $new_value;
|
||
}
|
||
} catch ( \Exception $e ) {
|
||
// 获取新值失败,尝试使用过期缓存
|
||
}
|
||
|
||
// 尝试使用过期缓存
|
||
$stale_value = $this->get( $key . '_stale' );
|
||
|
||
if ( false !== $stale_value ) {
|
||
return $stale_value;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
* 清除所有缓存
|
||
*/
|
||
public function flush(): void {
|
||
global $wpdb;
|
||
|
||
// 清除 transient(使用 prepare 防止 SQL 注入)
|
||
$wpdb->query(
|
||
$wpdb->prepare(
|
||
"DELETE FROM {$wpdb->options} WHERE option_name LIKE %s",
|
||
$wpdb->esc_like( '_transient_wpbridge_' ) . '%'
|
||
)
|
||
);
|
||
$wpdb->query(
|
||
$wpdb->prepare(
|
||
"DELETE FROM {$wpdb->options} WHERE option_name LIKE %s",
|
||
$wpdb->esc_like( '_transient_timeout_wpbridge_' ) . '%'
|
||
)
|
||
);
|
||
|
||
// 清除对象缓存组(不使用 flush 避免影响其他插件)
|
||
if ( wp_using_ext_object_cache() ) {
|
||
wp_cache_delete( 'wpbridge', 'wpbridge' );
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取缓存统计
|
||
*
|
||
* @return array
|
||
*/
|
||
public function get_stats(): array {
|
||
global $wpdb;
|
||
|
||
$count = $wpdb->get_var(
|
||
$wpdb->prepare(
|
||
"SELECT COUNT(*) FROM {$wpdb->options} WHERE option_name LIKE %s",
|
||
$wpdb->esc_like( '_transient_wpbridge_' ) . '%'
|
||
)
|
||
);
|
||
|
||
return [
|
||
'transient_count' => (int) $count,
|
||
'object_cache' => wp_using_ext_object_cache(),
|
||
'object_cache_type' => $this->get_object_cache_type(),
|
||
];
|
||
}
|
||
|
||
/**
|
||
* 获取对象缓存类型
|
||
*
|
||
* @return string
|
||
*/
|
||
private function get_object_cache_type(): string {
|
||
if ( ! wp_using_ext_object_cache() ) {
|
||
return 'none';
|
||
}
|
||
|
||
global $wp_object_cache;
|
||
|
||
if ( isset( $wp_object_cache->redis ) || class_exists( 'Redis' ) ) {
|
||
return 'redis';
|
||
}
|
||
|
||
if ( isset( $wp_object_cache->mc ) || class_exists( 'Memcached' ) ) {
|
||
return 'memcached';
|
||
}
|
||
|
||
return 'unknown';
|
||
}
|
||
}
|