Files
wordpress-theme-aksrt/src/AKSRT/Admin/ControllerBase.php
T
kate522 aaf458af1c refactor(autoload): migrate theme classes to Composer PSR-4
Move 288 types from global AKSRT_* + inc/classes manual requires into
namespace AKSRT\{Domain} under src/AKSRT/, wire composer.json psr-4,
and slim bootstrap to helpers/modules/hooks only. Split multi-class
widget/gateway files, update admin contract audit paths, and document
the layout in ADR/README.
2026-07-17 13:29:22 +08:00

171 lines
5.9 KiB
PHP

<?php
/**
* @package AKSRT
*/
namespace AKSRT\Admin;
defined('ABSPATH') || exit;
abstract class ControllerBase {
/**
* REST API namespace (shared with Admin).
*/
const API_NAMESPACE = 'aksrt/v1';
/**
* Register REST routes for this domain.
* Subclasses must implement this method.
*/
abstract public function register_routes(): void;
/**
* Permission callback — requires manage_options capability.
*/
public function check_permission(): bool {
return current_user_can('manage_options');
}
/**
* Create a standard success response.
*/
protected function success($data = [], int $status = 200): \WP_REST_Response {
return new \WP_REST_Response(array_merge(['success' => true], $data), $status);
}
/**
* Create a standard error response.
*/
protected function error(string $message, int $status = 400): \WP_REST_Response {
return new \WP_REST_Response([
'success' => false,
'message' => $message,
], $status);
}
/**
* Read JSON request body safely.
*/
protected function get_json_params(\WP_REST_Request $request): array {
$params = $request->get_json_params();
return is_array($params) ? $params : [];
}
/**
* Sanitize a settings array using a whitelist of allowed keys.
*
* @param array $input Raw input data.
* @param array $allowed Allowed keys with their types (string|int|bool|float|array).
* @return array Sanitized data.
*/
protected function sanitize_fields(array $input, array $allowed): array {
$out = [];
foreach ($allowed as $key => $type) {
if (!array_key_exists($key, $input)) continue;
$val = $input[$key];
switch ($type) {
case 'string':
$out[$key] = sanitize_text_field($val);
break;
case 'html':
$out[$key] = wp_kses_post($val);
break;
case 'code':
$out[$key] = is_string($val) ? wp_unslash($val) : '';
break;
case 'url':
$out[$key] = esc_url_raw($val);
break;
case 'int':
$out[$key] = (int) $val;
break;
case 'bool':
$out[$key] = !empty($val);
break;
case 'float':
$out[$key] = (float) $val;
break;
case 'array':
$out[$key] = is_array($val) ? $val : [];
break;
default:
$out[$key] = sanitize_text_field($val);
}
}
return $out;
}
protected function register_section_routes(string $route_slug, string $domain): void {
register_rest_route(self::API_NAMESPACE, '/settings/' . $route_slug . '/(?P<section>[a-z0-9_-]+)', [
'methods' => 'GET',
'callback' => function (\WP_REST_Request $request) use ($domain) {
return $this->get_section_settings($request, $domain);
},
'permission_callback' => [$this, 'check_permission'],
]);
register_rest_route(self::API_NAMESPACE, '/settings/' . $route_slug . '/(?P<section>[a-z0-9_-]+)', [
'methods' => 'POST',
'callback' => function (\WP_REST_Request $request) use ($domain) {
return $this->save_section_settings($request, $domain);
},
'permission_callback' => [$this, 'check_permission'],
]);
}
protected function get_section_settings(\WP_REST_Request $request, string $domain): \WP_REST_Response {
$provider = SchemaProvider::get_instance();
$section = (string) $request->get_param('section');
$schema = $provider->get_section_schema($domain, $section);
if (!$schema) {
return $this->error('未知的设置分组: ' . esc_html($section), 404);
}
return $this->success([
'data' => $provider->read_settings($domain, $section),
'schema' => $schema,
'payload' => [
'domain' => $provider->normalize_domain($domain),
'section' => $provider->normalize_section($section),
'version' => $schema['version'] ?? '1.0.0',
],
]);
}
protected function save_section_settings(\WP_REST_Request $request, string $domain): \WP_REST_Response {
$provider = SchemaProvider::get_instance();
$section = (string) $request->get_param('section');
$schema = $provider->get_section_schema($domain, $section);
if (!$schema) {
return $this->error('未知的设置分组: ' . esc_html($section), 404);
}
$params = $this->get_json_params($request);
$payload = $params['data'] ?? $params;
if (!is_array($payload)) {
return $this->error('无效的设置载荷');
}
$sanitized = $this->sanitize_fields($payload, $provider->get_allowed_fields($domain, $section));
$validation = $provider->validate($domain, $sanitized, $section);
if (!$validation['valid']) {
return $this->error(implode('; ', $validation['errors']), 422);
}
$provider->write_settings($domain, $sanitized, $section);
return $this->success([
'message' => sprintf('%s/%s 设置已保存', $provider->normalize_domain($domain), $provider->normalize_section($section)),
'data' => $provider->read_settings($domain, $section),
'payload' => [
'domain' => $provider->normalize_domain($domain),
'section' => $provider->normalize_section($section),
'version' => $schema['version'] ?? '1.0.0',
],
]);
}
}