はじめに
あるプロダクトの PHP のバージョンを 5.6 から 7.2 にバージョンアップする作業の中で、いくつかテストが落ちるようになっていたので、原因と修正作業を進めていた。
落ちたテストの中には、mt_rand の問題だったり *1 、浮動小数点演算の問題だったりがあったのだが、一つどうしてもわからずめちゃめちゃハマったテストがあった。
[:print:]
Failed asserting that two strings are identical.
— うたかた/ヨシキ (@yoshiki_utakata) 2019年2月13日
--- Expected
+++ Actual
@@ @@
-'にゃーん
-'
+'にゃーん'
PHP 5.6 までは普通に通っていたテストなのだが、7.2で改行が消えるようになっていてテストが落ちていた。頑張って原因を探った結果、 [:print:] の変更によるものだとわかった。
[:print:] とは
[:print:]
とは、正規表現で「印字可能なキャラクタ」を表す表現である。
http://www.kt.rim.or.jp/~kbk/regex/regex.html#CLASSNAME
上記ページによると、
とある。
[: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以前の挙動に戻る。
参考
- http://www.kt.rim.or.jp/~kbk/regex/regex.html#CLASSNAME
- 正規表現で「制御文字以外」のチェック - ockeghem's blog
- PHPのmt_rand()にプルリクを送った - kusano_k’s blog

プログラマのための文字コード技術入門 (WEB+DB PRESS plus) (WEB+DB PRESS plusシリーズ)
- 作者: 矢野啓介
- 出版社/メーカー: 技術評論社
- 発売日: 2010/02/18
- メディア: 単行本(ソフトカバー)
- 購入: 34人 クリック: 578回
- この商品を含むブログ (129件) を見る