[aru_12]前两天博客把 PHP 切到 8.3,文章能打开,拉到评论区却多了两行 Warning。昵称、邮箱这些输入框还在,警告就夹在表单中间,看着挺扎眼。
这次我翻了主题的评论模板,也拿旧文件和修补后的文件分别跑了一遍。最后只补了一行取用户 ID 的代码,顺便把 PHP 的变量作用域重新记了一遍。以后再碰到这种“升级后才看见”的问题,也有个排查顺序。

先跟着报错找文件
警告的核心是这一句,后面跟着主题文件名和行号:
Warning: Undefined variable $user_ID
wp-content/themes/mkBlog/comments.php,原文件第 30、51 行 翻到对应位置,都是在判断当前访客有没有登录。原模板里还有一处相同变量,用来处理“必须登录才能评论”的设置。我把相关判断摘出来,结构大致是这样:
if (get_option('comment_registration') && !$user_ID) {
// 显示登录提示。
}
if ($user_ID) {
// 显示当前用户信息。
}
if (!$user_ID) {
// 显示游客的昵称、邮箱、网址输入框。
} 这三处都读了 $user_ID,但模板前面没有给它赋值。所以我先查这个变量从哪里来,没有急着改评论插件,也没有去动数据库。
这里也得记准:“未定义变量”并不是 PHP 8.3 新增的规则。PHP 官方文档说明,从 PHP 8.0 起,这类访问会产生 E_WARNING,更早是 E_NOTICE。这次切换版本后页面显示了警告,但没有旧环境配置,不能断定以前完全没发生过。
外面有这个变量,模板就一定能拿到吗?
我最开始也容易把“WordPress 里有用户信息”和“这个文件里能直接使用这个变量”混在一起。实际查了这份 WordPress 的 comments_template():它在函数里面加载评论模板,并没有把 $user_ID 导入这个函数的局部作用域。
PHP 的 include 会继承执行位置的作用域。要看清这件事,可以在一个空目录里建两个小文件。下面是刻意保留问题的演示,不要拿来覆盖主题。
第一个文件叫 scope-demo.php:
<?php
error_reporting(E_ALL);
ini_set('display_errors', '1');
$user_ID = 7; // 演示用的全局变量,不是真实登录用户。
function render_comments_demo() {
include __DIR__ . '/scope-view.php';
}
render_comments_demo(); 第二个文件叫 scope-view.php:
<?php
echo $user_ID > 0 ? "已登录
" : "游客
"; 在这个目录执行 php scope-demo.php。虽然外面明明写了 $user_ID = 7,函数里的模板还是会报未定义变量,随后输出“游客”。我在本地 PHP 8.3.35 上复现了这个结果。上面的 display_errors 只用于这个命令行小实验。
回到博客,这个问题就不只是页面多两行字:登录判断走错分支,还可能让已登录用户看到游客输入框,或者在开启登录限制时看到登录提示。
我在模板开头补了这一行
这份主题的 comments.php 开头已经有访问保护、密码保护和评论开关判断。我保留这些逻辑,在它们后面、HTML 输出之前补上:
$user_ID = get_current_user_id(); 这行代码要放在已经加载 WordPress 的主题模板中使用,单独新建 PHP 文件运行是没有这个函数的。它返回当前登录用户的整数 ID;未登录时返回 0,刚好可以继续供原来的几个判断使用。
也可以用 global $user_ID; 把全局变量引进来,但我这里选择主动调用 WordPress 的取值函数,变量来源更清楚。不能为了消掉警告就写成 $user_ID = 0;,那会把所有人都当成游客。
如果只是判断有没有登录,新写的模板也可以考虑 is_user_logged_in()。这份旧模板后面还需要用户 ID 获取资料,所以这次保留变量名,集中赋值,改动小一点。
改完以后,我怎么确认没改坏
先做语法检查。这里的路径按自己下载到本地的目录调整:
php -l wp-content/themes/mkBlog/comments.php 不过语法通过,只说明 PHP 能解析这个文件。我还用测试替身提供 WordPress 函数,在函数作用域里加载真实模板,分别渲染了这些情况:
- 普通游客能看到评论表单;开启登录限制时,游客能看到登录提示。
- 登录用户能看到自己的信息;开启登录限制后,仍然可以看到表单。
- 带有历史评论信息的回访游客,能看到欢迎语和保存的资料。
- 关闭评论且没有旧评论时不输出评论区;有旧评论时仍显示列表和关闭提示。
- 受密码保护的内容,不输出这份评论模板。
本地 PHP 8.3.35 下共检查了 8 种场景。旧模板有 5 种未通过,除了未定义变量警告,还包括登录用户显示了错误的内容;补上取值后,8 种全部通过,语法检查也通过。
这轮检查覆盖的是模板渲染,WordPress 函数由测试替身提供,没有连接线上数据库,也没有提交真实评论。所以文件上传以后,我还会分别用无痕窗口和登录状态打开文章,检查表单,再确认真实评论提交、回复是否正常。模板测试不能代替这一步。
覆盖文件时,顺手记住这几件事
先备份服务器上当前的 comments.php,再上传修补文件。不要直接拿很早以前下载的整套主题覆盖线上,以免把后来加的作者榜或其他修改一起还原了。改主题文件也要留好差异,后续更新主题时可能被覆盖。
上传后如果仍然看到原来的警告,先确认文件传到了当前启用的主题目录,再清理页面缓存、CDN 缓存。若服务器有 OPcache,也要确认它已经重新读取文件;别对着一份旧页面反复改代码。
生产页面不适合把服务器路径和警告直接展示给访客,错误应记录到日志。但关掉页面错误显示,只能改变提示出现的位置,不能修正这里的登录判断。我这次先把变量来源补上,再去检查显示和日志配置。
这次留下的笔记很短:看到 Undefined variable,先找赋值,再看加载位置和作用域。尤其是老主题里那些看起来“到处都能用”的变量,升级环境时值得重新确认一次。
查资料时用到的官方说明也放这里,方便以后回看:
- PHP:变量作用域,包含 include 的作用域规则及未定义变量的警告级别。
- WordPress:comments_template(),可以直接查看模板的加载代码。
- WordPress:get_current_user_id(),核对返回值和未登录的情况。
🎨 原创不易,支持请点赞、转载请注明本文作者为老K