本文约 3,005 字,预计阅读 8 分钟。文中命令与配置均经过实测,可直接参考执行。
引言
你改完 functions.php 保存,刷新网站——白屏了。前台只留下一行”此站点遇到了致命错误。”(英文是 There has been a critical error on this website.),后台同样打不开。去服务器看 wp-content/debug.log,看到长长的一行:”PHP Fatal error: Cannot redeclare xxx() (previously declared in /wp-content/themes/你的主题/functions.php:120) in /wp-content/themes/你的主题/functions.php on line 260″。这类报错在给网站加自定义代码、从网上抄一段 snippet 复制粘贴的时候特别高发。它和普通语法错误不同,语法没错、代码本身能跑,问题是同一个函数被定义了两遍,PHP 不允许,于是直接终止整个请求。好消息是它的原因非常单纯,定位和修复都不难,本文把成因和几种标准解法一次讲透。
故障原因分析
- 函数真的被写了两遍。 最常见的场景是把一段代码复制进
functions.php,结果同一段函数前后粘贴了两次,PHP 在加载时第二次遇到同名函数就报”无法重复声明”。
- 子主题和父主题定义了同名函数。 很多人把父主题
functions.php里的一段函数原样复制到子主题去改,而父主题文件依然加载,父子两处都有同名函数,冲突就来了。
- 主题与插件撞名。 插件和主题各自定义了同名函数(尤其是名字很通用的
get_xxx()、custom_excerpt()这类),谁后加载谁触发报错。
- 同一个文件被引入两次。 用
require或include引入自定义文件时,如果引入动作执行了两遍,里面的函数会被定义两次。改用require_once就能避免。
- 代码片段类插件与主题里各写了一份。 用 Code Snippets 之类插件添加过一段函数,后来又在主题里加了同样一段,两处叠加导致重复。
分步解决方案
先定位两处声明。 报错信息是”金线索”,它明确告诉了你两个文件的路径和行号:
Cannot redeclare my_function() (previously declared in A.php:120) in B.php on line 260
A.php:120是第一次声明的位置。B.php:260是第二次(触发报错)的位置。
只要删掉其中一处、或把其中一处改名,报错立刻消失。若你在前台看不到这句话,就去 wp-content/debug.log 里找,方法是在 wp-config.php 中开启日志:
// wp-config.php
define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );
define( 'WP_DEBUG_DISPLAY', false );
方法一:删除或合并重复定义(最直接)
按上面定位到的两个文件,确认功能重复后,保留一份、删除另一份。如果是子主题复制父主题函数导致,删掉子主题里那份,改用下面方法三的”包裹”方式重写。
方法二:用 function_exists() 包裹(最通用)
无论函数出在哪个文件,都可以用 function_exists() 做存在性判断,保证同名函数只会被定义一次:
// functions.php(放在函数定义外层即可)
if ( ! function_exists( 'my_custom_excerpt' ) ) {
function my_custom_excerpt( $length = 100 ) {
// 你的函数逻辑
return $length;
}
}
这是 WordPress 生态里最推荐的写法,很多成熟主题和插件的自定义函数都用它兜底。它的好处是:谁先加载谁生效,后面遇到同名函数会安静地跳过,不会再白屏。
方法三:把 require 改成 require_once
如果报错来自”文件被引入两次”,检查你引入自定义文件的那一行:
// 错误写法:多处执行会重复加载
require get_template_directory() . '/inc/custom.php';
// 正确写法:只加载一次
require_once get_template_directory() . '/inc/custom.php';
方法四:给函数加命名前缀
如果同名函数分别来自两个不同的代码来源,又都舍不得改,可以给其中一个改名,加个独立前缀避免撞车:
// 修改前
function custom_breadcrumb() { /* ... */ }
// 修改后,加前缀降低撞名概率
function mimangfei_custom_breadcrumb() { /* ... */ }
改名前记得同步替换所有调用它的地方,否则会引发”函数未定义”的新错误。
方法五:把自定义代码放进插件
更规范的做法是把自定义函数放进一个自己写的小型插件(在 wp-content/plugins/ 下建目录和一个 PHP 文件,文件头写好插件注释),而不是塞进主题。插件在切换主题时依然生效,也更容易通过插件总开关一键停用,方便排查。
白屏后如何先恢复访问
如果连后台都进不去,可以用 FTP 把出问题的 functions.php 备份后,临时把新增的那段代码删掉或改回修改前的版本,让网站先恢复,再慢慢调试。不确定是哪段代码时,先停用所有主题相关自定义文件,或切回官方默认主题。
常见踩坑点
- 只看行号就动手改。 要同时看”first declared”和”on line”两处,只删一处往往不够,因为可能有三处重复。
function_exists()包错了位置。 它必须包在函数定义的最外层,包在函数体里面毫无作用。- 改名后忘了改调用。 给函数加前缀后,所有引用它的地方都要一起改,否则会变成”Call to undefined function”。
- 在父主题里改函数。 直接在父主题
functions.php里改,主题一更新就被覆盖;应该用子主题或自定义插件。 - 调试完好习惯没养成。
WP_DEBUG长期开着会向前台暴露路径信息,排查完记得关闭或至少关闭显示。 - 忽视缓存。 有些主机开了 OPcache,改完 PHP 文件需要清一次或重启 PHP,否则可能还在跑旧代码,误以为没修好。
总结
Cannot redeclare function 这个报错名字很直白:同一个函数被声明了两次,PHP 就直接罢工。它的报错信息会精确给出两个文件路径和行号,这是最省事的线索——照着删掉或合并其中一处即可。日常写代码时,养成两个习惯就能规避绝大部分此类问题:自定义函数统一用 function_exists() 包裹,引入文件统一用 require_once。如果代码需要长期保留,建议做成自定义插件,既不受主题切换影响,也方便随时停用。把这几招用熟,再遇到 PHP 致命错误时你就不会手足无措,能几分钟内让网站恢复访问。
