聊到 ThinkPHP 自定义函数,有个常见误解需要先澄清:它并不是“随便写个文件就能用”的。关键取决于版本、存放位置和调用方式。TP6 和 TP3.x 的机制可以说是两套截然不同的体系,混用会直接报错 Call to undefined function,没有任何商量余地。下面按照实际开发中最常遇到的场景,把如何创建、放在哪里、如何调用、模板中如何使用,一次性讲清楚。
TP6:全局函数文件仅限 app/common.php 这一入口
首先明确几个要点。TP6 不再扫描 Common 目录,也不支持 app/Common/common.php 或 Common/common.php 这类路径。唯一会被自动加载的全局函数文件只有一个:
- 路径必须为
app/common.php(小写 app,无子目录,文件名同样小写) - 该文件在框架启动早期就会被
require_once加载,定义好的函数全程都可直接调用 - 无需添加
if (!function_exists('xxx'))包裹——它只加载一次,不存在重复定义的风险 - 函数体内不能出现
return、exit、die等中断语句,否则整个请求会提前终止 - 函数必须具有返回值;同时不能修改传入的参数(尤其不能使用
&$var引用传参)
来看一个具体例子:
function format_time($ts) { return date('Y-m-d H:i', (int)$ts);}
写完之后,在控制器里直接调用 format_time(time()) 即可。操作就是这么简单直接。
TP3.x:通过配置加载多个函数文件
TP3 的处理方式则完全不同。它默认加载 Common/common.php,但其他函数文件需要手动配置才能引入:
- 在
Conf/config.php中设置:'LOAD_EXT_FILE' => 'helper,utils' - 系统会自动加载
Common/helper.php和Common/utils.php - 每个扩展文件内部仍需添加
if (!function_exists('xxx')),因为模块级的 Common 文件可能被多次包含 - 不建议将所有函数都塞进一个大文件——调试麻烦、热更新慢、还容易产生命名冲突
模板中调用:必须使用 | 管道语法
最需要注意的地方在于,ThinkPHP 的模板引擎不支持 这种原生写法,也不接受命名参数或跳过参数。所有自定义函数的调用都必须走修饰器语法:
- 单参数函数:写成
{$time|format_time} - 多参数函数:用
###占位原变量位置。例如{$text|truncate=20,###,'…'}对应的是function truncate($str, $len, $suffix)这个函数 - 函数签名必须严格匹配:参数顺序固定,不能有引用形参,必须要有返回值
- 函数内部不能使用
$this或容器实例(如app()、db()),因为模板渲染时没有上下文对象
需要在 {:func()} 中使用?需先注册模板函数
如果想在模板里写 {:format_time($time)} 这种形式,TP6 需要显式注册:
- 打开
app/provider.php - 在
thinkemplatedriverThink的配置中添加:'format_time' => 'format_time'(全局函数)或'format_time' => 'apphelper::formatTime'(静态方法) - 未注册直接调用会报错:
Template Error: function not exists - 注册的函数同样必须有返回值,且不能依赖运行时上下文
这样做的目的是让模板引擎在编译阶段就能准确判断函数是否存在,避免运行时报错。理解了这套规则,ThinkPHP 的函数自定义也就不再神秘了。
