laoyang
|
704a9346e1
|
3.2.12 紧急修复:点「测试邮件」引发无限递归、内存耗尽
3.2.11 引入的事故。线上表现:
Allowed memory size of 268435456 bytes exhausted
in .../wp-includes/class-wp-block-parser.php on line 91
前台首页、后台登录页都正常,只有点那个按钮才炸。
根因不在区块解析器 —— 那只是递归链最后执行到的一行,看它会完全跑偏。
validate_options() 是 register_setting() 注册的 sanitize_callback,
WP 把它挂在 `sanitize_option_kratos` 过滤器上(wp-includes/option.php:3073)。
3.2.11 为了让"刚改的 SMTP 配置立刻生效",在这个回调里调了
update_option('kratos', $clean)。而 update_option() 内部**会再跑一遍**
同名过滤器 ⇒ 又调回 validate_options ⇒ 再 update_option ⇒ 无限递归。
每层都在复制 $clean 派生的大数组,直到吃满 256M。
本地实测确认递归存在:回调被调用 3 次、嵌套深度 2。
改法:不写库,改用一次性覆盖层。
- 分支里把 $clean 放进 $GLOBALS['__yang_options_override']
- kratos_option() 优先读它,取不到再读库
- 效果等价(点测试用的就是刚填的值),但不触发任何过滤器,不可能递归
- 覆盖层是本请求内的全局,不落库、不跨请求残留
assertions: run_tests 104 → 110
- 分支里**没有** update_option(这条是红线,直接对应本次事故)
- 分支里有覆盖层
- 覆盖层真生效:kratos_option 优先返回新值
- 覆盖层未命中的键仍读库(不能把别的配置一起抹掉)
- 撤掉覆盖层回到库值(不跨请求残留)
- 桩与产品代码一致:直接读 autoload.php 确认它也认覆盖层
(桩比产品温和是这次漏检的根源之一 —— 桩里 update_option 不挂
sanitize 过滤器,所以注入递归 bug 后 harness 根本不崩)
顺带修了两个测试基础设施问题:
1. wp_stub.php 的 kratos_option 与真实实现语义不一致。已同步,
并加断言防止两边再次漂移(否则"测的是桩、不是产品代码")。
2. falsify 的 rebuild() 没把被 file_get_contents 读的 autoload.php
复制进副本 ⇒ 新断言读不到文件 ⇒ 基线假红。
falsify 24 → 25:注入「分支里调 update_option」能抓住(靠静态红线,
不靠跑崩)、注入「去掉覆盖层」能抓住、注入「退回裸 wp_mail」能抓住。
全量 14 组全绿;run_tests 110 / run_tests2 154 / falsify 25。
|
2026-10-23 18:49:06 +08:00 |
|
laoyang
|
9a053169e9
|
3.2.11 后台「测试邮件」改走主题模板,并修发信早于落库
两个问题叠在一起,都不报错,只是「测了等于没测」:
1. 按钮发的是纯文本
validate_options() 里 isset($_POST['sendmail']) 那个分支只有一句裸
wp_mail(),正文是「恭喜您 SMTP 邮件服务配置成功!」。于是点测试收到的
东西和真实评论通知(HTML 模板)完全不同,没法用它验收版式。
主题里 yang_mail_send_test() 本来就是干这件事的,但全仓库零调用点 ——
注释写着「后台测试按钮走这条」,实际上从没接上线。
2. 发信发生在配置落库之前
validate_options() 是 register_setting 的回调,返回值之后才由 WP
写进数据库。在分支里直接发信,主题配 PHPMailer 时读 kratos_option()
拿到的还是上一次保存的主机/账号/密码。刚改完 SMTP 密码点测试,
测的是旧密码,表现成「明明填对了却发不出去」。
改法:
- 分支里先 update_option('kratos', $clean) 再调 yang_send_test_mail()
- yang_send_test_mail() 优先调主题的 yang_mail_send_test();
主题被换掉时退回原来的纯文本,不能因此白屏
- send_mail_notice() 从「一律发送完成」改成三态:WP_Error / wp_mail
返回 false / 成功,前两者用 error 样式。原来密码错了也显示成功,
用户会去邮箱里干等。
assertions: run_tests 95 → 104 条(新增 2k 组 8 条 + 1 条)
- sendmail 分支确实调用主题发信函数、确实先落库
- 落库位置在发信位置之前(判顺序,不判有没有)
- 分支里没有裸 wp_mail
- 发信函数对主题缺失做了兜底
- 提示语分三态,两类失败都用 error 样式
falsify 22 → 24 条:
- 注入「先发信后落库」⇒ 变红(这条一开始是假绿:判据比的是注释里
那两个词谁先出现,注释里 update_option 和函数名都在,恒真。
改成先剥注释再比代码里的位置)
- 注入「退回裸 wp_mail」⇒ 变红
顺带修了 falsify 的一个基础设施坑:options-framework 那批文件在工作区
是 CRLF,锚点里写 \n 对多行内容恒不匹配,报的是「命中 0 次」,
很像锚点选错了。inject() 现在先试原样、不中再把 \n 换 \r\n 重试一次。
全量 12 组 / 506 条断言全绿。
|
2026-10-23 18:28:53 +08:00 |
|