$options */ private function normalize_case_insensitive_option($value, array $options): ?string { if (!is_string($value) || $value === '') { return null; } $lower = strtolower(trim($value)); foreach ($options as $opt) { $opt_value = is_array($opt) ? ($opt['value'] ?? null) : $opt; if (is_string($opt_value) && strtolower($opt_value) === $lower) { return $opt_value; } } 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 $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; } }