2020-02-03に更新

phpMyAdmin5.0.1の「状態」タブがFatal Errorで表示されないバグ

どこにもエラー報告が見当たらなかったので。デフォルトでも発生する、大きいバグと思われる。問題詳細から解決方法まで記録用に。

事前に行なっていたこと

環境:Windows 10

64bit Windows版 XAMPP7.4.1(PHP 7.4.1, Apache 2.4.41, MariaDB 10.4.11, PHP 7.4.1, phpMyAdmin 5.0.1, OpenSSL 1.1.1, XAMPP Control Panel 3.2.4, Webalizer 2.23-04, Mercury Mail Transport System 4.63, FileZilla FTP Server 0.9.41, Tomcat 7.0.99 (with mod_proxy_ajp as connector), Strawberry Perl 5.16.3.1 Portable)をインストールした。

起動前に若干設定ファイルを変更した。タイムアウトの延長や、前バージョンの設定の引継ぎなど。今回の問題に関わるような変更はしていない。

問題

「外観の設定」で「言語 - Language」を「日本語 - Japanese」にし、phpMyAdminの「状態」(http://localhost/phpmyadmin/server_status.php)を開いたときに、

image.png

Fatal error: Uncaught TypeError: mb_detect_encoding() expects parameter 1 to be string, bool given in C:\xampp\phpMyAdmin\libraries\classes\Util.php:1620 Stack trace: #0 C:\xampp\phpMyAdmin\libraries\classes\Util.php(1620): mb_detect_encoding(false, 'UTF-8', true) #1 C:\xampp\phpMyAdmin\libraries\classes\Controllers\Server\Status\StatusController.php(51): PhpMyAdmin\Util::localisedDate(1580669987) #2 C:\xampp\phpMyAdmin\server_status.php(35): PhpMyAdmin\Controllers\Server\Status\StatusController->index(Object(PhpMyAdmin\ReplicationGui)) #3 {main} thrown in C:\xampp\phpMyAdmin\libraries\classes\Util.php on line 1620

と表示され、閲覧できない。
当該ソース(C:\xampp\phpMyAdmin\libraries\classes\Util.php)はこちら。

image.png

/* Fill in AM/PM */
$hours = (int) date('H', (int) $timestamp);
if ($hours >= 12) {
    $am_pm = _pgettext('AM/PM indication in time', 'PM');
} else {
    $am_pm = _pgettext('AM/PM indication in time', 'AM');
}
$date = preg_replace('@%[pP]@', $am_pm, $date);

$ret = strftime($date, (int) $timestamp);
// Some OSes such as Win8.1 Traditional Chinese version did not produce UTF-8
// output here. See https://github.com/phpmyadmin/phpmyadmin/issues/10598
if (mb_detect_encoding($ret, 'UTF-8', true) != 'UTF-8') {
    $ret = date('Y-m-d H:i:s', (int) $timestamp);
}

これはphpMyAdminを構成するコアな部分で、もちろん一切コードは変更していない。

このバグは言語が日本語の時だけ発生する。他の言語では発生しないため、発見が遅れている可能性が高い。

原因

該当ソースのユーザー定義関数localisedDate()の中で発生している。直接的には、1617行目のstrftime()がfalseを返していることが原因。php.netによれば

エラー / 例外
出力内容は元となった C ライブラリに依存するため、サポートしていない変換指定子もあります。 Windows では、対応していない変換指定子を渡すと 5 つの E_WARNING メッセージが出て FALSE を返します。 その他のオペレーティングシステムでは特に E_WARNING メッセージは出ず、 変換指定子が (変換されずに) そのまま出力されます。

とあり、Windows依存のバグ。

先ほどのコードでは、strftime()の第一引数に、$dateの中身として%Y 年 2 月 %d 日 %H:%Mが渡されている。これ自体は仕様通りで問題が無いように見える。

しかし、strftimeを処理する際に、依存するWindowsのCライブラリでは、文字列を一度Shift-JISに変換している。つまり、UTF-8→SJIS→UTF-8の変換が行われている。ここで、漢字の「月」の字が文字化けを起こし、不正なマルチバイト文字と認識され、strftime()がfalseを吐いている、というのが真相である。

実際に、UTF-8→Shift-JIS→UTF-8変換を行うと、「%Y 年 2 � %d 日 %H:%M」となる。
また、$dateフォーマット文字列において漢字の「月」を削除すればこのバグは発生しなくなる。

結論

要するに、このバグはOS依存の関数を用いるコードのほうに問題がある。strftime()ではなくdate()に書き換えるべきである。

とりあえず日本語ユーザーができる仮対応としては、C:\xampp\phpMyAdmin\libraries\classes\Util.phpの1617行目を「$ret = strftime('%Y-%B-%d %H:%M', (int) $timestamp);」に書き換えることで閲覧できるようになる。

Fatal Errorが出るのは相当だと思うので、もし他の方が同じバグに遭遇したら是非プロジェクトに熱いissueを送ってほしい。私はやり方がよくわからないので原因究明で終わりにします。

【追記】
だらさんに報告して頂きました、ありがとうございます。
https://github.com/phpmyadmin/phpmyadmin/issues/15830

ツイッターでシェア
みんなに共有、忘れないようにメモ

ウラル

Splatoonの二次創作サイト「スプランプ」の管理人です。サーモンラン研究所やオクトチャット、フェス速報などを作りました。

Crieitは誰でも投稿できるサービスです。 是非記事の投稿をお願いします。どんな軽い内容でも投稿できます。

また、「こんな記事が読みたいけど見つからない!」という方は是非記事投稿リクエストボードへ!

有料記事を販売できるようになりました!

こじんまりと作業ログやメモ、進捗を書き残しておきたい方はボード機能をご利用ください。
ボードとは?

コメント