National Yang Ming Chiao Tung University
Uedu Main Site
Explore Uedu
Student Console
Register as Member/Login
Research Informed Consent Center
Survey Center
Educational Big Data Challenge
Claim or create a course
Try a Scenario Task
Support & Messages
Uptime Data

UeduGPTs

--

Jupyters

1

Local AI

--

Uedu Code

--

陽明交大 AQI 69 24°C PM2.5 26
AI Reply Desktop Notifications

Show a desktop notification when the AI TA finishes replying

Chat Message Notifications

Notify me when classmates post messages in the forum

Sound notification

Play an alert sound whenever there is a new notification

Unit 11

表單處理

GET 與 POST、$_GET 與 $_POST、驗證使用者輸入、顯示錯誤與 PRG 模式
Interaction實作 30 minute(s)

Learning Objectives

  • 說明表單送出時,瀏覽器把資料放在網址(GET)或請求本體(POST)送到伺服器
  • 用 $_GET、$_POST 讀取資料,用 $_SERVER['REQUEST_METHOD'] 判斷是不是送出表單
  • 驗證使用者輸入:必填、長度、格式(filter_var),把錯誤訊息顯示給使用者並保留已填的內容
  • 說明為什麼輸出使用者輸入時一定要用 htmlspecialchars 跳脫(XSS)
  • 用 PRG(Post/Redirect/Get)模式避免重新整理時重複送出

表單:讓使用者把資料送到伺服器

登入、留言、報名、搜尋,都是表單(form):使用者在網頁上填資料,按下按鈕,瀏覽器把資料送到伺服器, 伺服器上的 PHP 讀取資料、處理,再產生一個新的網頁回傳。

<form method="post" action="register.php">
  <input name="name">          <!-- name 屬性就是送到伺服器的欄位名稱 -->
  <input name="email">
  <button>送出</button>
</form>
  • action:資料送到哪個網址;省略時送回目前這一頁
  • method:送的方式,get 或 post(省略時是 get)
  • 每個欄位的 name 屬性,就是 PHP 讀取時用的鍵;沒有 name 的欄位不會被送出

PHP 收到請求時,已經把資料整理成超全域變數(superglobals):在程式任何地方、包括函式裡,都可以直接使用。

變數 內容
$_GET 網址查詢字串(? 後面)的資料
$_POST 用 POST 送出的表單資料
$_SERVER 這次請求的資訊:REQUEST_METHOD、REQUEST_URI、HTTP_HOST…
$_COOKIE、$_SESSION 第 16 課介紹

本課的範例模擬一次 HTTP 請求執行 PHP;逐步執行動畫的全域框架會顯示這次請求帶來的 $_GET、$_POST。

GET 與 POST

GET POST
資料放在哪裡 網址:/search.php?q=php&page=2 請求的本體(body),網址看不到
PHP 讀取 $_GET['q'] $_POST['q']
可以加入書籤、分享連結 可以 不行
重新整理 直接再送一次 瀏覽器會問要不要「重新傳送表單」
適合 搜尋、篩選、分頁:只讀取資料 登入、報名、留言:會修改伺服器上的資料

密碼不要用 GET 送:網址會留在瀏覽紀錄、伺服器紀錄檔和書籤裡。不過 POST 也只是「網址看不到」,資料一樣是明文傳送, 保護傳輸內容要靠 HTTPS。

讀取網址的查詢字串

這個範例模擬瀏覽器請求 /index.php?name=Amy&lang=php。全域框架從第一步就有 $_GET,右邊可以看到它的內容。網址裡沒有的欄位(page)用 ?? 給預設值,避免「Undefined array key」警告。

讀取與驗證 POST 資料

永遠不要相信使用者送來的資料。欄位可能是空的、格式不對、長度超過,甚至根本沒有送(有人直接用程式發送請求)。 處理表單的固定步驟:

  1. 用 $_SERVER['REQUEST_METHOD'] === 'POST' 判斷是不是送出表單(第一次打開頁面是 GET,不用檢查)
  2. 用 ?? '' 讀取欄位,欄位不存在也不會出現警告;用 trim() 去掉前後空白
  3. 逐一檢查,把錯誤訊息放進一個陣列
  4. 沒有錯誤才真正處理(存進資料庫、寄信…);有錯誤就重新顯示表單和錯誤訊息

常用的檢查:

檢查 寫法
必填 $name === ''
長度(中文) mb_strlen($name) > 20
Email 格式 filter_var($email, FILTER_VALIDATE_EMAIL),不合格回傳 false
整數與範圍 filter_var($age, FILTER_VALIDATE_INT, ['options' => ['min_range' => 1, 'max_range' => 120]])
只能是某幾個值 in_array($color, ['red', 'green'], true)

filter_var 驗證成功時回傳轉換後的值(例如整數 20),失敗回傳 false,所以判斷時要用 === false: 年齡填 0 的時候,if (!$age) 會把合法的 0 也當成失敗。

驗證表單欄位

模擬的 POST 資料是:姓名 " 小明 "(前後有空白)、Email ming@、年齡 "20"。注意 $_POST 裡的值都是字串;filter_var 驗證整數成功後,$age 變成整數 20。

想一想:GET、POST 與驗證

1.

使用者在搜尋框輸入「資料庫」,表單是 <form method="get" action="search.php">、欄位 name="kw"。PHP 要怎麼讀到這個值?

