fix(admin): keep enum values in their declared type when sanitizing
The 积分兑换明细.level radio declares its options as the integers 1 and 2, but sanitize_field_value() ran every radio value through sanitize_text_field(), so the sanitized payload held the string 1. validate() then compared with in_array(..., true) and rejected both default rows, which made the VIP payment section impossible to save. Resolve enum values to the value declared by their option during sanitization, covering select, radio, palette and image_select. A value that matches no option still falls through to the previous string coercion, so invalid input keeps producing a precise validation error. This also keeps the stored type aligned with the schema, which the naive-ui radio group compares with === when it re-renders a saved setting. Verified with an end-to-end sanitize, validate, write and read round-trip, and locked in with a contract test in the VIP structured-settings suite. The widget-system, admin, front-submit and navigation suites all pass (658 tests).
This commit is contained in:
1 parent
4b5098a753
commit
064f4e0955
3 files changed
+127
No files matched your search
@@ -866,6 +866,47 @@ class SchemaProvider {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a submitted enum value to the value declared by its option.
|
||||
*
|
||||
* Payloads reach the sanitizer as strings (or numbers), so a strict match
|
||||
* against the schema would reject a valid choice whenever the option value
|
||||
* is not itself a string -- e.g. 积分兑换明细.level declares 1 / 2 while the
|
||||
* SPA submits them as numbers and sanitize_text_field() then turns them into
|
||||
* "1" / "2". Returning the declared value keeps the stored type aligned with
|
||||
* the schema, which the radio/select controls compare with === when they
|
||||
* re-render a saved setting.
|
||||
*
|
||||
* Returns null when nothing matches, so validation still reports the
|
||||
* invalid value instead of silently rewriting the operator's input.
|
||||
*
|
||||
* @param mixed $value
|
||||
* @param array<int,mixed> $options
|
||||
* @return mixed
|
||||
*/
|
||||
private function match_option_value($value, array $options) {
|
||||
foreach ($options as $opt) {
|
||||
$opt_value = is_array($opt) ? ($opt['value'] ?? null) : $opt;
|
||||
if ($opt_value === $value) {
|
||||
return $opt_value;
|
||||
}
|
||||
}
|
||||
if (is_bool($value) || !is_scalar($value)) {
|
||||
return null;
|
||||
}
|
||||
$needle = (string) $value;
|
||||
foreach ($options as $opt) {
|
||||
$opt_value = is_array($opt) ? ($opt['value'] ?? null) : $opt;
|
||||
if (is_bool($opt_value) || !is_scalar($opt_value)) {
|
||||
continue;
|
||||
}
|
||||
if ((string) $opt_value === $needle) {
|
||||
return $opt_value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private function sanitize_field_value($value, array $field) {
|
||||
if (!empty($field['captcha_provider'])) {
|
||||
return function_exists('aksrt_captcha_sanitize_provider_config')
|
||||
@@ -898,6 +939,15 @@ class SchemaProvider {
|
||||
return $normalized !== null ? $normalized : (string) ($field['default'] ?? '');
|
||||
}
|
||||
|
||||
// Enum fields keep the option's declared value so the stored type always
|
||||
// matches what validate() compares against with in_array(..., true).
|
||||
if (in_array($type, ['select', 'radio', 'palette', 'image_select'], true) && empty($field['multiple']) && !empty($field['options'])) {
|
||||
$matched = $this->match_option_value($value, (array) $field['options']);
|
||||
if ($matched !== null) {
|
||||
return $matched;
|
||||
}
|
||||
}
|
||||
|
||||
if (in_array($type, ['fieldset', 'accordion'], true)) {
|
||||
return $this->sanitize_object_fields($value, (array) ($field['fields'] ?? []));
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user