preg_match($pattern, $subject, $matches)は、文字列が正規表現に一致するかを確認します。一致すれば1を、そうでなければ0を返し、見つけたもので$matchesを埋めます:preg_match('/\d+/', 'Order 4521 shipped', $m)は1を返し、$m[0]を"4521"にします。
パターンは前後を区切り文字(たいてい/.../)で囲んだ文字列で、そのあとにiのような修飾子が続きます。パターンはシングルクォートで書きましょう。ダブルクォートの中では、PHPが先に$nameや、\x41や\101のようなエスケープを展開するので、正規表現エンジンが入力したものとは違うパターンを受け取ることがあります。
キャプチャグループ
パターンの中の括弧は、文字列のうち一致した部分を取り込みます。$m[0]が一致全体、$m[1]が最初のグループ、$m[2]が2つ目で、左から開き括弧の順に数えます。
名前付きグループ
(?<name>...)はグループに名前を付けます。値は名前と番号の両方で保存されるので、誰かがパターンの前のほうにグループを追加しても、結果を読むコードは壊れません。
preg_match_allですべての一致を見つける
preg_match_all()は最初の一致のあとも検索を続け、一致の数を返します。デフォルトでは$m[0]にすべての一致全体が、$m[1]に最初のグループのすべての値が入ります。PREG_SET_ORDERを指定すると、代わりに一致ごとに1つの配列が得られ、ループしやすくなります。
入力を検証する:アンカー、電話番号、郵便番号
検証では、パターンが文字列全体に一致する必要があるので、^で始めて$で終えます。罠が1つあります。$は末尾の改行の直前にも一致するので、'/^\d+$/'は"123\n"を受け付けてしまいます。D修飾子を付けるか、本当の末尾にだけ一致する\zを使いましょう。
メールアドレスでは、正規表現は正しいアドレスを拒否するか、不正なアドレスを受け付けるかのどちらかになります。代わりにfilter_var($email, FILTER_VALIDATE_EMAIL)を使いましょう(filter_var()を参照)。
UTF-8や日本語のテキスト用のu修飾子
u修飾子がないと、パターンはバイトに対して働きます。日本語の文字はUTF-8で3バイトなので、.は文字の3分の1に一致し、長さも間違ってしまいます。uを付けるとパターンは文字に対して働き、Unicodeのプロパティクラスも使えます。
uは対象の文字列の検証も行います。文字列が正しいUTF-8でなければ、preg_match()は一致する代わりにfalseを返します。
preg_quoteでユーザーの入力をエスケープする
.、+、?、(、/のような文字は、パターンの中で特別な意味を持ちます。検索語のようにパターンの一部が変数から来る場合は、2つ目の引数に区切り文字を指定してpreg_quote()に通しましょう。そうしないと1+1が11に一致し、エスケープされていない/がパターンを壊します。
不正なパターンは例外を投げません。PHPは警告を表示し、preg_match()はfalseを返します。
Warning: preg_match(): No ending delimiter '/' found in /path/to/index.php on line 3
そのため、パターンを実行時に組み立てるときは=== false(またはpreg_last_error_msg())で確認します。パターンを使わない単純な部分文字列の確認なら、str_contains()のほうが速くてわかりやすく書けます。一致したテキストを変更するにはpreg_replace()を使います。
よくある質問
PHPのpreg_matchは何を返しますか?
パターンが一致すれば1、一致しなければ0、パターン自体が不正ならfalseを返します。0もfalseもfalseと評価されるので、パターンの失敗と一致なしを区別する必要があるなら=== 1で比較します。
preg_matchで一致したテキストを取得するには?
3つ目の引数を渡します:preg_match('/(\d+)/', 'Order 4521', $m)。すると$m[0]が一致全体、$m[1]が最初のキャプチャグループで、ここではどちらも"4521"です。名前付きグループ(?<id>\d+)なら、値は$m['id']にも入ります。
preg_matchとpreg_match_allの違いは何ですか?
preg_match()は最初の一致で止まります。preg_match_all()はすべての一致を見つけて見つけた数を返し、$matchesをそのすべてで埋めます:preg_match_all('/\d+/', 'a1 b22 c333', $m)は3を返し、$m[0]は["1", "22", "333"]です。
日本語などのUTF-8テキストでpreg_matchを使うには?
u修飾子を付けます:preg_match('/^.{3}$/u', '日本語')は1を返します。uがないとPHPは文字列をバイトとして扱うので、.は3バイトの文字の1バイトに一致します。uを付ければ、\p{Han}、\p{Hiragana}、\p{Katakana}のようなUnicodeのクラスも使えます。
PHPで文字列が数字だけかを確認するには?
preg_match('/^\d+$/D', $s)、または正規表現なしでctype_digit($s)です。D修飾子(または$の代わりに\z)が重要です。素の$は末尾の改行も受け付けるので、"123\n"も通ってしまいます。