2.

模擬 POST 送出 qty=0,下面的程式會印出什麼?

<?php
$qty = filter_var($_POST['qty'], FILTER_VALIDATE_INT);
if (!$qty) {
    echo '數量不合法';
} else {
    echo "數量 $qty";
}
第 1 次作答

輸出時一定要跳脫:XSS

PHP 產生的是 HTML。如果把使用者的輸入原封不動放進 HTML,輸入裡的 <script> 就會被瀏覽器當成程式執行, 這種攻擊叫 XSS(Cross-Site Scripting,跨網站指令碼):攻擊者在留言板留下一段程式,每個看留言的人都會執行它, 可能被偷走登入狀態、被導到釣魚網站。

解法是輸出前用 htmlspecialchars() 把 HTML 的特殊字元換成實體(entity),瀏覽器就只會把它們當成文字顯示:

字元 變成
& &amp;
<、> &lt;、&gt;
" &quot;
' &#039;

原則:輸入時驗證、輸出時跳脫。資料存進資料庫時保留原樣,輸出到 HTML 的那一刻才跳脫。 第 18 課會再整理其他常見的攻擊與防護。

有跳脫與沒有跳脫

模擬有人在留言欄位送出 <script>alert("hi")</script>。看最後的輸出:第一行把它原樣放進 HTML,瀏覽器會執行這段程式;第二行跳脫之後只是一段文字。

多選欄位:名稱加上 []

勾選框(checkbox)可以勾好幾個。欄位名稱寫成 hobby[],PHP 會把勾選的值收集成一個陣列:

<input type="checkbox" name="hobby[]" value="閱讀"> 閱讀
<input type="checkbox" name="hobby[]" value="運動"> 運動
<input type="checkbox" name="hobby[]" value="程式"> 程式

沒有勾選的勾選框不會被送出:一個都沒勾時,$_POST 裡根本沒有 hobby,所以要寫 $_POST['hobby'] ?? []。 送來的值也可能被竄改,用 array_intersect 只留下允許的選項。

勾選框送出的陣列

模擬勾選了「閱讀」和「程式」。右邊可以看到 $_POST['hobby'] 本身就是一個陣列。

PRG 模式:處理完就轉址

POST 處理成功後,如果直接在同一頁顯示「報名成功」,使用者按下重新整理,瀏覽器會再送一次同樣的 POST, 報名就重複了。常見的做法是 PRG(Post/Redirect/Get):

  1. 瀏覽器 POST 表單
  2. PHP 處理成功,回應 header('Location: thanks.php'); 並 exit;,狀態碼是 302(轉址)
  3. 瀏覽器自動用 GET 請求 thanks.php

使用者最後看到的是一個 GET 請求的頁面,重新整理也只會再 GET 一次。

header() 必須在任何輸出之前呼叫(包括 <?php 前面的空白與 HTML),否則會出現 「Cannot modify header information - headers already sent」警告,第 16 課會再說明。 header() 之後一定要 exit;,否則程式會繼續執行後面的內容。

動手操作:搜尋與報名表單

這是一個模擬的瀏覽器,網站有 index.php 和 thanks.php 兩個檔案。每次送出表單或點連結,都會在下方的「請求紀錄」多一筆,「HTTP 回應」可以看到狀態碼與標頭。照著任務操作,也可以修改程式再按「儲存並重新整理」。

想一想:跳脫與轉址

1.

模擬 GET 請求 ?name=<b>Amy</b>,下面的程式會輸出什麼 HTML?

<?php
echo 'Hi ' . htmlspecialchars($_GET['name']);
2.

下面的程式處理 POST 之後轉址,但少寫了 exit;。模擬 POST 送出時,回應的內容會是什麼?

<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    header('Location: done.php');
}
echo '這裡是表單頁';
第 1 次作答

動手做:驗證密碼設定表單

模擬使用者用 POST 送出 user=amy、pw=abc123、pw2=abc124。請完成驗證:帳號 3 到 12 個字元、密碼至少 8 個字元、兩次輸入的密碼要相同。依序檢查,每個錯誤印一行;全部通過才印「設定完成:帳號」。

你的輸出
預期輸出

重點回顧

  • 表單欄位的 name 就是 PHP 讀取的鍵;GET 的資料在網址、用 $_GET 讀,POST 的資料在請求本體、用 $_POST 讀
  • 只讀取資料(搜尋、分頁)用 GET;會修改資料(登入、報名)用 POST
  • 用 $_SERVER['REQUEST_METHOD'] === 'POST' 判斷是不是送出表單;讀欄位寫 $_POST['x'] ?? '',再 trim()
  • 永遠驗證使用者輸入:必填、長度用 mb_strlen、格式用 filter_var(判斷失敗要用 === false)
  • 輸出使用者輸入到 HTML 之前一定要 htmlspecialchars(),避免 XSS:輸入時驗證、輸出時跳脫
  • 多選欄位名稱寫 hobby[],PHP 收到陣列;沒有勾選時欄位不存在
  • POST 處理成功後用 header('Location: …'); exit; 轉址(PRG),避免重新整理時重複送出
這一課完成了!
Next lesson:檔案操作
你目前沒有登入,學習進度只存在這個瀏覽器。 Login 後,進度會跟著帳號,換電腦也看得到。