はじめに
PHPで正規表現を使う際、ユーザー入力や動的な文字列を正規表現パターンに含めようとして、予期しないエラーに遭遇したことはありませんか?そんなときに役立つのがpreg_quote関数です。
この記事では、正規表現初心者の方でも理解できるよう、preg_quoteの基本から実践的な使い方まで詳しく解説します。
preg_quoteとは?
preg_quoteは、文字列内の正規表現特殊文字を自動的にエスケープしてくれる関数です。これにより、動的な文字列を安全に正規表現パターンとして使用できるようになります。
なぜ必要なのか?
正規表現には特別な意味を持つ文字(メタ文字)があります:
. \ + * ? [ ^ ] $ ( ) { } = ! < > | : - #
これらの文字をそのまま正規表現で使うと、意図しない動作やエラーが発生します。
基本構文
preg_quote(
string $str, // エスケープする文字列
?string $delimiter = null // デリミタ文字(オプション)
): string
戻り値: エスケープされた文字列を返します。
基本的な使用例
例1: 特殊文字のエスケープ
<?php
// エスケープなし(危険!)
$search = "price: $100.50";
$pattern = "/$search/"; // エラーになる可能性
// preg_quoteを使用(安全!)
$search = "price: $100.50";
$pattern = "/" . preg_quote($search) . "/";
echo $pattern . "\n";
// 出力: /price: \$100\.50/
$text = "The price: $100.50 is reasonable.";
if (preg_match($pattern, $text)) {
echo "マッチしました!\n";
}
?>
例2: デリミタの指定
デリミタ自体もエスケープが必要な場合があります。第2引数でデリミタを指定すると、それもエスケープされます。
<?php
$search = "https://example.com/page";
$delimiter = '/';
// デリミタを指定しない場合
$escaped1 = preg_quote($search);
echo $escaped1 . "\n";
// 出力: https://example\.com/page (スラッシュはエスケープされない)
// デリミタを指定した場合
$escaped2 = preg_quote($search, '/');
echo $escaped2 . "\n";
// 出力: https:\/\/example\.com\/page (スラッシュもエスケープされる)
$pattern = "/" . $escaped2 . "/";
$text = "Visit https://example.com/page for more info.";
if (preg_match($pattern, $text)) {
echo "URLが見つかりました!\n";
}
?>
どの文字がエスケープされるのか?
preg_quoteは以下の正規表現メタ文字をエスケープします:
<?php
$special = ". \\ + * ? [ ^ ] $ ( ) { } = ! < > | : - #";
$escaped = preg_quote($special);
echo "元の文字列: $special\n";
echo "エスケープ後: $escaped\n";
/*
出力:
元の文字列: . \ + * ? [ ^ ] $ ( ) { } = ! < > | : - #
エスケープ後: \. \\ \+ \* \? \[ \^ \] \$ \( \) \{ \} \= \! \< \> \| \: \- \#
*/
?>
実践的な使用例
例1: ユーザー入力での検索機能
ユーザーが入力した文字列で検索する際、特殊文字が含まれていても安全に処理できます。
<?php
function searchInText($userInput, $text) {
// ユーザー入力を安全にエスケープ
$escaped = preg_quote($userInput, '/');
$pattern = "/$escaped/i"; // iフラグで大文字小文字を区別しない
if (preg_match($pattern, $text, $matches)) {
return "「{$matches[0]}」が見つかりました!";
} else {
return "見つかりませんでした。";
}
}
// 特殊文字を含む検索
echo searchInText("C++", "I love C++ programming.") . "\n";
// 出力: 「C++」が見つかりました!
echo searchInText("$100", "Price is $100") . "\n";
// 出力: 「$100」が見つかりました!
echo searchInText("[重要]", "メール件名: [重要]会議のお知らせ") . "\n";
// 出力: 「[重要]」が見つかりました!
?>
例2: 動的な正規表現パターンの構築
複数のキーワードを含む動的な正規表現を作成する場合:
<?php
$keywords = ["C++", "$price", "[注意]", "file.txt"];
// 各キーワードをエスケープ
$escapedKeywords = array_map(function($keyword) {
return preg_quote($keyword, '/');
}, $keywords);
// OR条件の正規表現を作成
$pattern = '/' . implode('|', $escapedKeywords) . '/';
echo "パターン: $pattern\n";
// 出力: /C\+\+|\$price|\[注意\]|file\.txt/
$text = "Check the file.txt for $price information.";
preg_match_all($pattern, $text, $matches);
echo "見つかったキーワード:\n";
print_r($matches[0]);
/*
出力:
見つかったキーワード:
Array
(
[0] => file.txt
[1] => $price
)
*/
?>
例3: ファイル名やパスの検索
ファイルパスには多くの特殊文字が含まれるため、preg_quoteが必須です。
<?php
function findFilePath($searchPath, $logText) {
$escaped = preg_quote($searchPath, '/');
$pattern = "/" . $escaped . "/";
if (preg_match($pattern, $logText)) {
return "パス「{$searchPath}」がログに含まれています。";
} else {
return "パスが見つかりませんでした。";
}
}
$log = "Error in file: C:\\Users\\Admin\\Documents\\report.txt at line 42";
echo findFilePath("C:\\Users\\Admin\\Documents\\report.txt", $log) . "\n";
// 出力: パス「C:\Users\Admin\Documents\report.txt」がログに含まれています。
?>
例4: HTMLタグの検索と置換
<?php
function replaceTag($tag, $replacement, $html) {
// タグをエスケープ
$escaped = preg_quote($tag, '/');
$pattern = "/$escaped/";
return preg_replace($pattern, $replacement, $html);
}
$html = "<div>Content</div> <span>More content</span>";
$result = replaceTag("<div>", "<section>", $html);
echo $result . "\n";
// 出力: <section>Content</div> <span>More content</span>
?>
preg_quoteを使わない場合の危険性
例: エラーが発生するケース
<?php
// ❌ 危険: preg_quoteを使わない
$userInput = "How much? $100+"; // 特殊文字が含まれる
$pattern = "/$userInput/";
// これは正規表現としてエラーまたは予期しない動作をする
// $100は変数として解釈され、+は「1回以上の繰り返し」として解釈される
// ✅ 安全: preg_quoteを使う
$escaped = preg_quote($userInput, '/');
$pattern = "/$escaped/";
$text = "Question: How much? $100+ for premium";
if (preg_match($pattern, $text)) {
echo "正しくマッチしました!\n";
}
?>
セキュリティの観点
ユーザー入力を直接正規表現に使用すると、ReDoS(Regular Expression Denial of Service)攻撃のリスクがあります。preg_quoteを使うことで、この種の攻撃を防げます。
<?php
// 悪意のある入力例
$maliciousInput = "(a+)+b"; // バックトラックを引き起こす可能性
// preg_quoteを使えば安全
$safe = preg_quote($maliciousInput, '/');
// 出力: \(a\+\)\+b (メタ文字がエスケープされ、単なる文字列として扱われる)
?>
よくある使用パターン
パターン1: 前方一致・後方一致の検索
<?php
$keyword = "test.php";
$escaped = preg_quote($keyword, '/');
// 前方一致(行頭)
$pattern = "/^$escaped/";
// 後方一致(行末)
$pattern = "/$escaped$/";
// 単語境界を使用
$pattern = "/\b$escaped\b/";
?>
パターン2: 大文字小文字を区別しない検索
<?php
$search = "C++";
$escaped = preg_quote($search, '/');
$pattern = "/$escaped/i"; // iフラグで大文字小文字を無視
$text = "I love c++ and C++ programming!";
preg_match_all($pattern, $text, $matches);
print_r($matches[0]);
/*
Array
(
[0] => c++
[1] => C++
)
*/
?>
パターン3: 複数の文字列を一度に検索
<?php
$terms = ["PHP", "C++", "$variable", "file.txt"];
$escaped = array_map(fn($t) => preg_quote($t, '/'), $terms);
$pattern = '/\b(' . implode('|', $escaped) . ')\b/i';
$text = "Learn PHP and C++ to handle file.txt with $variable";
preg_match_all($pattern, $text, $matches);
echo "見つかった用語: " . implode(", ", $matches[0]) . "\n";
// 出力: 見つかった用語: PHP, C++, file.txt, $variable
?>
注意点とベストプラクティス
1. デリミタは常に指定する
パターンで使うデリミタを第2引数に指定することを習慣化しましょう。
// ✅ 推奨
$escaped = preg_quote($str, '/');
// △ デリミタがない場合は省略可能だが、統一性のため指定が望ましい
$escaped = preg_quote($str);
2. エスケープ後にさらに正規表現を追加できる
<?php
$filename = "report.txt";
$escaped = preg_quote($filename, '/');
// エスケープした文字列の前後に正規表現を追加
$pattern = "/\b$escaped\b/"; // 単語境界を追加
$pattern = "/$escaped\s+\d+/"; // スペースと数字を追加
?>
3. UTF-8文字列の扱い
日本語などのマルチバイト文字は特殊文字ではないため、エスケープされません。
<?php
$japanese = "こんにちは。値段は¥1000です。";
$escaped = preg_quote($japanese, '/');
echo $escaped . "\n";
// 出力: こんにちは。値段は¥1000です。
// (日本語はそのまま、句点もエスケープ不要)
?>
4. エスケープ不要な場面
正規表現を使わない文字列関数が使える場合は、そちらを優先しましょう。
// 単純な文字列検索なら正規表現は不要
if (str_contains($text, $search)) { // PHP 8.0+
// ...
}
// 文字列置換も同様
$result = str_replace($search, $replace, $text);
まとめ
preg_quoteは、動的な文字列を正規表現で安全に扱うための必須関数です。
重要ポイント:
- ユーザー入力を正規表現に使う際は必ず
preg_quoteを使う - デリミタを第2引数に指定する習慣をつける
- セキュリティ対策(ReDoS攻撃の防止)にも有効
- 全ての正規表現メタ文字を自動でエスケープ
- 単純な文字列検索なら
str_containsなどの方が効率的
正規表現は強力ですが、適切にエスケープしないと危険です。preg_quoteを使って安全なコードを書きましょう!
参考リンク
セキュリティを意識したPHPコーディングで、安全なWebアプリケーションを構築しましょう!
