Hyperf 3.0 中 Db::select() 默认返回 stdClass 对象而非关联数组,因 PDO fetch_mode 被设为 PDO::FETCH_OBJ;应配置 db.php 中 fetch_mode 为 PDO::FETCH_ASSOC 并清缓存,或多库逐个配置,或通过 StatementPrepared 事件监听统一设为 FETCH_ASSOC。

Hyperf 3.0 升级后 Db::select() 默认返回 stdClass 对象,而不是你习惯的关联数组 —— 这是因为底层 PDO 的 fetch_mode 配置被重置为 PDO::FETCH_OBJ(或未显式继承配置),而旧版本可能默认沿用 PDO::FETCH_ASSOC。
确认当前 fetch_mode 实际值
别猜,直接查。在任意控制器里加一行调试:
var_dump(config('databases.default.fetch_mode'));
如果输出是 5(即 PDO::FETCH_OBJ 的整数值),或根本没定义、为 null,就坐实了问题根源。
- Hyperf 3.0 的
config/autoload/db.php中fetch_mode默认值确实是PDO::FETCH_ASSOC,但这个配置只对新创建的连接生效;若项目早期手动改过连接初始化逻辑,或用了自定义Connection类,该配置可能被绕过 - 多库场景下,
default配置不自动透传到其他库名(如mysql2),需单独检查对应键
优先修改配置文件(最轻量)
打开 config/autoload/db.php,确保 default 下明确声明:
'fetch_mode' => PDO::FETCH_ASSOC,
注意:必须是 PDO::FETCH_ASSOC 常量本身,不能写字符串 'PDO::FETCH_ASSOC' 或数字 2(虽然值相同,但部分版本会因类型判断失败而 fallback)。
- 改完后清缓存:
php bin/hyperf.php clear:config,否则 config 不重载 - 若使用多库,每个库配置块都要加这一行,不能只配
default - 该配置仅影响
Db::select()等原生查询,不影响 Eloquent 模型查询结果(模型始终返回对象)
监听 StatementPrepared 事件(兼容旧逻辑)
如果你的项目已存在自定义连接初始化、或需要对所有查询强制统一行为(比如 legacy 代码大量依赖数组结构),用事件监听更可靠:
新建 app/Listener/FetchModeListener.php,内容如下:
#[Listener]
class FetchModeListener implements ListenerInterface
{
public function listen(): array
{
return [StatementPrepared::class];
}
<pre class="brush:php;toolbar:false;">public function process(object $event): void
{
if ($event instanceof StatementPrepared) {
$event->statement->setFetchMode(PDO::FETCH_ASSOC);
}
}}
- 这个监听器在每次 PDOStatement 准备好后立刻执行,比配置更底层,100% 覆盖所有
Db::*查询 - 不需要改业务代码,对现有
Db::select()调用零侵入 - 注意命名空间和 use 语句要完整,
StatementPrepared来自Hyperf\Database\Events\StatementPrepared
临时兜底:手动转换(慎用)
只建议用于单点修复或调试,不要全局推广:
$rows = Db::select('SELECT * FROM users WHERE status = ?', [1]);
$rows = array_map(fn($row) => (array)$row, $rows);
这种写法成本高(每次遍历 + 强制转换),且丢失类型信息(如 NULL 字段可能变成空字符串)。
- 若字段含 JSON 字符串,
(array)不会自动json_decode,仍需额外处理 - 协程环境下频繁做 array_map 可能轻微拖慢吞吐,高 QPS 场景应避免
真正要命的是:一旦漏掉某处调用,后续代码按数组访问却拿到对象,就会抛 Trying to get property 'xxx' of non-object 错误 —— 这类 bug 很难静态扫描发现。


















