「變量傳遞」本質上是 **PHP 在生成 HTML/JS 代碼時,將數值「寫入」輸出流**,瀏覽器接收到的是純文本代碼,隨後由 JS 引擎執行。無法在 JS 中直接「呼叫」PHP 變量,也無法在 PHP 中直接「讀取」JS 變量(除非發起新的 HTTP 請求)。
最常見:將後端配置、用戶資料、語言包等注入前端。
<?php
$config = [
'apiBase' => 'https://api.example.com',
'userId' => 12345,
'locale' => 'zh-TW'
];
?>
<script>
// 使用 json_encode 確保語法正確、防 XSS
const AppConfig = <?= json_encode($config, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP) ?>;
console.log(AppConfig.apiBase); // "https://api.example.com"
</script>
關鍵點:
json_encode()** 處理字符串、陣列、物件,自動處理引號、換行、Unicode。</script> 等標籤注入(XSS 防禦)。<script> 標籤內**,而非內聯事件屬性(onclick="...")。適用於伺服端渲染的表單頁,需將後端數據傳給前端邏輯。
<form id="configForm">
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($csrfToken) ?>">
<input type="hidden" name="user_role" value="<?= htmlspecialchars($userRole) ?>">
</form>
<script>
const form = document.getElementById('configForm');
const csrfToken = form.elements.csrf_token.value;
const userRole = form.elements.user_role.value;
console.log(csrfToken, userRole);
</script>
注意:htmlspecialchars() 防止屬性值注入,json_encode() 這裡不適用(輸出會包含雙引號,導致 HTML 屬性截斷)。
表單提交時,隱藏域值隨 $_POST 回傳服務端。
<form action="/submit" method="post">
<!-- 單一值 -->
<input type="hidden" name="item_id" value="<?= htmlspecialchars($itemId) ?>">
<!-- 陣列/物件:先 JSON 編碼 -->
<input type="hidden" name="metadata" value="<?= htmlspecialchars(json_encode($metadata)) ?>">
</form>
接收端:$metadata = json_decode($_POST['metadata'], true);
前端動態計算的值(如簽名、加密數據、Canvas 生成的圖片 Base64)需提交給後端。
<form id="dataForm" action="/api/save" method="post">
<input type="hidden" name="signature" id="signatureInput" value="">
<button type="submit">提交</button>
</form>
<script>
// 假設經過複雜計算得到 signature
const signature = generateSignature({userId: 123, timestamp: Date.now()});
// 寫入隱藏域
document.getElementById('signatureInput').value = signature;
// 或動態創建(適用於無固定表單場景)
const input = document.createElement('input');
input.type = 'hidden';
input.name = 'dynamic_data';
input.value = JSON.stringify(complexObject);
document.getElementById('dataForm').appendChild(input);
</script>
<?php
// layout.php 或基礎模板
$initialData = [
'user' => $currentUser ? ['id'=>$currentUser->id, 'name'=>$currentUser->name] : null,
'csrfToken' => $csrfToken,
'apiBase' => $config['api_base'],
'featureFlags' => $featureFlags,
];
?>
<script id="initial-data" type="application/json"><?= json_encode($initialData, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP) ?></script>
前端讀取:
const initialData = JSON.parse(document.getElementById('initial-data').textContent);
// 或使用 type="application/json" 瀏覽器自動解析(現代框架常用)
對於複雜交互,**優先用 fetch/axios 呼叫 API**,而非塞滿隱藏域:
// 前端
const response = await fetch('/api/user/preferences', {
method: 'POST',
headers: {'Content-Type': 'application/json', 'X-CSRF-Token': initialData.csrfToken},
body: JSON.stringify({theme: 'dark', lang: 'zh-TW'})
});
| 風險 | 防禦措施 |
|---|---|
| XSS(腳本注入) | 輸出到 JS 代碼用 json_encode($val, JSON_HEX_TAG|...);輸出到 HTML 屬性用 htmlspecialchars($val, ENT_QUOTES, 'UTF-8') |
| CSRF(跨站請求偽造) | 所有狀態改變請求攜帶 CSRF Token(隱藏域或 Header) |
| 敏感數據洩露 | 不要將密碼、Token、內部 ID 直接輸出到前端;前端只需最小權限數據 |
| 類型混淆 | PHP json_encode 陣列輸出為 JS 物件/陣列,注意 [] vs {} 差異 |
<script> const data = <?= json_encode($data, JSON_HEX_*) ?>; </script>htmlspecialchars() 寫入 value,提交後 $_POST 讀取。input.value 或動態創建 input[type=hidden]。type="application/json" 數據島 + API 交互,減少模板耦合。理解「服務端模板生成客戶端代碼」這一本質,變量傳遞就不再是難題,而是可控的工程環節。