JavaScript(瀏覽器端)與 PHP(伺服器端)運行在不同環境,資料交換必須透過 HTTP 協定。理解四種基本模式,即可掌握所有互傳場景:
伺服器渲染時將資料嵌入頁面
JS 操作 DOM 更新頁面顯示
表單提交、AJAX 請求送資料給後端
後端輸出資料供前端渲染或 API 回應
<script>
// 直接輸出 JS 變數
const appConfig = {
apiBase: '<?= htmlspecialchars($config['api_base'], ENT_QUOTES) ?>',
userId: <?= (int)$user->id ?>,
permissions: <?= json_encode($user->permissions) ?>,
csrfToken: '<?= htmlspecialchars($csrfToken, ENT_QUOTES) ?>'
};
</script>
注意:必須使用 htmlspecialchars(..., ENT_QUOTES) 防止 XSS,陣列/物件用 json_encode。
<div id="app"
data-user-id="<?= $user->id ?>"
data-permissions="<?= htmlspecialchars(json_encode($user->permissions), ENT_QUOTES) ?>"
data-csrf-token="<?= $csrfToken ?>">
</div>
<script>
const app = document.getElementById('app');
const userId = parseInt(app.dataset.userId, 10);
const permissions = JSON.parse(app.dataset.permissions);
</script>
<script>
window.APP_INITIAL_STATE = <?= json_encode([
'user' => $user->toArray(),
'settings' => $settings,
'translations' => $i18nMessages,
], JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT) ?>;
</script>
JSON_HEX_* 旗標確保安全嵌入 HTML script 標籤中。
<form action="/api/user/update" method="POST">
<input type="hidden" name="_token" value="<?= csrf_token() ?>">
<input type="text" name="name" value="<?= htmlspecialchars($user->name) ?>">
<input type="email" name="email" value="<?= htmlspecialchars($user->email) ?>">
<button type="submit">儲存</button>
</form>
PHP 接收:$_POST['name'], $_POST['email']
// 純 JS Fetch
async function updateUser(userData) {
const response = await fetch('/api/user/update', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content,
'Accept': 'application/json',
},
credentials: 'same-origin', // 攜帶 Cookie
body: JSON.stringify(userData),
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.message || '更新失敗');
}
return response.json();
}
// 使用時
updateUser({ name: '新名字', email: 'new@example.com' })
.then(data => console.log('成功:', data))
.catch(err => console.error('失敗:', err));
$input = json_decode(file_get_contents('php://input'), true);
// 或使用框架封裝:$request->json()->all()
$name = $input['name'] ?? null;
$email = $input['email'] ?? null;
// 驗證...
// 回應 JSON
header('Content-Type: application/json');
echo json_encode(['success' => true, 'data' => $user->toArray()]);
const formData = new FormData();
formData.append('avatar', fileInput.files[0]);
formData.append('name', '新名字');
fetch('/api/user/avatar', {
method: 'POST',
headers: { 'X-CSRF-TOKEN': csrfToken },
credentials: 'same-origin',
body: formData, // 別設 Content-Type,瀏覽器自動設定 multipart/form-data
});
// 成功回應
http_response_code(200);
header('Content-Type: application/json; charset=utf-8');
echo json_encode([
'success' => true,
'data' => $user->toArray(),
'meta' => ['timestamp' => microtime(true)],
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
// 錯誤回應
http_response_code(422);
echo json_encode([
'success' => false,
'message' => '驗證失敗',
'errors' => [
'email' => ['Email 格式不正確'],
'name' => ['名稱不可為空'],
],
], JSON_UNESCAPED_UNICODE);
<?php // partials/user-row.php ?>
<tr data-user-id="<?= $user->id ?>">
<td><?= htmlspecialchars($user->name) ?></td>
<td><?= htmlspecialchars($user->email) ?></td>
<td><?= $user->status_label ?></td>
<td>
<button class="btn-edit" data-action="edit">編輯</button>
<button class="btn-delete" data-action="delete">刪除</button>
</td>
</tr>
AJAX 回應 HTML 片段,前端直接 container.innerHTML = responseText 或 $(container).html(response)。
<?php // sse.php ?>
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
header('Connection: keep-alive');
while (true) {
$data = getRealtimeData(); // 從 Redis/DB 獲取
echo "data: " . json_encode($data) . "\n\n";
ob_flush();
flush();
sleep(2);
// 檢查客戶端是否斷開
if (connection_aborted()) break;
}
// 前端
const eventSource = new EventSource('/sse.php');
eventSource.onmessage = (e) => {
const data = JSON.parse(e.data);
updateDashboard(data);
};
eventSource.onerror = () => eventSource.close();
// 使用 Laravel Echo + Pusher/Soketi
Echo.private('order.' . orderId)
.listen('OrderStatusUpdated', (e) => {
updateOrderStatus(e.order_id, e.status);
});
| 威脅 | 防護措施 | |
|---|---|---|
| XSS (Cross-Site Scripting) | 輸出時 htmlspecialchars($var, ENT_QUOTES, 'UTF-8')、CSP 標頭、JSON_HEX_* 旗標 | |
| CSRF (Cross-Site Request Forgery) | 所有狀態改變請求驗證 CSRF Token(表單隱藏欄位、AJAX 標頭) | |
| SQL Injection | 參數化查詢(PDO 預處理)、ORM、嚴禁字串拼接 SQL | |
| 敏感資料洩露 | API 最小權限原則、敏感欄位不過度暴露、HTTPS 強制 | |
| 點擊劫持 | X-Frame-Options: DENY 或 Content-Security-Policy: frame-ancestors 'none' |
| 框架 | 資料傳遞方式 | 特色 |
|---|---|---|
| Laravel | Blade @json、Controller resource、API Resource | 自動 CSRF、API Resource 轉換、Eloquent 隱藏屬性 |
| Symfony | Twig json_encode、Serializer、API Platform | 序列化群組、DTO、OpenAPI 整合 |
| CodeIgniter 4 | View 渲染、ResourcePresentation | 簡單直接、內建 CSRF |
| Yii 2 | View params、ActiveRecord fields、REST Controller | 資料格式化、HATEOAS |