タイトル: エスケープ文字
SEOタイトル: PHP エスケープ文字 (シングル/ダブルクォート) 完全ガイド
| この記事の要点 |
|
ダブルクォート vs シングルクォートの違い
| エスケープ | シングル '...' | ダブル "..." | 意味 |
|---|---|---|---|
\n | そのまま \n | 改行 (LF) | Line Feed (0x0A) |
\r | そのまま \r | キャリッジリターン | CR (0x0D) |
\t | そのまま \t | 水平タブ | 0x09 |
\v | そのまま | 垂直タブ | 0x0B |
\f | そのまま | 改ページ | 0x0C |
\0 | そのまま | ヌル文字 | 0x00 |
\\ | バックスラッシュ | バックスラッシュ | 両方OK |
\' | シングルクォート | そのまま \' | - |
\" | そのまま \" | ダブルクォート | - |
\$ | そのまま \$ | $ 記号(変数展開抑止) | - |
\xNN | そのまま | 16進文字 (例 \x41 → A) | - |
\NNN | そのまま | 8進文字 | - |
\u{NNNN} | そのまま | Unicode (PHP 7+) | 例 \u{1F600} → 😀 |
具体例
// シングルクォート: \n はそのまま文字
echo 'Hello\nWorld';
// → Hello\nWorld (改行されない)
// ダブルクォート: \n は改行
echo "Hello\nWorld";
// → Hello
// World
// 変数展開
$name = 'Taro';
echo 'Hello, $name'; // Hello, $name (展開されない)
echo "Hello, $name"; // Hello, Taro
echo "Hello, \$name"; // Hello, $name (\$ でエスケープ)
// 配列・オブジェクト展開
$arr = ['name' => 'Taro'];
echo "Hello, {$arr['name']}"; // Hello, Taro
echo "Hello, $arr[name]"; // Hello, Taro (簡易記法、キーはクォートなし)
// Unicode (PHP 7+)
echo "\u{1F600}"; // 😀
echo "\u{3042}"; // あ
// 16進文字
echo "\x41\x42\x43"; // ABC
heredoc と nowdoc
複数行の文字列リテラルは heredoc / nowdoc がベター:
$name = 'Taro';
// heredoc - ダブルクォートと同じ動作(変数展開あり)
$html = <<<HTML
<div>
Hello, $name
</div>
HTML;
// nowdoc - シングルクォートと同じ動作(変数展開なし)
$raw = <<<'TEXT'
Path: C:\Users\$name
\nは改行しません
TEXT;
// インデント付き heredoc (PHP 7.3+)
function getHtml(): string {
return <<<HTML
<div>
<p>$name</p>
</div>
HTML;
// 終端タグのインデント分が全行から削除される
}
HTML エスケープ (XSS 対策)
ユーザー入力をそのまま HTML に出すと XSS。必ず htmlspecialchars():
// ❌ XSS 脆弱
echo "<p>Hello, $name</p>";
// $name = "<script>alert(1)</script>" だと実行される
// ✅ htmlspecialchars で対策
echo "<p>Hello, " . htmlspecialchars($name, ENT_QUOTES, 'UTF-8') . "</p>";
// 第2引数のフラグ
// ENT_QUOTES - シングル+ダブル両方をエスケープ(推奨)
// ENT_HTML5 - HTML5 仕様(XHTML は ENT_XHTML)
// ENT_SUBSTITUTE - 不正バイト列を U+FFFD に置換
// ENT_DISALLOWED - 文書型で許可されない文字を置換
// 推奨フォーム
function h($s): string {
return htmlspecialchars($s ?? '', ENT_QUOTES | ENT_HTML5 | ENT_SUBSTITUTE, 'UTF-8');
}
echo "<p>" . h($name) . "</p>";
// htmlentities - htmlspecialchars + 全文字エンティティ化(重い、通常不要)
echo htmlentities('é', ENT_QUOTES, 'UTF-8'); // é
| 文字 | エスケープ後 |
|---|---|
& | &amp; |
< | < |
> | > |
" | &quot; |
' | &#039; (ENT_QUOTES) |
SQL Injection 対策 (PDO Prepared Statement)
絶対に文字列連結で SQL を組まないのが鉄則:
// ❌ SQL Injection 脆弱
$id = $_GET['id']; // "1 OR 1=1"
$pdo->query("SELECT * FROM users WHERE id = $id");
// ❌ addslashes だけでは不十分(マルチバイト攻撃に弱い)
$id = addslashes($_GET['id']);
$pdo->query("SELECT * FROM users WHERE id = '$id'");
// ✅ Prepared Statement で安全
$stmt = $pdo->prepare('SELECT * FROM users WHERE id = ?');
$stmt->execute([$_GET['id']]);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
// ✅ 名前付きバインド
$stmt = $pdo->prepare('SELECT * FROM users WHERE email = :email');
$stmt->execute([':email' => $email]);
// LIKE 句のワイルドカードエスケープ
$keyword = str_replace(['\\', '%', '_'], ['\\\\', '\\%', '\\_'], $keyword);
$stmt = $pdo->prepare("SELECT * FROM logs WHERE msg LIKE ? ESCAPE '\\\\'");
$stmt->execute(["%{$keyword}%"]);
JSON / URL エスケープ
// JSON
$arr = ['name' => 'Taro', 'msg' => 'Hello "World"'];
echo json_encode($arr); // {"name":"Taro","msg":"Hello \"World\""}
echo json_encode($arr, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT);
// URL
$keyword = 'php 入門';
$url = 'https://example.com/search?q=' . urlencode($keyword);
// → ?q=php+%E5%85%A5%E9%96%80
// rawurlencode - スペースを + ではなく %20 に
$path = 'docs/' . rawurlencode($filename);
// パラメータ全部
$params = ['q' => $keyword, 'page' => 2];
$url = 'https://example.com/search?' . http_build_query($params);
非推奨: addslashes / stripslashes / Magic Quotes
古い記事で出てくる addslashes 系はもう使わないでください:
// ❌ 非推奨(古い解説に出てくる)
$safe = addslashes($input); // ' " \ \0 にバックスラッシュ追加
$orig = stripslashes($safe);
// ❌ Magic Quotes は PHP 5.4 で廃止
get_magic_quotes_gpc(); // 常に false (PHP 5.4+)、PHP 8 で関数自体削除
// ✅ 現代の対策
// SQL → PDO Prepared Statement
// HTML → htmlspecialchars
// JSON → json_encode
// シェル → escapeshellarg / escapeshellcmd
PHP コードを文字列に埋めたいとき
// nowdoc が最強(変数展開もエスケープも不要)
$snippet = <<<'PHP'
<?php
$name = $_POST['name'] ?? '';
echo htmlspecialchars($name);
PHP;
// HTML として出すなら全てエスケープ
echo '<pre>' . htmlspecialchars($snippet, ENT_QUOTES, 'UTF-8') . '</pre>';
FAQ
Q: シングルとダブル、どちらを使うべき?
A: 変数展開・エスケープシーケンスが要らないならシングル(わずかに高速)。展開したいときだけダブル。
Q: 全角スペースもエスケープ必要?
A: 不要。HTML エスケープ対象は & / < / > / " / ' のみ。
Q: str_replace で自前エスケープを書くのは?
A: 危険。順序・抜けが起きやすい。必ず標準関数 htmlspecialchars / json_encode / PDO::prepare を使ってください。