用 require 替代 require_once 最稳妥,因 ThinkPHP 自身大量使用 require_once 会导致 opcache 失效,且其基于绝对路径的去重在 Web/CLI 环境下因工作目录不同而失效,引发重复加载。

用 require 替代 require_once,配合手动控制加载逻辑,是最稳妥的方式。
为什么 require_once 在 ThinkPHP 里反而容易出问题
ThinkPHP 框架自身已大量使用 require_once 加载核心类和配置,叠加你再用,会导致 PHP 的 opcache 无法有效缓存文件路径映射,反而降低性能。更重要的是,require_once 的“唯一性”判断基于文件的**绝对路径**,而 TP 在不同运行环境(如 Web 请求 vs 命令行)下工作目录可能不同,__DIR__ 或相对路径解析结果不一致,导致同一文件被多次加载——你以为它没重复,其实它悄悄重复了。
推荐做法:require + 显式标记
改用 require,但通过一个全局数组记录已加载的文件路径,由你自己控制是否跳过:
- 在公共入口文件(如
public/index.php)顶部定义:$loadedFiles = []; - 封装一个安全引入函数:
function safeRequire($file) {<br> global $loadedFiles;<br> $realPath = realpath($file);<br> if ($realPath && !in_array($realPath, $loadedFiles)) {<br> require $file;<br> $loadedFiles[] = $realPath;<br> }<br>} - 后续 anywhere 都调用
safeRequire('./config/database.php');,路径统一转为真实路径,避免因软链或符号路径导致误判
框架内更自然的替代方案
ThinkPHP 5.1+ 原生支持自动加载和配置合并,多数场景根本不需要手写 include:
立即学习“PHP免费学习笔记(深入)”;
- 配置文件统一放
config/目录下,用config('filename')读取,框架自动合并、缓存,无重复加载风险 - 工具函数写成独立类,放进
extend/并注册到自动加载映射:\think\Loader::addClassMap(['Helper' => APP_PATH . 'extend/Helper.php']);
之后直接new Helper(),由 autoload 机制保障只加载一次 - 模板中复用代码用
{include}标签,如{include file="public/header"},TP 模板引擎内部已做去重处理
特别注意命令行场景
在自定义命令(php think my:command)中,getcwd() 往往是项目根目录,而 Web 请求下可能是 public/。所以不要依赖 ./xxx.php 这种相对写法:
- 一律用
realpath(__DIR__ . '/../config/extra.php') - 或借助 TP 常量:
APP_PATH、CONFIG_PATH、RUNTIME_PATH等,它们始终指向明确位置



















