ThinkPHP工具类封装需分清用途与调用者:无状态纯函数放app/common.php全局加载;有状态跨模块逻辑封装为Service类并依赖注入;重复验证逻辑用Trait抽离;模板中调用函数须按管道符或注册规则。

ThinkPHP中封装工具类,核心是分清“用在哪”和“谁来调”。不是所有功能都适合塞进一个类里,也不是所有函数都要变静态方法。关键看它有没有状态、是否跨模块、要不要复用——没状态、纯计算、到处用的,适合封装成独立函数;带上下文、需依赖注入、逻辑较重的,就该做成Service类或Trait。
通用无状态函数:统一放 app/common.php(TP6)
TP6只认这一个全局函数文件,路径必须是 app/common.php(小写,不能有子目录)。里面定义的函数自动加载,控制器、模型、命令行都能直接调用。
- 函数必须有返回值,禁止 exit/die/echo/print_r 等输出或中断语句
- 不要用 &$param 引用传参,避免意外修改原始数据
- 示例:时间格式化、字符串截断、安全过滤
比如写一个安全过滤函数:
function sanitize_input($str) {
return htmlspecialchars(trim((string)$str), ENT_QUOTES, 'UTF-8');
}
立即学习“PHP免费学习笔记(深入)”;
控制器里直接用:sanitize_input($request->param('name'))
跨模块工具类:用 Service 层承载有状态逻辑
当功能涉及数据库操作、外部 API 调用、缓存读写、事务控制时,别写在控制器里,也别硬编码 new 类。应封装为 Service 类,通过依赖注入使用。
- 类放在 app/common/service/ 下,如
UserAuthService.php - 构造方法接收 Model、Db 或 Config 实例,不自己 new
- 方法只做一件事:比如
login($credentials)负责校验+生成 token+记录日志,不跳转、不渲染 - 控制器中通过容器获取:
$this->authService = app(UserAuthService::class);
这样换登录方式(JWT 改 Session)、加风控逻辑、接消息队列,改 Service 即可,控制器完全不动。
验证逻辑:优先用 Trait 抽离复用
验证规则重复写在多个控制器里?scene 不生效?报错定位难?说明验证行为没解耦。用 Trait 是最轻量又可控的方式。
- 新建 app/common/trait/ValidatesForm.php
- 只提供一个
validateForm($data, $validatorClass, $scene = null)方法 - 内部必须用
new $validatorClass()实例化,再调check(),否则 $scene 不起作用 - 验证类中用
protected $scene = ['edit' => ['username', 'email']]控制字段,不建多个类
控制器里统一写:$this->validateForm($post, UserValidate::class, 'register');
模板中调用函数:注意语法和注册规则
模板引擎不支持直接写 <?php echo xxx(); ?>,也不认普通函数名。必须按规范来:
- 单参数函数:用管道符
{$time|format_time} - 多参数函数:用
###占位,如{$text|truncate=20,###,'…'} - 想用
{:format_time($time)}这种写法?得在 app/provider.php 的模板配置里显式注册 - 函数内不能访问
$this、app()、input()等运行时对象,模板渲染阶段无 HTTP 上下文
不复杂但容易忽略。



















