wpmind/includes/Admin/AdminPage.php
LinuxJoy adb6c49301 style: phpcbf 自动格式化 — 全量 WPCS 3.x 规范对齐
135 文件 19,211 处自动修复:空格→Tab 缩进、括号间距、
函数声明空格、前置自增、尾逗号等纯格式化改动。
零逻辑变更,php -l + token 级对比验证通过。

新增 phpcs.xml.dist / phpstan.neon.dist 项目配置。

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 00:48:46 +08:00

393 lines
8.5 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
/**
* Admin page rendering and settings.
*
* @package WPMind\Admin
* @since 3.3.0
*/
declare(strict_types=1);
namespace WPMind\Admin;
use WPMind\WPMind;
/**
* Class AdminPage
*/
final class AdminPage {
/**
* Singleton instance.
*
* @var AdminPage|null
*/
private static ?AdminPage $instance = null;
/**
* Get singleton instance.
*
* @return AdminPage
*/
public static function instance(): AdminPage {
if ( null === self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Constructor.
*/
private function __construct() {}
/**
* 添加管理菜单
*/
public function add_admin_menu(): void {
add_menu_page(
__( '心思设置', 'wpmind' ),
__( '心思', 'wpmind' ),
'manage_options',
'wpmind',
[ $this, 'render_settings_page' ],
'dashicons-heart',
30
);
}
/**
* 注册设置
*/
public function register_settings(): void {
register_setting(
'wpmind_settings',
'wpmind_custom_endpoints',
[
'type' => 'array',
'sanitize_callback' => [ $this, 'sanitize_endpoints' ],
'default' => [],
]
);
register_setting(
'wpmind_settings',
'wpmind_request_timeout',
[
'type' => 'integer',
'sanitize_callback' => [ $this, 'sanitize_timeout' ],
'default' => 60,
]
);
register_setting(
'wpmind_settings',
'wpmind_exact_cache_enabled',
[
'type' => 'string',
'sanitize_callback' => [ $this, 'sanitize_exact_cache_enabled' ],
'default' => '1',
]
);
register_setting(
'wpmind_settings',
'wpmind_exact_cache_default_ttl',
[
'type' => 'integer',
'sanitize_callback' => [ $this, 'sanitize_exact_cache_ttl' ],
'default' => 900,
]
);
register_setting(
'wpmind_settings',
'wpmind_exact_cache_max_entries',
[
'type' => 'integer',
'sanitize_callback' => [ $this, 'sanitize_exact_cache_max_entries' ],
'default' => 500,
]
);
register_setting(
'wpmind_settings',
'wpmind_default_provider',
[
'type' => 'string',
'sanitize_callback' => [ $this, 'sanitize_default_provider' ],
'default' => '',
]
);
// 图像服务设置
register_setting(
'wpmind_image_settings',
'wpmind_image_endpoints',
[
'type' => 'array',
'sanitize_callback' => [ $this, 'sanitize_image_endpoints' ],
'default' => [],
]
);
register_setting(
'wpmind_image_settings',
'wpmind_default_image_provider',
[
'type' => 'string',
'sanitize_callback' => 'sanitize_text_field',
'default' => '',
]
);
// GEO settings are managed by GeoModule via AJAX.
}
/**
* 清理默认提供者(允许列表校验)
*
* @param mixed $input 输入值
* @return string 清理后的值
* @since 1.2.0
*/
public function sanitize_default_provider( $input ): string {
$key = sanitize_key( $input );
// 允许空值
if ( empty( $key ) ) {
return '';
}
// 只允许已定义的端点
$allowed = array_keys( WPMind::instance()->get_default_endpoints() );
return in_array( $key, $allowed, true ) ? $key : '';
}
/**
* 清理端点配置
*
* @param mixed $input 输入数据
* @return array 清理后的数据
* @since 1.1.0
*/
public function sanitize_endpoints( $input ): array {
if ( ! is_array( $input ) ) {
return [];
}
$defaults = WPMind::instance()->get_default_endpoints();
$sanitized = [];
foreach ( $defaults as $key => $default ) {
// 强制使用默认的 name, base_url, models忽略用户提交的值防止篡改
$sanitized[ $key ] = [
'name' => $default['name'],
'base_url' => $default['base_url'],
'models' => $default['models'],
'enabled' => ! empty( $input[ $key ]['enabled'] ),
'api_key' => $this->sanitize_api_key(
$input[ $key ] ?? [],
$key
),
];
}
return $sanitized;
}
/**
* 清理 API Key支持清除功能
*
* @param array $endpoint_input 端点输入数据
* @param string $endpoint_key 端点标识
* @return string 处理后的 API Key
* @since 1.2.0
*/
private function sanitize_api_key( array $endpoint_input, string $endpoint_key ): string {
// 如果勾选了清除,返回空字符串
if ( ! empty( $endpoint_input['clear_api_key'] ) ) {
return '';
}
$api_key = trim( (string) ( $endpoint_input['api_key'] ?? '' ) );
// 如果为空,保留原有值
if ( $api_key === '' ) {
$existing = get_option( 'wpmind_custom_endpoints', [] );
return $existing[ $endpoint_key ]['api_key'] ?? '';
}
return sanitize_text_field( $api_key );
}
/**
* 清理图像端点配置
*
* @param mixed $input 输入数据
* @return array 清理后的图像端点配置
* @since 2.4.0
*/
public function sanitize_image_endpoints( $input ): array {
if ( ! is_array( $input ) ) {
return [];
}
$sanitized = [];
$available_providers = [
'openai_gpt_image',
'google_gemini_image',
'tencent_hunyuan',
'bytedance_doubao',
'flux',
'qwen_image',
];
foreach ( $available_providers as $key ) {
if ( ! isset( $input[ $key ] ) ) {
continue;
}
$provider_input = $input[ $key ];
// 处理清除 API Key
$api_key = $this->sanitize_image_api_key( $provider_input, $key );
if ( ! empty( $provider_input['clear_api_key'] ) ) {
$api_key = '';
}
$sanitized[ $key ] = [
'enabled' => ! empty( $provider_input['enabled'] ),
'api_key' => $api_key,
'custom_base_url' => esc_url_raw( $provider_input['custom_base_url'] ?? '' ),
];
}
return $sanitized;
}
/**
* 清理图像服务 API Key
*
* @param array $provider_input 服务商输入数据
* @param string $provider_key 服务商标识
* @return string 处理后的 API Key
* @since 2.4.0
*/
private function sanitize_image_api_key( array $provider_input, string $provider_key ): string {
$api_key = trim( (string) ( $provider_input['api_key'] ?? '' ) );
// 如果是掩码值(********),保留原有值
if ( $api_key === '' || $api_key === '********' ) {
$existing = get_option( 'wpmind_image_endpoints', [] );
return $existing[ $provider_key ]['api_key'] ?? '';
}
return sanitize_text_field( $api_key );
}
/**
* 清理超时设置
*
* @param mixed $input 输入值
* @return int 清理后的超时值
* @since 1.1.0
*/
public function sanitize_timeout( $input ): int {
$timeout = absint( $input );
return max( 10, min( 300, $timeout ) );
}
/**
* 清理 Exact Cache 开关
*
* @param mixed $input 输入值
* @return string
*/
public function sanitize_exact_cache_enabled( $input ): string {
if ( ! empty( $input ) && in_array( (string) $input, [ '1', 'true', 'on', 'yes' ], true ) ) {
return '1';
}
return '0';
}
/**
* 清理 Exact Cache 默认 TTL
*
* @param mixed $input 输入值
* @return int
*/
public function sanitize_exact_cache_ttl( $input ): int {
$ttl = (int) $input;
return max( 0, min( 86400, $ttl ) );
}
/**
* 清理 Exact Cache 最大条目
*
* @param mixed $input 输入值
* @return int
*/
public function sanitize_exact_cache_max_entries( $input ): int {
$entries = (int) $input;
if ( $entries <= 0 ) {
return 500;
}
return min( 50000, max( 100, $entries ) );
}
/**
* 渲染设置页面
*/
public function render_settings_page(): void {
if ( ! current_user_can( 'manage_options' ) ) {
return;
}
// 显示保存成功消息
settings_errors( 'wpmind_messages' );
include WPMIND_PLUGIN_DIR . 'templates/settings-page.php';
}
/**
* 插件操作链接
*
* @param array $links 现有链接
* @return array 修改后的链接
*/
public function plugin_action_links( array $links ): array {
$settings_link = sprintf(
'<a href="%s">%s</a>',
esc_url( admin_url( 'admin.php?page=wpmind' ) ),
esc_html__( '设置', 'wpmind' )
);
array_unshift( $links, $settings_link );
return $links;
}
/**
* 插件行元信息
*
* @param array $links 现有链接
* @param string $file 插件文件
* @return array 修改后的链接
* @since 1.1.0
*/
public function plugin_row_meta( array $links, string $file ): array {
if ( plugin_basename( WPMIND_PLUGIN_FILE ) !== $file ) {
return $links;
}
$links[] = sprintf(
'<a href="%s" target="_blank" rel="noopener noreferrer">%s</a>',
esc_url( 'https://linuxjoy.com/plugins/wpmind/docs' ),
esc_html__( '文档', 'wpmind' )
);
return $links;
}
}