核心概念:Heredoc vs Nowdoc
| 特性 | Heredoc (<<| Nowdoc (<<<'EOT') | |
| 變數解析 | ✅ 自動解析 `$var`、`{$obj->prop}` | ❌ 視為字面字串 |
| 跳脫序列 | ✅ `\n`, `\t`, `\"` 等生效 | ❌ 僅 `\\` 和 `\'` 生效 |
| 類比 | 雙引號字串 | 單引號字串 |
| 結束標記 | 須獨佔一行、無前後空白、不可縮進 | 同 |
語法規則速查
<?php
// 1. 基本 Heredoc
$str = <<<EOT
這是多行字串
變數 $var 會被解析
換行 \n 也會生效
EOT;
// 2. Nowdoc(單引號風格)
$str = <<<'EOT'
這裡的 $var 不會被解析
\n 就是反斜線加 n
EOT;
// 3. 結束標記規則
<<<SQL
SELECT * FROM users WHERE id = :id
SQL; // ✅ 正確:獨佔行、無縮進、無分號前空白
// 錯誤示範
<<<SQL
SELECT 1
SQL; // ❌ 縮進
<<<SQL
SELECT 1
SQL ; // ❌ 分號前有空白
<<<SQL
SELECT 1
SQL // ❌ 缺少分號
echo/print 搭配 Heredoc 的差異
<?php
$title = 'Heredoc Demo';
// echo 可直接輸出,無需括號,支援多參數
echo <<<EOT
<h1>$title</h1>
<p>這是直接輸出</p>
EOT;
// print 僅接受單一運算元,需括號,有回傳值 1
print(<<<EOT
<h1>$title</h1>
<p>這是 print 輸出</p>
EOT); // 回傳 1
// 變數賦值
$html = <<<EOT
<div>$title</div>
EOT;
進階變數解析技巧
$user = ['name' => 'Alice', 'age' => 25];
$obj = new class { public $role = 'admin'; };
echo <<<EOT
簡單變數: \$user['name'] = {$user['name']} // 需大括號
物件屬性: \$obj->role = {$obj->role} // 需大括號
陣列元素: \$user[0] = {$user[0]} // 需大括號
運算式: 2+2 = {2+2} // 需大括號
函數呼叫: time() = {time()} // 需大括號
EOT;
// 輸出:
// 簡單變數: $user['name'] = Alice
// 物件屬性: $obj->role = admin
// 陣列元素: $user[0] =
// 運算式: 2+2 = 4
// 函數呼叫: time() = 1700000000
縮進處理:PHP 7.3+ 支援結束標記縮進
<?php
// PHP 7.3+ 允許結束標記縮進,並自動移除每行相同前導空白
function renderCard(string $title): string {
return <<<HTML
<div class="card">
<h2>$title</h2>
<p>內容區塊</p>
</div>
HTML; // 結束標記可縮進
}
// 輸出 HTML 保留相對縮進,但無多餘前導空白
echo renderCard('標題');
/*
<div class="card">
<h2>標題</h2>
<p>內容區塊</p>
</div>
*/
常見錯誤與避坑指南
| 錯誤代碼 | 問題 | 修正 |
| `<<<EOT\n $var;\nEOT;` | 結束標記前有空白 | 結束標記必須頂格、獨佔行 |
| `<<<'EOT'\n $var;\nEOT;` | Nowdoc 變數不解析 | 改用 Heredoc 或手動拼接 |
| `echo <<<EOT\n...\nEOT` | echo 後無分號 | Heredoc 結束標記後需分號 |
| 巢狀 Heredoc | 不支援直接巢狀 | 用變數儲存內層再嵌入外層 |
| 結束標記為保留字 | `END`, `PHP` 等可能衝突 | 使用唯一標記如 `HTML_END`, `SQL_QUERY` |
實戰最佳實踐
1. SQL 語句模板
$sql = <<<SQL
SELECT u.id, u.name, p.title
FROM users u
JOIN posts p ON p.user_id = u.id
WHERE u.status = :status
AND p.created_at > :since
ORDER BY p.created_at DESC
LIMIT :limit
SQL;
$stmt = $pdo->prepare($sql);
$stmt->execute(['status'=>1, 'since'=>'2024-01-01', 'limit'=>10]);
2. HTML 片段輸出(配合縮進)
function flashMessage(string $type, string $msg): string {
return <<<HTML
<div class="alert alert-$type alert-dismissible fade show" role="alert">
$msg
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
HTML;
}
3. 郵件/通知模板
$mailBody = <<<EMAIL
親愛的 {$user['name']},
您的訂單 {$order['sn']} 已{$statusText}。
訂單金額: {$order['amount']} 元
預計送達: {$order['eta']}
如有疑請聯繫客服。
{$siteName} 敬上
EMAIL;
4. CLI 表格/幫助文字
$help = <<<HELP
Usage: php script.php [options]
Options:
-h, --help Show this help
-v, --verbose Verbose output
-c, --config Config file path
Examples:
php script.php -c config.prod.php
HELP;
效能備註
- Heredoc/Nowdoc 編譯期即解析為字串常數,**執行期零開銷**
- 大量字串拼接時,優於 `.` 運算子與 `sprintf`
- 記憶體佔用:字串常數存於 opcode 快取,不重複分配
結語
Heredoc/Nowdoc 是 PHP 處理多行字串、模板、SQL、HTML 的**原生最佳方案**。掌握變數解析規則、縮進行為(PHP 7.3+)、結束標記嚴格要求三大核心,即可寫出可讀性高、維護性強、效能佳的字串代碼。現代 PHP 專案建議:SQL 用 Heredoc、靜態模板用 Nowdoc、動態輸出用 Heredoc + 縮進。