preg_matchのよくある落とし穴と戻り値の扱い方
preg_matchは、マッチで1・不一致で0・エラーでfalseを返す正規表現の関数です。戻り値の三値の区別やデリミタ・u修飾子の扱いにハマりどころがある関数ですね。
戻り値の0とfalseは意味が違う
preg_match でいちばんやりがちなのが、戻り値を単純に真偽で判定してしまう件ですね。この関数はマッチしたら 1、しなかったら 0、そしてエラーのときは false を返します。0 も false も緩い比較では偽になるので、区別したいときは型まで見る必要があります。
$result = preg_match('/\d+/', $input);
if ($result === false) {
// パターンエラー。ここを握りつぶすとバグの原因に
throw new RuntimeException('正規表現エラー');
}
if ($result === 1) {
// マッチした
}
ふだんは if (preg_match(…)) で十分ですが、パターンを動的に組み立てているときはエラーの false をちゃんと拾っておかないと、静かに壊れて原因究明に時間を取られます。
デリミタの選び方でバックスラッシュ地獄を避ける
正規表現はデリミタで囲む必要があって、慣例的に / を使いますが、URL やパスのようにスラッシュを含むパターンだと \/ だらけになって読めなくなります。こういうときはデリミタを # や ~ に変えると一気に読みやすくなりますね。
// / だとエスケープが多い
preg_match('/^https:\/\/example\.com\//', $url);
// # に変えるとすっきり
preg_match('#^https://example\.com/#', $url);
日本語を扱うならu修飾子を忘れずに
マルチバイト文字を扱うときは末尾に u 修飾子を付けて UTF-8 として解釈させます。これを忘れると . が1バイト単位で動いてしまい、全角文字の途中でマッチして意図しない結果になります。u を付けないと、不正なバイト列で preg_match が false を返すこともあるので注意ですね。
// u なし:バイト単位で数えてしまう
preg_match('/^.{3}$/', 'あいう'); // 0(9バイト扱い)
// u あり:文字単位
preg_match('/^.{3}$/u', 'あいう'); // 1
まとめ
preg_match は戻り値が三値であることと、デリミタや修飾子の存在を押さえておくと、大半のハマりは避けられると思います。特にエラーの false を握りつぶさないこと、日本語には u を付けること、この二つだけでも意識しておくと後々の自分が助かりますね。
よくある質問
Q. preg_matchの戻り値はどう判定すればいいですか。
A. マッチで1、不一致で0、エラーでfalseの三値です。動的にパターンを組むときは === false でエラーを拾い、握りつぶさないようにします。
Q. 日本語を扱うのに気をつけることは。
A. 末尾にu修飾子を付けてUTF-8として解釈させます。付けないと . が1バイト単位で動き、全角文字の途中でマッチしてしまいますね。