TP5默认使用PDO作为底层数据库驱动,必须通过database.php中params显式配置PDO行为(如ERRMODE、INIT_COMMAND等),且DSN需包含charset=utf8mb4,否则易出现静默失败或中文乱码。

PDO 是 ThinkPHP5 默认的数据库底层驱动,不是可选插件,而是框架运行时强制依赖的连接方式。只要你用的是 TP5(包括 5.0、5.1),只要配置了数据库,它就在用 PDO —— 你不需要手动写 new PDO(),也不需要在控制器里自己实例化连接对象。
但很多人卡在“连不上”“中文乱码”“报错没提示”上,问题往往出在 PDO 层级的参数没对齐,而不是 TP 配置项写错了。
database.php 里必须显式配 params 才能控制 PDO 行为
ThinkPHP 的 database.php 配置表面看是“数据库账号密码”,实际它会把 params 数组原样透传给 PDO 构造函数。不配 params,就等于让 PDO 用默认行为启动,而默认行为在很多场景下不兼容生产环境。
常见漏配项:
立即学习“PHP免费学习笔记(深入)”;
-
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION:不设这个,SQL 错误只会静默失败,Db::table()->select()返回空数组,你根本不知道哪错了 -
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8mb4':不设这个,中文插入或读取大概率乱码(尤其 emoji 或生僻字) -
PDO::ATTR_PERSISTENT => true:高并发下建议开启,但要注意 MySQL 服务端的wait_timeout设置,否则可能拿到失效连接
示例片段(放在 database.php 的 return 数组内):
```php
'params' => [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8mb4',
PDO::ATTR_PERSISTENT => false, // 开发环境建议关掉,方便调试连接生命周期
],
```
charset=utf8mb4 必须写进 DSN,不能只靠 SET NAMES
TP5 的 DSN 字符串(即 'dsn' => 'mysql:host=...')如果没带 charset=utf8mb4,即使你在 params 里写了 SET NAMES,PDO 初始化阶段仍可能按 latin1 建立连接,导致部分字段(如 json 类型、带 emoji 的 VARCHAR)写入失败或截断。
正确写法(注意末尾的 charset=utf8mb4):
```php 'dsn' => 'mysql:host=127.0.0.1;port=3306;dbname=thinkphp;charset=utf8mb4', ```
错误写法(缺 charset):
```php 'dsn' => 'mysql:host=127.0.0.1;dbname=thinkphp', // ⚠️ 这里没指定 charset,PDO 会 fallback 到服务器默认值 ```
连接失败时,PDOException 不抛到页面?检查 APP_DEBUG 和异常处理器
TP5 默认在 APP_DEBUG = true 时才显示完整异常堆栈。如果你关了调试模式,又没配自定义异常处理,PDO 抛出的 PDOException 会被静默吞掉,只返回 500 页面,看不到具体是“拒绝连接”还是“数据库不存在”。
排查步骤:
- 确认
APP_DEBUG为true(开发阶段不要关) - 检查
config/app.php中是否覆盖了exception_handle,且该类有没有忽略PDOException - 临时加一行测试代码:
try { Db::query('SELECT 1'); } catch (\PDOException $e) { dump($e->getMessage()); }
真正容易被忽略的一点:TP5 的 Db 类做了惰性连接,Db::table('user')->where(...)->find() 这种链式调用,直到真正执行查询时才建立连接。所以「配置写对了但一直没报错」不等于「连上了」,得真查一次数据才能验证。



















