PHP7.0から7.1に変更時にmb_eregの結果が変わる?:メモ

悩み
悩み

結局原因がわかっていないのですが、こうすれば動くというところまでがきたと思うのでメモとして。

構築環境
WordPress4.7.5
PHP7.1.4
MySQL5.7.16

発生した問題

今回発生した問題は以下の通り。

  • XserverでWordPress使用
  • PHP7.0.18から7.1.4に切替
  • PHP7.0では問題がなかった分岐が意図通りに動かず
  • mb_ereg()を使用
  • PHPのエラーはでていない
  • WordPress自体の文字コードはUTF-8
  • mbstring.internal_encodingの設定はEUC-JP

具体例としては、以下のようなコードで問題が起きました。


//検索文字列の$strにはマルチバイト文字が含まれる
if ( mb_ereg("a",$str) ) {
 //条件に一致したら実行
} else {
 //条件に一致しなかったら実行
}

PHP7.0までは一致と判定されていたものが判定されなくなりました。

対応策

原因もわかっていないので、あくまで「こうしたらできた」というレベルですが。

調査の結果、mb_ereg()の文字コードをmb_regex_encoding("UTF-8")にすれば意図した動きをすることが分かりました。

そのため、作業的には以下のいずれかになると思います。

  1. サーバーでmbstring.internal_encodingUTF-8に書き換える
  2. mb_ereg()preg_match()に書き換える
  3. mb_regex_encoding("UTF-8")を該当のPHPファイルに記載する

1:mbstring.internal_encodingの変更

1の場合、サーバーの管理パネルなどから操作可能ですので、作業が楽な場合もあります。

2017.5現在、Xserverのmbstring.internal_encodingはデフォルトでUTF-8なので、その点も後押ししてくれます。

ただし、当然ですがmbstring.internal_encodingの設定に依存したプログラムが存在する場合、不具合の原因となります。

影響がないことを確認してから変更されることをお勧めします。

なお、もしもUTF-8が既に設定されている場合は、この記事とはまた別の原因の可能性が高まります。

2:preg_matchへ書き換える

WordPressのようにUTF-8で動いている場合、それなりの目的がなければmb_ereg()は使われないように思います。

そのため、該当箇所の動作にUTF-8以外は不要ならpreg_match()を使うのが一般的なのかもしれません。

しかし、preg_match()もu修飾子なしではUTF-8として扱わないと思うのですが、今回はu修飾子なしでも問題なく動作していたようです。

3:mb_regex_encodingの変更

mb_ereg()を使い続ける場合は、mb_regex_encoding()を記載することになります。

しかし、今回に関して言えば結局UTF-8を指定しているので、前項のpreg_match()を使う方がシンプルに思えます。

なお、調べてみても実際の使い方が分からなかったのですが、どうやら関連するPHPファイルのうちの1つに記載があれば全体に反映されるもようです。

WordPressなら、functions.phpに記載するのが良いかもせれません。

原因

原因はわかりませんでしたが、ある程度の状況は見えてきました。

以下は、渡す文字列とmb_regex_encoding()の文字コードの異同と、マルチバイト文字の有無によって、mb_ereg()が意図した動作になるかどうかを表にしたものです。

文字コード マルチバイト文字 動作するか
同じ あり
同じ なし
違う なし
違う あり ×

つまり、以下のように言えます。

  • mb_ereg()で文字コードが違う場合、マルチバイト文字が含まれていると意図した動作にならない

補足

今回の問題点に対して自分の理解を整理するためにもう少し書いてみます。

マルチバイト文字

WordPress内で使われる文字コード(wp-config.phpで確認できます)は、基本的にはUTF-8です。

しかし、mb_ereg()が用いる文字コードはmbstring.internal_encodingで設定された文字コードを利用するため、今回はEUC-JPが用いられます。

結果として、文字列に日本語などのマルチバイト文字が含まれている場合にmb_ereg()での検索が上手くいかない、のだと思います。

ところが、実際にはPHP7.1に切り替えるまでマルチバイト文字が含まれていても動作していました。

