为何PDO::FETCH_CLASS处理小结果集正常,大结果集失效?
问题
我尝试用PDO::FETCH_CLASS把SQL查询结果转换成BibliaTextus类实例,小结果集下代码运行正常,但处理返回83行数据的查询时出错。该查询返回两列,一列是int类型,另一列是text类型。
报错信息
Fatal error: Uncaught TypeError: App\Models\BibliaLectio::add(): Argument #1 ($bibliaTextus) must be of type App\Models\BibliaTextus, array given, called in /MYROUTE/src/App/Domain/Infrastructure/Mapper/Biblia/PdoBibliaMapper.php on line 60 and defined in /MYROUTE/src/App/Models/BibliaLectio.php:10
相关代码
PdoBibliaMapper
//.. $bibliaLectio= new \App\Models\BibliaLectio(); $stmt=$this->pdo->run($sql,$params); $stmt->setFetchMode(PDO::FETCH_CLASS, '\App\Models\BibliaTextus'); while ($row = $stmt->fetch()) { $bibliaLectio->add($row); #Line 60 }
BibliaTextus
namespace App\Models; class BibliaTextus { private int $v; private string $t; public function toHtml() { return sprintf('<span class="versiculo">%s</span> %s',$this->v,$this->t); } }
BibliaLectio
namespace App\Models; class BibliaLectio { private array $list; public function add(BibliaTextus $bibliaTextus) #Line 10 { $this->list[]=$bibliaTextus; } public function toHtml() { $s='<h1>Texto Bíblico</h1><div class="textus-title"><p>'; foreach ($this->list as $t) { $s.=$t->toHtml(); } $s.='</p></div>'; $s.='<p class="bibliaTextus"><a href="https://example.com/biblia" target="_blank" rel="nofollow">Some Text</p>'; return $s; } }
原因分析
问题核心是大结果集中存在不符合类属性类型声明的数据:
BibliaTextus的$v属性声明为int类型,但结果集中某一行的v列可能是null、空字符串或非数值内容。- 当PDO尝试用
FETCH_CLASS实例化类时,无法将无效值赋值给强类型的私有属性,此时会 fallback 返回数组而非类实例。 - 小结果集恰好没有这类无效数据,所以能正常生成类实例;大结果集包含无效数据,就触发了类型错误。
解决方案
1. 排查并修复无效数据
直接查看SQL查询结果,定位到导致错误的行(通常是第83行附近),检查v列的数据是否为有效的整数,修复不符合要求的数据。
2. 调整类属性的类型声明(允许空值)
如果业务逻辑允许v列为空,将BibliaTextus的属性修改为可空类型:
private ?int $v; // 允许int或null private string $t;
3. 开启PDO异常模式,精准定位错误
在初始化PDO时开启异常错误模式,这样当实例化类失败时会直接抛出异常,方便定位具体问题行:
$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
4. 手动映射数据(更可控)
放弃FETCH_CLASS,手动将数组数据映射到类实例,确保类型转换正确:
// 修改PdoBibliaMapper中的循环部分 while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { // 利用构造函数初始化私有属性 $textus = new \App\Models\BibliaTextus((int)$row['v'], $row['t']); $bibliaLectio->add($textus); }
对应的,给BibliaTextus添加构造函数:
namespace App\Models; class BibliaTextus { private int $v; private string $t; public function __construct(int $v, string $t) { $this->v = $v; $this->t = $t; } public function toHtml() { return sprintf('<span class="versiculo">%s</span> %s',$this->v,$this->t); } }
内容的提问来源于stack exchange,提问作者A. Cedano
相关产品推荐
相关产品推荐

