From 4beb09307c36ec0ad853feeb9165fc5d0ad7842e Mon Sep 17 00:00:00 2001 From: laoyang Date: Fri, 23 Oct 2026 23:18:51 +0800 Subject: [PATCH] =?UTF-8?q?3.2.17:=20=E4=BF=AE=E5=A4=8D=E3=80=8C=E4=BF=9D?= =?UTF-8?q?=E5=AD=98=E8=AE=BE=E7=BD=AE=E6=97=B6=E9=9D=99=E9=BB=98=E6=B8=85?= =?UTF-8?q?=E7=A9=BA=E6=9C=AA=E6=8F=90=E4=BA=A4=E7=9A=84=E5=AD=97=E6=AE=B5?= =?UTF-8?q?=E3=80=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 现象 在主题设置里填了「附加日期」(例如 10-23),过一阵发现它变回空了, 于是当天的哀悼效果不再出现,而其他设置都好好的,看不出哪里坏了。 根因 inc/options-framework/includes/class-options-framework-admin.php 的 validate_options() 里,对每个选项无条件取 $input[$id] 去 sanitize: $clean[$id] = apply_filters('of_sanitize_'.$type, $input[$id], $option); 一旦某次提交里没有这个键(分组没渲染、字段被条件隐藏、表单中途出错只 提交了一半……),$input[$id] 是未定义索引 ⇒ null ⇒ sanitize_text_field() 返回空串 ⇒ 把用户存了很久的配置写成空。而且"清成空串"和"用户主动清空" 在库里完全一样,事后无法区分,只表现为"我明明填过,怎么没了"。 实测证据(线上 PHP 8.5.12,同一套用例) 修复前:3 通过 / 2 失败,失败项同时伴随 PHP Warning: Undefined array key "g_rip_extra" ... line 252 修复后:5 通过 / 0 失败 改法 - 循环外先取一次 get_option('kratos') 作 $saved_options(不在循环里查库) - 提交了(array_key_exists($id,$input))才 sanitize - 没提交则沿用 $saved_options 里的旧值;旧值也没有就不写这个键, 让运行时的默认值继续生效 - checkbox / multicheck 保持原语义:这两类"没提交"确实等于"没勾", 沿用旧值会导致开关关不掉。用 $is_toggle 单独判定,与"沿用旧值"互斥 为什么这个改动值得单独发一版 它不是界面问题,是**数据被静默改写**。凡是这种"用户没动、值却变了"的 缺陷,都会让人怀疑数据不可靠;而且每次后台保存都可能再触发一次。 验证 - 线上 _test_keep_old_value.php:5/5(修复前 3/5,用例 2 精确复现空串) - 本地 _static_317.php:8/8,第 4 组自带注入(改成老写法必须变红) - 全量流水线 verify_all.sh:rc=0 - 线上浏览器 _live315.py:纪念日分支 33/33、非纪念日分支 27/27 (判据改为「由配置侧独立算 expect_show,再与页面渲染对比」, 不再无条件断言"必须有灰度",也不再拿页面自身当预期值) --- .../class-options-framework-admin.php | 30 ++++++++++++++++++- style.css | 2 +- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/inc/options-framework/includes/class-options-framework-admin.php b/inc/options-framework/includes/class-options-framework-admin.php index 79db8a7..88c3f90 100644 --- a/inc/options-framework/includes/class-options-framework-admin.php +++ b/inc/options-framework/includes/class-options-framework-admin.php @@ -223,6 +223,11 @@ class Options_Framework_Admin { $clean = array(); $options = & Options_Framework::_optionsframework_options(); + // 已保存的值,供"字段未提交时沿用旧值"用。放循环外,避免重复查库。 + $saved_options = get_option( 'kratos' ); + if ( ! is_array( $saved_options ) ) { + $saved_options = array(); + } foreach ( $options as $option ) { if ( ! isset( $option['id'] ) ) { @@ -249,7 +254,30 @@ class Options_Framework_Admin { // For a value to be submitted to database it must pass through a sanitization filter if ( has_filter( 'of_sanitize_' . $option['type'] ) ) { - $clean[$id] = apply_filters( 'of_sanitize_' . $option['type'], $input[$id], $option ); + /* + * ⚠️ 表单里没有这个键时,**必须保留库里已有的值**,不能写空。 + * + * 原来这里直接写 `$input[$id]`。如果该字段没被提交(比如某个 + * 分组没渲染出来、或者字段被条件隐藏、或者表单中途出错只提交了一半), + * `$input[$id]` 是未定义索引 ⇒ 取到 null ⇒ + * `sanitize_text_field(null)` 返回 `''` ⇒ **把用户存了很久的 + * 配置静默清成空串**。而"清成空串"和"用户主动清空"在库里长得一模一样, + * 事后完全无法区分,只会表现为"我明明填过,怎么没了"。 + * + * 例外:checkbox 和 multicheck 上面已经把缺省项补成了 false, + * 这两类的"没提交"在语义上确实等于"没勾",保持原样。 + */ + $is_toggle = in_array( $option['type'], array( 'checkbox', 'multicheck' ), true ); + if ( array_key_exists( $id, $input ) ) { + $clean[$id] = apply_filters( 'of_sanitize_' . $option['type'], $input[$id], $option ); + } elseif ( $is_toggle ) { + $clean[$id] = apply_filters( 'of_sanitize_' . $option['type'], $input[$id], $option ); + } else { + // 没提交 ⇒ 沿用旧值(旧值也没有就不写这个键,让运行时默认值生效) + if ( array_key_exists( $id, $saved_options ) ) { + $clean[$id] = $saved_options[$id]; + } + } } } diff --git a/style.css b/style.css index 4acdb63..93adfc4 100644 --- a/style.css +++ b/style.css @@ -1,7 +1,7 @@ /* Theme Name: Kratos_Yang Text Domain: kratos_Yang -Version: 3.2.16 +Version: 3.2.17 Requires PHP: 7.0 Description: 这是一个Kratos主题的分支版本,用来增加一些定制化功能。 Tags: 博客, 双栏, 响应式, 自定义背景, 自定义颜色, 自定义图标, 自定义菜单, 特色图像, 收录优化, 邮件中心, 主题选项, 小工具, 文章置顶, 无障碍友好