PHPのPDOStatementはfetch
しているループの最中に、MySQLサーバがシャットダウンしたりして結果セットがすべて取れなくなっても、例外を投げたり、エラーを出したりはせずに静かに処理を中断してしまうようだ。
検証コード:
<?php
$pdo = new PDO(
'mysql:dbname=testing;host=localhost',
'root',
'root',
[
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => false,
]
);
// ダミーデータを1024件(64MBほど)作る
$pdo->query('CREATE TABLE IF NOT EXISTS data (data text)');
//$pdo->query('TRUNCATE TABLE data');
$stmt = $pdo->query('SELECT COUNT(*) AS total FROM data');
if ($stmt->fetch()['total'] == 0) {
$stmt = $pdo->prepare('INSERT INTO data VALUES (?)');
for ($i = 0; $i < 1024; $i++) {
$stmt->execute([str_repeat('a', (1024 * 64) - 1)]); // 64KB - 1
}
}
unset($stmt);
// メモリが少なくなってる状況を作り、すべての結果セットがメモリに乗らないようにする
ini_set('memory_limit', '2M');
$statement = $pdo->query('SELECT * FROM data');
$i = 1;
while ($row = $statement->fetch()) {
echo $i++, PHP_EOL;
usleep(100000); // このループ中にMySQLサーバを落としてみる
}
var_dump($row);
var_dump($statement->errorInfo());
PDOStatement::fetch()
している最中に、MySQLサーバを落としてみる:
$ brew services start mysql
==> Successfully started `mysql` (label: homebrew.mxcl.mysql)
すると読み込めた行まででループが終了する。下の結果例では77行目まで読み込めたが、残りの947行は当然読み取られない。
出力結果:
71
72
73
74
75
76
77
/private/var/folders/mf/cm46yj755lv9v_v4458953lc0000gn/T/CodeRunner/Untitled 6.php:35:
bool(false)
/private/var/folders/mf/cm46yj755lv9v_v4458953lc0000gn/T/CodeRunner/Untitled 6.php:36:
array(3) {
[0] =>
string(5) "00000"
[1] =>
NULL
[2] =>
NULL
}
PDOのエラーコード「00000」は成功を意味するので、本当に静かにループを終了して終わってしまう。
こういう静かなエラーに対応する方法はあるのだろうか?