wpbridge/includes/AIBridge/Adapters/AbstractAdapter.php
feibisi 7bb18002f7 fix: 修复 Codex v0.3.0 代码评审发现的问题
HIGH 级别修复:
- WebhookHandler: 添加 SSRF 防护 (Validator::is_valid_url)
- AIGateway: 自定义端点添加 SSRF 验证
- AIGateway: 白名单域名匹配改为大小写不敏感
- CommercialManager: 文件读取添加路径遍历防护 (realpath)
- GroupModel: from_array() 添加输入类型验证和清理
- GroupManager: add/remove_source_to_group 添加权限检查

MEDIUM 级别修复:
- NotificationManager: 添加 5 分钟速率限制防止通知轰炸
- NotificationManager: 允许第三方扩展通知处理器
- EmailHandler: 添加收件人邮箱格式验证
- EmailHandler: 移除自定义 From 头避免 SPF/DKIM 问题
- CommercialManager: lock/unlock_version 添加权限检查
- CommercialManager: 商业插件列表支持过滤器扩展
- GroupManager: toggle 方法改进原子性,先更新分组再更新源
- AbstractAdapter: 正则匹配添加错误处理

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 20:14:16 +08:00

172 lines
3.8 KiB
PHP

<?php
/**
* AI 适配器抽象基类
*
* @package WPBridge
*/
namespace WPBridge\AIBridge\Adapters;
use WPBridge\Core\Settings;
use WPBridge\Core\Logger;
// 防止直接访问
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* AI 适配器抽象基类
*/
abstract class AbstractAdapter implements AdapterInterface {
/**
* 设置实例
*
* @var Settings
*/
protected Settings $settings;
/**
* 支持的插件 slug 列表
*
* @var array
*/
protected array $supported_plugins = [];
/**
* 匹配的 URL 模式
*
* @var array
*/
protected array $url_patterns = [];
/**
* 构造函数
*
* @param Settings $settings 设置实例
*/
public function __construct( Settings $settings ) {
$this->settings = $settings;
}
/**
* 检查是否支持该插件
*
* @param string $plugin_slug 插件 slug
* @return bool
*/
public function supports( string $plugin_slug ): bool {
return in_array( $plugin_slug, $this->supported_plugins, true );
}
/**
* 检查请求是否匹配
*
* @param string $url 请求 URL
* @param array $args 请求参数
* @return bool
*/
public function matches( string $url, array $args ): bool {
foreach ( $this->url_patterns as $pattern ) {
$result = @preg_match( $pattern, $url );
if ( $result === false ) {
$this->log( '无效的 URL 匹配模式', [ 'pattern' => $pattern ] );
continue;
}
if ( $result === 1 ) {
return true;
}
}
return false;
}
/**
* 是否启用
*
* @return bool
*/
public function is_enabled(): bool {
$ai_settings = $this->settings->get( 'ai_bridge', [] );
$adapters = $ai_settings['adapters'] ?? [];
return in_array( $this->get_name(), $adapters, true );
}
/**
* 记录日志
*
* @param string $message 消息
* @param array $context 上下文
*/
protected function log( string $message, array $context = [] ): void {
$context['adapter'] = $this->get_name();
Logger::debug( $message, $context );
}
/**
* 获取请求体
*
* @param array $args 请求参数
* @return array|null
*/
protected function get_request_body( array $args ): ?array {
if ( empty( $args['body'] ) ) {
return null;
}
$body = $args['body'];
if ( is_string( $body ) ) {
$decoded = json_decode( $body, true );
return json_last_error() === JSON_ERROR_NONE ? $decoded : null;
}
return is_array( $body ) ? $body : null;
}
/**
* 设置请求体
*
* @param array $args 请求参数
* @param array $body 请求体
* @return array
*/
protected function set_request_body( array $args, array $body ): array {
$args['body'] = wp_json_encode( $body );
return $args;
}
/**
* 获取响应体
*
* @param array|\WP_Error $response 响应
* @return array|null
*/
protected function get_response_body( $response ): ?array {
if ( is_wp_error( $response ) ) {
return null;
}
$body = wp_remote_retrieve_body( $response );
if ( empty( $body ) ) {
return null;
}
$decoded = json_decode( $body, true );
return json_last_error() === JSON_ERROR_NONE ? $decoded : null;
}
/**
* 设置响应体
*
* @param array $response 响应
* @param array $body 响应体
* @return array
*/
protected function set_response_body( array $response, array $body ): array {
$response['body'] = wp_json_encode( $body );
return $response;
}
}