feat(widgets): add phase 4 validation and accessibility

This commit is contained in:
2026-08-17 19:12:38 +08:00
parent 31e72748c3
commit 009b66326a
23 changed files with 8078 additions and 53 deletions
+177
View File
@@ -0,0 +1,177 @@
<?php
declare(strict_types=1);
if (!defined('ABSPATH')) {
define('ABSPATH', dirname(__DIR__, 2) . DIRECTORY_SEPARATOR);
}
if (!defined('DAY_IN_SECONDS')) {
define('DAY_IN_SECONDS', 86400);
}
if (!defined('MINUTE_IN_SECONDS')) {
define('MINUTE_IN_SECONDS', 60);
}
set_error_handler(static function (int $severity, string $message, string $file, int $line): bool {
if (!(error_reporting() & $severity)) {
return false;
}
throw new ErrorException($message, 0, $severity, $file, $line);
});
$GLOBALS['aksrt_test_state'] = [];
function aksrt_test_reset_state(): void {
$GLOBALS['aksrt_test_state'] = [
'options' => [],
'sidebars' => [],
'routes' => [],
'query_args' => [],
'current_post' => null,
'current_id' => 0,
'singular' => false,
'pay_meta' => ['pay_type' => 'no', 'price' => 0],
'paid_access' => false,
'logged_in' => false,
];
$GLOBALS['wp_registered_sidebars'] = [];
}
aksrt_test_reset_state();
class WP_Error {
public function __construct(private string $code = '', private string $message = '', private array $data = []) {}
public function get_error_code(): string { return $this->code; }
public function get_error_message(): string { return $this->message; }
public function get_error_data(): array { return $this->data; }
}
class WP_REST_Controller {
public string $namespace = '';
public string $rest_base = '';
}
class WP_REST_Server {
public const READABLE = 'GET';
}
class WP_REST_Request {
public function __construct(private array $params = []) {}
public function get_params(): array { return $this->params; }
public function get_param(string $key) { return $this->params[$key] ?? null; }
}
class WP_REST_Response {
public function __construct(private $data) {}
public function get_data() { return $this->data; }
}
class WP_Post {
public int $ID;
public int $post_author;
public function __construct(int $id = 1, int $author = 1) {
$this->ID = $id;
$this->post_author = $author;
}
}
class WP_Query {
public array $posts = [];
public int $max_num_pages = 1;
public int $found_posts = 0;
public function __construct(array $args = []) {
$GLOBALS['aksrt_test_state']['query_args'][] = $args;
}
public function have_posts(): bool { return false; }
public function the_post(): void {}
}
class WP_Widget {
public string $id_base = '';
public string $id = '';
public int $number = 0;
public function __construct(string $id_base = '', string $name = '', array $widget_options = [], array $control_options = []) {
unset($name, $widget_options, $control_options);
$this->id_base = $id_base;
}
public function get_settings(): array {
$value = get_option('widget_' . $this->id_base, []);
return is_array($value) ? $value : [];
}
public function get_field_id(string $field): string { return 'widget-' . $this->id_base . '-' . $this->number . '-' . $field; }
public function get_field_name(string $field): string { return 'widget-' . $this->id_base . '[' . $this->number . '][' . $field . ']'; }
public function _set(int $number): void {
$this->number = $number;
$this->id = $this->id_base . '-' . $number;
}
}
function __($text): string { return (string) $text; }
function esc_html__($text): string { return esc_html((string) $text); }
function esc_attr__($text): string { return esc_attr((string) $text); }
function esc_html_e($text): void { echo esc_html((string) $text); }
function esc_attr_e($text): void { echo esc_attr((string) $text); }
function esc_html($value): string { return htmlspecialchars((string) $value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); }
function esc_attr($value): string { return esc_html((string) $value); }
function esc_textarea($value): string { return esc_html((string) $value); }
function esc_url_raw($value): string {
$value = trim((string) $value);
return preg_match('#^(?:https?://|/)#', $value) ? $value : '';
}
function esc_url($value): string { return esc_attr(esc_url_raw($value)); }
function sanitize_text_field($value): string { return trim(preg_replace('/[\r\n\t]+/', ' ', strip_tags((string) $value)) ?? ''); }
function sanitize_textarea_field($value): string { return trim(strip_tags((string) $value)); }
function sanitize_key($value): string { return preg_replace('/[^a-z0-9_\-]/', '', strtolower((string) $value)) ?? ''; }
function sanitize_html_class($value): string { return preg_replace('/[^A-Za-z0-9_-]/', '', (string) $value) ?? ''; }
function sanitize_hex_color($value): string {
return preg_match('/^#(?:[0-9a-fA-F]{3}){1,2}$/', (string) $value) ? strtolower((string) $value) : '';
}
function sanitize_textarea($value): string { return sanitize_textarea_field($value); }
function wp_kses_post($value): string { return strip_tags((string) $value, '<a><b><strong><em><br><p><span><ul><ol><li>'); }
function wp_json_encode($value): string { return (string) json_encode($value, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); }
function absint($value): int { return abs((int) $value); }
function checked($actual, $expected = true, bool $display = true): string {
$result = $actual == $expected ? 'checked="checked"' : '';
if ($display) echo $result;
return $result;
}
function selected($actual, $expected = true, bool $display = true): string {
$result = $actual == $expected ? 'selected="selected"' : '';
if ($display) echo $result;
return $result;
}
function number_format_i18n($number): string { return number_format((float) $number); }
function wp_trim_words($text, int $count = 55): string {
$words = preg_split('/\s+/', trim((string) $text)) ?: [];
return implode(' ', array_slice($words, 0, $count));
}
function get_option(string $name, $default = false) { return $GLOBALS['aksrt_test_state']['options'][$name] ?? $default; }
function update_option(string $name, $value): bool { $GLOBALS['aksrt_test_state']['options'][$name] = $value; return true; }
function wp_get_sidebars_widgets(): array { return $GLOBALS['aksrt_test_state']['sidebars']; }
function wp_set_sidebars_widgets(array $sidebars): void { $GLOBALS['aksrt_test_state']['sidebars'] = $sidebars; }
function register_rest_route(string $namespace, string $route, array $args): bool {
$GLOBALS['aksrt_test_state']['routes'][$namespace . $route] = $args;
return true;
}
function rest_ensure_response($data): WP_REST_Response { return $data instanceof WP_REST_Response ? $data : new WP_REST_Response($data); }
function is_wp_error($value): bool { return $value instanceof WP_Error; }
function is_singular(): bool { return (bool) $GLOBALS['aksrt_test_state']['singular']; }
function get_queried_object_id(): int { return (int) $GLOBALS['aksrt_test_state']['current_id']; }
function get_post($post = null) {
if ($post instanceof WP_Post) return $post;
return $GLOBALS['aksrt_test_state']['current_post'];
}
function aksrt_get_widget_null(string $message): string { return '<div class="aksrt-widget__empty" role="status">' . esc_html($message) . '</div>'; }
function aksrt_get_pay_meta($post = null): array { unset($post); return $GLOBALS['aksrt_test_state']['pay_meta']; }
function aksrt_has_paid_access($post = null): bool { unset($post); return (bool) $GLOBALS['aksrt_test_state']['paid_access']; }
function aksrt_get_paywall_html(WP_Post $post, bool $fragment = false): string {
unset($post, $fragment);
$state = $GLOBALS['aksrt_test_state']['logged_in'] ? 'member-locked' : 'guest-locked';
return '<div data-test-paywall="' . $state . '">PAYWALL</div>';
}
function aksrt_get_pay_attributes_html($post, string $class): string { unset($post); return '<ul class="' . esc_attr($class) . '"></ul>'; }
function aksrt_get_demo_link_html($post, string $class): string { unset($post); return '<a class="' . esc_attr($class) . '">DEMO</a>'; }
function is_user_logged_in(): bool { return (bool) $GLOBALS['aksrt_test_state']['logged_in']; }
require dirname(__DIR__, 2) . '/vendor/autoload.php';