TP6 的 strict() 是模型/查询构建器的字段存在性校验开关,启用后写入非法字段立即抛异常,禁用则静默丢弃;仅作用于 insert/save/replace 等写入操作,与 PHP declare(strict_types=1) 无关。

strict() 是 TP6 数据写入的字段校验开关
在 ThinkPHP6 中,strict() 不是 PHP 语言层的严格模式,而是模型/查询构建器提供的一个连贯操作方法,专门控制「新增或更新数据时是否校验字段合法性」。默认情况下,TP6 会静默丢弃 $data 中不存在于数据表结构里的字段;启用 strict(true) 后,只要传了数据库里没有的字段,立刻抛出异常——不是警告,是中断执行。
- 常见错误现象:
Db::name('user')->strict(true)->insert(['name' => '张三', 'age' => 25, 'avatar_url' => 'xxx.jpg'])报错SQLSTATE[HY000]: General error: 1364 Field 'avatar_url' doesn't have a default value(其实更可能是字段根本不存在),但你查表发现真没这个字段,却一直没意识到 —— 关闭 strict 就默默删掉它,掩盖问题 - 使用场景:开发联调阶段、CI/CD 自动化插入测试数据、DB Schema 变更后快速暴露字段不一致
- 参数差异:
strict(true)开启校验,strict(false)或不调用则走默认宽松策略;注意它只影响insert()、save()、replace()等写入操作,对select()无效 - 性能影响几乎为零,但会改变错误行为路径:从“静默丢弃 → 成功返回”变成“立即抛异常”,这对调试友好,对线上兜底逻辑有要求(得加 try/catch)
strict 和 PHP 的 declare(strict_types=1) 完全无关
新手常混淆这两者。TP6 的 strict() 是框架级的数据层校验,而 declare(strict_types=1) 是 PHP 解释器级别的类型检查,作用在函数参数和返回值上。你在控制器里写 $model->strict(true)->save($data),跟当前文件有没有 declare(strict_types=1) 一点关系都没有。
- 容易踩的坑:在控制器顶部加了
declare(strict_types=1),就以为字段校验也开启了,结果依然丢字段不报错 - 另一个坑:把
strict()误当成全局配置,试图在中间件里统一开启 —— 它是链式调用的一部分,必须在每次写入前显式调用 - 兼容性:仅适用于 TP6.0+,且依赖底层数据库连接驱动支持元信息读取(如 MySQL 的
SHOW COLUMNS),SQLite 或某些精简驱动可能无法准确判断字段是否存在
控制器里怎么安全地用 strict()?
直接在控制器方法中调用 strict(true) 没问题,但要注意它不自动回滚事务,也不捕获异常。一旦字段不匹配,整个请求就崩了,前端看到 500,日志里留一条 PDOException。
- 推荐做法:只在开发环境或单元测试中无条件开启;生产环境慎用,或包裹在 try/catch 里并降级处理(比如记录告警 + 删除非法字段后重试)
- 示例:
try { Db::name('order')->strict(true)->insert($postData); } catch (\think\Exception $e) { // 记录字段不匹配日志 \think\Log::error('strict insert failed: ' . $e->getMessage()); // 降级:去掉未知字段再试一次(可选) $safeData = array_intersect_key($postData, array_flip(Db::getFields('order'))); Db::name('order')->insert($safeData); } - 别把它当数据过滤用:它不校验值类型(比如字符串塞进 INT 字段)、不防 SQL 注入、不替代验证器(
Validate)—— 该用验证器的地方,strict()一概不管
strict() 是个“字段存在性快照开关”,不是万能校验器,也救不了设计混乱的表结构。最容易被忽略的一点是:它只在校验「字段名」,不校验「字段是否允许为空」「是否有默认值」「是否唯一」——这些仍由数据库约束兜底,strict() 连碰都不碰。



















