タイトル: 改行出力
SEOタイトル: PHP 改行出力 (\n / PHP_EOL / nl2br) 完全ガイド
| この記事の要点 |
|
PHP の改行コード基礎
PHP で文字列に改行を含める方法は複数あります。エスケープシーケンス \n / \r\n / \r、定数 PHP_EOL、Heredoc/Nowdoc などです。
<?php
// ✅ ダブルクォートではエスケープシーケンスが展開される
echo "abc\n"; // abc + LF
echo "abc\r\n"; // abc + CRLF
echo 'abc' . "\n"; // 結合
// ❌ シングルクォートでは展開されない(リテラルの \n が出力される)
echo 'abc\n'; // abc\n (改行されない!)
// ✅ 環境依存定数(Unix では \n、Windows では \r\n)
echo "abc" . PHP_EOL;
// ✅ Heredoc は改行をそのまま含められる
echo <<<EOT
line1
line2
EOT;
改行コード一覧
| 表記 | 名称 | 16進 | 主な OS |
|---|---|---|---|
\n | LF (Line Feed) | 0x0A | Unix / Linux / macOS (X 以降) |
\r\n | CRLF | 0x0D 0x0A | Windows / HTTP / SMTP プロトコル |
\r | CR (Carriage Return) | 0x0D | Mac OS 9 以前(現代では稀) |
PHP_EOL | 環境依存 | 環境次第 | OS に応じて自動切替 |
HTML 出力で改行を反映する: nl2br()
ブラウザは \n を空白として扱うため、フォーム入力の改行をそのまま echo しても見えません。nl2br() で <br /> を挿入します:
<?php
$comment = "1 行目\n2 行目\n3 行目";
// ❌ そのままだとブラウザでは 1 行
echo $comment;
// → 1 行目 2 行目 3 行目
// ✅ nl2br で <br /> を挿入
echo nl2br($comment);
// → 1 行目<br />
// 2 行目<br />
// 3 行目
// XSS 対策とセットで使う
echo nl2br(htmlspecialchars($comment, ENT_QUOTES, 'UTF-8'));
// CRLF / LF / CR すべて対応
echo nl2br("a\r\nb\nc\rd");
// → a<br />
// b<br />
// c<br />
// d
改行コードの正規化
外部ファイル / フォーム入力 / API 受信などで混在する改行コードを統一する定番処理:
<?php
// LF (\n) に統一
function normalizeNewline(string $s): string {
return str_replace(["\r\n", "\r"], "\n", $s);
}
// CRLF (\r\n) に統一(HTTP / メール)
function toCRLF(string $s): string {
$s = str_replace(["\r\n", "\r"], "\n", $s); // 一度 LF 化
return str_replace("\n", "\r\n", $s);
}
// 改行の正規表現
preg_match_all('/\R/u', $text, $matches); // \R は任意の改行
$lineCount = count($matches[0]) + 1;
CSV / TSV / ログ出力での改行
<?php
// CSV は RFC 4180 で CRLF 推奨
$fp = fopen('out.csv', 'w');
fputcsv($fp, ['名前', '年齢']); // 内部で \r\n を使う場合と \n がある
fputcsv($fp, ['田中', 30]);
fclose($fp);
// 明示的に CRLF を入れたい場合
file_put_contents('out.csv', "name,age\r\nTanaka,30\r\n");
// ログ 1 行追記(PHP_EOL で環境依存)
file_put_contents('app.log', date('Y-m-d H:i:s') . " ERROR\n", FILE_APPEND);
// fwrite + PHP_EOL(環境依存)
$fp = fopen('app.log', 'a');
fwrite($fp, "message" . PHP_EOL);
fclose($fp);
Heredoc / Nowdoc での複数行
<?php
$name = '田中';
// Heredoc: ダブルクォート相当(変数展開あり)
$html = <<<HTML
<div>
<p>こんにちは、{$name}さん</p>
<p>本日の天気: 晴れ</p>
</div>
HTML;
// Nowdoc: シングルクォート相当(変数展開なし)
$template = <<<'TPL'
SELECT * FROM users
WHERE name = :name
AND status = 'active'
TPL;
// PHP 7.3+ では終了識別子のインデント可
function buildHtml(string $title): string {
return <<<HTML
<article>
<h1>$title</h1>
</article>
HTML;
}
PHP_EOL vs ハードコード
| シーン | 推奨 | 理由 |
|---|---|---|
| CLI ツールで標準出力 | PHP_EOL | Windows と Unix で適切に改行 |
| HTTP プロトコル ヘッダ | "\r\n" 固定 | RFC 7230 で CRLF 必須 |
| メール (SMTP) 送信 | "\r\n" 固定 | RFC 5322 で CRLF 必須 |
| JSON / SQL / 構造化文字列 | 固定 ("\n") | OS 依存にすると再現性低下 |
| HTML 出力 | nl2br() | ブラウザは改行を空白扱い |
| ファイル書き出し(社内のみ) | "\n" | テキストエディタは両対応 |
JavaScript / 他言語との対比
// JavaScript も同じ
console.log("abc\n"); // LF
console.log("abc\r\n"); // CRLF
console.log(`line1
line2`); // テンプレートリテラルは改行をそのまま含む
// Node.js の改行定数
const os = require('os');
console.log("abc" + os.EOL); // PHP_EOL 相当
よくあるトラブル
| 症状 | 原因 | 対処 |
|---|---|---|
| シングルクォートで改行されない | '\n' はリテラル | ダブルクォートに変更 |
| ブラウザで改行が反映されない | HTML が改行を空白扱い | nl2br() 適用 |
| Windows で改行が変 | LF のみ出力 → メモ帳で 1 行に | CRLF にするか PHP_EOL を使う |
| CSV ファイル化けする | 改行コード不一致 | CRLF + BOM 付き UTF-8 |
| メール送信失敗 | ヘッダの改行が LF のみ | "\r\n" 固定で出力 |
FAQ
Q: echo "\n" と print("\n") はどちらが速い?
A: echo の方が微妙に速い(言語構造なので関数呼び出しオーバーヘッドが無い)。可読性で選んで問題なし。
Q: nl2br() は元の改行を消す?
A: 消しません。<br />\n のように元の改行を残して <br> を挿入します。HTML ソースを綺麗に保ちたい場合は意図通り。
Q: ヒアドキュメント内で $var を展開させたくない
A: Nowdoc(識別子をシングルクォートで囲む <<<'TPL')を使えば一切展開されません。