PDOで「Packets out of order. Expected 1 received 0」のワーニングが出る


|[../](https://qiita.com/taconana/items/d382d08a83a2a67f3b6a) |
|---|

CentOS Stream 8でPHP7.4からPDOでMySQL8.0に接続する際に「Packets out of order. Expected 1 received 0」のワーニングが出る時がある。毎回確実に出る訳でもなく、どのような状況の時に出るのかは判明できていない。心当たりのある原因を後述しておく。

```
PHP message: @@ enter getUsers('admin',false)
PHP message: @@ new UserManager()
PHP message: @@ new UserDataAccessor()
PHP message: @@ PersistenceBase::__construct()
PHP message: @@ PersistenceBase::pdo = null
PHP message: 
PHP Warning:  Packets out of order. Expected 1 received 0. Packet size=145 
              in common/integration/PersistenceBase.php on line 46
              
PHP message: @@@ connected mysql:dbname=test;host=localhost;port=3306;charset=utf8
PHP message: @@@ select * from user order by user_id --> 6
```
数日前は何度か起きていたが、最近はまったく起きていない。

上記の場合、PDOに接続でき、ユーザー情報の検索はできている。PDOに接続に行く際に「Packets out of order. Expected 1 received 0」のWarningが出たと言っている。「パケットの順序がおかしい。1でないといけないのに0になっている」と言っている。Warningだから無視していいのか、解決すべきなのかも分からず。

```php
  private function pdo(): PDO {
  
    error_log('@@ PersistenceBase::pdo = ' . (is_null($this->pdo)? 'null' : 'available'));
    
    if (empty($this->pdo)){
    
      $dsn = $this->props['db.dsn'];
      $user = $this->props['db.user'];
      $password = $this->props['db.password'];
      $options = [
          PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
          PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
          // PDO::ATTR_EMULATE_PREPARES, false,
          PDO::ATTR_PERSISTENT => true
      ];
      try {
        
        $this->pdo = new PDO($dsn, $user, $password, $options);
        error_log("@@@ connected " . $dsn);
        
      } catch (PDOException $e) {
        
        error_log("@@@ dsn = " . $dsn);
        error_log("@@@ user = " . $user);
        error_log("@@@ password = " . $password);
        error_log("@@@ connect failed: " . $e->getMessage());
        error_log($e->getTraceAsString());
      }
    }
    return $this->pdo;
  }
```
ネット情報を見てもよく分からず。再現するようになったら、そのとき再度、調べたい。関連しそうなログは、以下のもの。
```
$ tail -200 /var/log/php_errors.log
$ tail -200 /var/log/httpd/error_log
$ tail -200 /var/log/mysql/mysqld.log
$ tail -200 /var/log/nginx/error.log
$ tail -200 /var/log/php-fpm/error.log
```
## 原因は、MySQLの wait_timeout が8時間の問題だったかも

確認はしていないのだけど、原因は、MySQLの wait_timeout の設定だったかもしれない。MySQLは、デフォルトでは8時間アクセスがないとタイムアウトする。例えば、前日にはうまく行っていても、翌朝にはMySQLを再起動しないといけないときが、Javaでもあった。今回の環境でも、wait_timeoutを設定しないまま使っていたためかもしれない。

以下のように、wait_timeout に31536000をセットしたから起きなくなったのかな。31536000 = 365 * 24 * 3600 である。
```
$ vi /etc/my.cnf.d/mysql-server.cnf

[mysqld]
character-set-server=utf8
lower_case_table_names=1

interactive_timeout=31536000
wait_timeout=31536000
```
## PHPのシステムロガー error_log() の使い方

ついでなので、PHPのシステムロガーの使い方を整理しておく。呼び出しには、汎用のerror_log()を使う。
```
error_log(string $message, int $message_type = 0, ?string $destination = null): bool
```
message_type=0のとき、message がPHPのシステムロガーに送られ、error_log の値に応じて、OSのシステムログ機構を使って保存されるか、ファイルに保存されるかが決まる。デフォルトのオプション。
message_type=3のとき、message は destination で指定されたファイルに追加される。messageの末尾に改行文字を付与した方がよい。

PHPのシステムロガーの設定は、php.ini で行う。
```
$ vi /etc/php.ini

; 出力するエラーのレベル 
;error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT
error_reporting = E_ALL

; エラーを表示するか。どこに表示するか。{ On,Off,stderr,stdout }
display_errors = On

; 起動時のエラーを表示するか。
display_startup_errors = On

; ログを出力するか。stderrやerror_logで指定される場所に出力される。
log_errors = On

; ログの出力先ファイル
error_log = /var/log/php_errors.log

; ログの出力先をsyslogに委ねる場合
;error_log = syslog
;syslog.ident = php
;syslog.facility = user

$ systemctl restart httpd
$ systemctl restart nginx
```
以前は、error_reporting = E_ALL | E_STRICT と書いていたらしい。PHP7.4では error_reporting = E_ALL でいい。E_STRICTなど細かいものを除外するときは、error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT と書くそうだ。


|[../](https://qiita.com/taconana/items/d382d08a83a2a67f3b6a) |
|---|