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

はじめに

11月4日 にリリースされた MariaDB 10.2.3510.3.26 で不具合が出ていて、
PHP 7.2 以下だと下記エラーが出るようです。

SQLSTATE[HY000]: General error: 1835 Malformed communication packet

しかしミドルウェアのメジャーバージョンアップをすぐに行うのは難しい場合も多いですよね。

そこで今日は、上記問題を PHP プログラム側で対応する方法をご紹介します。

動作検証には Docker を使って、公式の PHP と MariaDB のイメージで構築した環境を使いました。

PHP
7.2.34
MariaDB
10.2.35
目次
  1. すぐ動かしたい場合【要注意】
  2. リスクを減らしつつ対応
  3. おわりに

1. すぐ動かしたい場合【要注意】

PDO::ATTR_EMULATE_PREPAREStrue にするか、コメントアウトすると動きました。

ただし、これには SQL インジェクションのリスクがあるようです。

僕はまだ詳細を把握していないのですが、よく検討したうえで実行していただければと思います。

$conn = new PDO(...);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// ↓ このように true にするかコメントアウト
$conn->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);

2. リスクを減らしつつ対応

色々と試したところ、どうやら下記2点を同時に満たす場合にエラーが出るようです。

  1. PDO::ATTR_EMULATE_PREPARES が false
  2. 値をバインドしていない
    (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 7.3 と 7.4 の環境で、PHP 7.2 でエラーとなったコードを動かしたところ、
確かにエラーは出ませんでした。

とは言っても、PHP のメジャーバージョンアップは動作確認やサーバ管理の都合など、様々なことが絡んでくるので悩ましい問題ですよね...

この記事が何かのお役に立てば幸いです。