Webエンジニアの日常とリーグオブレジェンド

Webエンジニアとして働いている猫のブログ。EmacsとMySQLとリーグオブレジェンド(LoL)が好物。主に技術的な記事かLoLの記事を書く。

PHP7.2での正規表現における [:print:] の変更について

f:id:yoshiki_utakata:20181004212834j:plain

はじめに

あるプロダクトの PHP のバージョンを 5.6 から 7.2 にバージョンアップする作業の中で、いくつかテストが落ちるようになっていたので、原因と修正作業を進めていた。

落ちたテストの中には、mt_rand の問題だったり *1浮動小数点演算の問題だったりがあったのだが、一つどうしてもわからずめちゃめちゃハマったテストがあった。

[:print:]

PHP 5.6 までは普通に通っていたテストなのだが、7.2で改行が消えるようになっていてテストが落ちていた。頑張って原因を探った結果、 [:print:] の変更によるものだとわかった。

[:print:] とは、正規表現で「印字可能なキャラクタ」を表す表現である。

http://www.kt.rim.or.jp/~kbk/regex/regex.html#CLASSNAME

上記ページによると、

[:print:] 印字可能なキャラクタ(=制御文字以外のキャラクタ)

とある。

[:cntrl:] というのはよく目にするだろう。

[:cntrl:] 制御文字

PHP 7.2 における [:print:] の挙動変更

この [:print:] の挙動が PHP 7.2 から変更されているっぽかった。

PHP 7.1

$ php -v
PHP 7.1.16 (cli) (built: Mar 31 2018 02:59:59) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.1.0, Copyright (c) 1998-2018 Zend Technologies

$ php -r 'var_dump(mb_ereg_replace("[^[:print:]]", "", "にゃーん\r\n"));'
string(14) "にゃーん
"

PHP 7.2

$ php -v
PHP 7.2.4 (cli) (built: Sep 18 2018 13:44:20) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologies
    with Zend OPcache v7.2.4, Copyright (c) 1999-2018, by Zend Technologies

$ php -r 'var_dump(mb_ereg_replace("[^[:print:]]", "", "にゃーん\r\n"));'
string(12) "にゃーん"

これが実際のところPHP 7.2の挙動変更なのか、その他の環境による差なのかは非常に微妙なところだが、PHP 7.2 から [:print:] に改行文字が含まれなくなったようだ。

[^[:print:]] は「[:print:]以外の文字」を表すわけだが、[:print:] から改行コードが消えた結果、 [^[:print:]] には改行コードが含まれるようになり、改行コードが除去された結果テストが落ちるようになっていた。

おそらく本来 [:print:][:cntrl:] 以外の文字を表すものだろうから、改行が含まれなくなるのは正しい(今回のPHPの仕様変更は正しい)。が、この微妙な使用を活用していた我々としては「改行コード以外の制御文字」をどうやって除去するべきか悩みに悩んだ。

対処方法

こうする

mb_ereg_replace("[^[:print:]\r\n]", "", "にゃーん\r\n")

こうすると [:print:] と改行以外になるので、PHP7.1以前の挙動に戻る。

参考

プログラマのための文字コード技術入門 (WEB+DB PRESS plus) (WEB+DB PRESS plusシリーズ)

プログラマのための文字コード技術入門 (WEB+DB PRESS plus) (WEB+DB PRESS plusシリーズ)