mb_ereg()とマルチバイト文字に巻してPHP7.1の変更点を調べてはみたのですが、私では該当箇所が確認できていません。

mb_eregに関するPHP7.0からの変更点

一応、調べたことのメモとして、PHP7.1でmb_ereg()の動作がどのように変わったのかを以下のサイトから引用します。

mb_ereg() will now set regs to an empty array, if nothing matched. Formerly, regs was not modified in that case.

この変更点は、当記事冒頭の問題には絡んでいない、と思います。

結び

エラーが出ないと問題箇所が特定しにくく時間がかかりましたが、多少調査が進んだところで原因はまだわからずです。

当初は少し触れた第三引数の部分が影響しているのかとPHPのバージョンを頻繁に変えてチェックを繰り返していて、終わってみれば見当違いの方向で時間を費やしていたなと。

判別したい文字コードとmb_regex_encoding()の文字コードが違うこと自体があまり良いことには思えないので(それともこういうものなのでしょうか?)、その点では文字コードを揃える方が良いようには思いました。

補足

調べていてよく分からなくなったのが、u修飾子の使いどころです。

具体的には、以下の場合はu修飾子は必要なのでしょうか?

  1. 検索パターンも、検索文字列も日本語
  2. 検索パターンが英数記号で、検索文字列は日本語
  3. 検索パターンが日本語で、検索文字列は英数記号

上記1はu修飾子が必要だと思います。

問題は2で、この場合にu修飾子が必要なのかどうなのかわかりませんでした。

情報としては以下を見つけましたが、他には見つけられませんでした。

この記事の公開当時はパターン修飾子”u”が必須でした。
しかしPHP5.6では、検索対象の中に日本語が含まれているが正規表現の中で日本語は使わない、という場合ならばパターン修飾子”u”は必要ないようです。

u修飾子自体は、PHPのサイトで以下のように説明されています。

u (PCRE_UTF8)
この修正子は、Perl 非互換な PCRE の機能を有効にします。パターンと対象文字列は、 UTF-8 として処理されます。 無効な対象文字列を preg_* 関数に渡しても、何もマッチしません。 無効なパターンを渡すと、E_WARNING レベルのエラーが発生します。 5オクテットおよび6オクテットの UTF-8 シーケンスは、PHP 5.3.4 以降 (PCRE 7.3 2007-08-28 以降) では無効とみなされます。 以前のバージョンでは、これらも UTF-8 として有効だとみなされていました。

パターンと文字列両方をUTF-8として扱うとありますね。

普通に考えて、両方がUTF-8である必要性は十分にありえるため、左右どちらがなどと考える必要はないのかもしれません。

ただ、仮に検索パターンが英数記号のみで、その文字列がASCIIであれば、文字コードがなんであっても正規表現で扱えるとも考えられます。

その場合、UTF-8で動いている中で検索パターンがASCIIならば、あえてu修飾子をつけなくても動作することにもあまり疑問は感じません。

であればUTF-8環境下では、検索文字列にのみマルチバイト文字列がある場合、検索パターンにu修飾子は必要ない、といいうことなら理解できすそうですが。

…結局PHP7.1では、どの場合にu修飾子が必要なのかがよくわかりません。

preg_machの文字コードは?

こちらはさらに把握しかねているのですが、preg_match()のデフォルトの文字コードはどれで、どこできまるのでしょうか?

mb_regex_encoding()mbstring.internal_encodingで定義された文字コードなのか、プログラム自体の文字コードなのか…。

mb_regex_encoding()の説明を見る限り、これはmb_ereg()のためだけのものではないと読めますので、恐らくそうだと思うのですが、ちゃんとした確認がとれていません。

この辺りは基本的すぎることなのか、日本語サイトでは情報が見つけられず、英語サイトでみてもよくわかりませんでした。

この点ももう少し調べてみたいと思います。

2人がこの記事を評価

役に立ったよという方は上の「記事を評価する」ボタンをクリックしてもらえると嬉しいです。

連投防止のためにCookie使用。SNSへの投稿など他サービスとの連動は一切ありません。

コメント欄