require_once 不报错是因为它会检查文件是否已加载,避免重复解析类定义;但路径判定敏感、缓存配置(APC/OPcache)或命名空间误用仍可能导致冲突。

为什么 require 变成 require_once 就不报错了
因为 require 每次都会无条件加载并执行文件,如果同一类文件被多次 require,类定义就会重复执行,触发 Fatal error: Cannot redeclare class。而 require_once 会检查该文件是否已被加载过,避免二次解析。
但要注意:这个“已加载”是按文件路径判定的,不是按内容。所以以下情况仍会出错:
-
require_once 'A.php';和require_once './A.php';被视为两个不同路径,可能重复加载 - 软链接或不同大小写的路径(如
Class.phpvsclass.php)在某些系统上也会绕过require_once的去重逻辑
class_exists() 判断要不要加第二个参数 false
用 class_exists('MyClass') 包一层再加载,确实能兜底,但默认行为会触发自动加载器(比如 spl_autoload_register),可能造成意外副作用或性能损耗。
推荐写法是显式关掉 autoload 触发:
立即学习“PHP免费学习笔记(深入)”;
if (!class_exists('MyClass', false)) {
require_once __DIR__ . '/MyClass.php';
}
这样只查已加载的类,不调 autoload,更可控。适用于 legacy 项目中无法统一改 require_once 的场景。
APC / OPcache 导致的“明明没重复却报错”
老项目如果还开着 APC(尤其是 apc.include_once_override=1),或者 OPcache 开了 opcache.revalidate_path=0,就可能出现:文件实际只被引入一次,但缓存层误判为多次声明。
验证方式很简单:
- 临时停掉 PHP 缓存:
sudo systemctl restart php-fpm或opcache_reset() - 如果重启后错误消失,基本就是缓存配置问题
- APC 用户可设
apc.stat=0+apc.canonicalize=0;OPcache 用户重点检查opcache.validate_timestamps是否为On
命名空间和全局空间混用时的隐藏陷阱
类名冲突不一定来自你写的代码——比如定义了 class Com {},但在 Windows 下 Com 是 PHP 内置 COM 扩展的保留类名,Linux 不报,Windows 直接 Fatal。
更隐蔽的是命名空间漏写:
- 文件开头没写
namespace App;,但类文件又被 PSR-4 自动加载器按App\SomeClass去找,结果在全局空间里又定义了一次同名类 - 用
echo __NAMESPACE__;快速确认当前文件实际所处空间,空字符串说明在全局空间 - 检查
use语句是否拼错,或文件开头有 BOM 头(导致namespace声明失效)
这类问题不会在单文件测试时暴露,只在组合加载后浮现,最容易被忽略。



















