PHP + MariaDB で出る Malformed communication packet エラーをプログラムの変更で回避する方法

はじめに
11月4日 にリリースされた MariaDB 10.2.35 と 10.3.26 で不具合が出ていて、
PHP 7.2 以下だと下記エラーが出るようです。
- [MDEV-24121] Recent MariaDB update appears to have introduced a DB connection issue for PHP < 7.3 (or anything using PDO) - Jira
- https://jira.mariadb.org/browse/MDEV-24121
しかしミドルウェアのメジャーバージョンアップをすぐに行うのは難しい場合も多いですよね。
そこで今日は、上記問題を PHP プログラム側で対応する方法をご紹介します。
動作検証には Docker を使って、公式の PHP と MariaDB のイメージで構築した環境を使いました。
- PHP
- 7.2.34
- MariaDB
- 10.2.35
1. すぐ動かしたい場合【要注意】
PDO::ATTR_EMULATE_PREPARES を true にするか、コメントアウトすると動きました。
ただし、これには SQL インジェクションのリスクがあるようです。
- PDOでATTR_EMULATE_PREPARESを適切に設定してないとSQLインジェクションの原因になるかも(MySQL編) - Qiita
- https://qiita.com/stk2k/items/c46cc921a4f7b6e4bab2
僕はまだ詳細を把握していないのですが、よく検討したうえで実行していただければと思います。
$conn = new PDO(...);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// ↓ このように true にするかコメントアウト
$conn->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
2. リスクを減らしつつ対応
色々と試したところ、どうやら下記2点を同時に満たす場合にエラーが出るようです。
- PDO::ATTR_EMULATE_PREPARES が false
- 値をバインドしていない
(query だけでなく prepare + execute でもバインドしてない場合はエラーになるようです)
なので、値をバインドしていない箇所に対してだけ、
下記のように一時的に PDO::ATTR_EMULATE_PREPARES を true にすれば、リスクを軽減しつつ対応できるかと思います。
// true にして
$conn->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
// 実行して
$res = $conn->query('SELECT id, name FROM items');
// false に戻す
$conn->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
また、下記のようにダミーの値をバインドしても
PDO::ATTR_EMULATE_PREPARES が false のままで動きました。
$conn->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$stmt = $conn->prepare('SELECT id, name FROM items WHERE :dummy');
$stmt->bindValue(':dummy', 1);
$stmt->execute();
$rows = $stmt->fetchAll();
...
3. おわりに
PHP 7.2 のサポート期限が 2020年11月30日までなので、これを機に PHP のバージョンアップを行うのがいいのかなと思います。
- PHP: Supported Versions
- https://www.php.net/supported-versions.php
実際に PHP 7.3 と 7.4 の環境で、PHP 7.2 でエラーとなったコードを動かしたところ、
確かにエラーは出ませんでした。
とは言っても、PHP のメジャーバージョンアップは動作確認やサーバ管理の都合など、様々なことが絡んでくるので悩ましい問題ですよね...
この記事が何かのお役に立てば幸いです。