Cannot change default fetch mode while fetching¶
Description¶
PDOStatement::setFetchMode() refuses to run while the statement is in the middle of fetching a row. This happens when PDO::FETCH_CLASS is used and the constructor of the target class receives the statement object itself (via the constructor arguments) and calls setFetchMode() on it before the current fetch() call has finished.
Prior to this guard, such a re-entrant call could corrupt the statement’s internal fetch state and lead to a use-after-free. PDO now detects the situation and throws an Error instead.
Example¶
<?php
class Test {
public string $val1;
public string $val2;
public function __construct(mixed $stmt) {
if ($stmt instanceof PDOStatement) {
// Re-entrant call -- the statement is still fetching the current row
$stmt->setFetchMode(PDO::FETCH_CLASS, self::class, [$this->val2]);
}
}
}
$stmt = $pdo->prepare('SELECT val1, val2 FROM example');
$stmt->setFetchMode(PDO::FETCH_CLASS, Test::class, [$stmt]);
$stmt->execute();
$stmt->fetch();
?>
Solutions¶
Call
setFetchMode()beforefetch()/fetchAll()starts, never from within a constructor triggered by the fetch itself.Avoid passing the live
PDOStatementas a constructor argument to the fetched class; pass the data it needs instead.If the fetch mode must change mid-iteration, finish consuming the current row first, then call
setFetchMode()before the nextfetch().
See Also¶
Changed Behavior¶
This error may appear following an evolution in behavior, in previous versions. See ` <https://php-changed-behaviors.readthedocs.io/en/latest/behavior/.html>`_.