第十一章-PHP表单传值
一,核心概念
1. 表单的基本结构(HTML)
通过HTML的<form>
标签定义表单,关键属性包括:
action
: 指定处理表单数据的PHP脚本路径(如action="process.php"
)。method
: 定义数据传输方式,常用GET
或POST
。
html
<form action="process.php" method="post">
<input type="text" name="username">
<input type="password" name="password">
<input type="submit" value="提交">
</form>
2. PHP接收表单数据的超全局变量
PHP通过预定义超全局变量获取表单数据:
$_GET
: 接收通过method="get"
提交的数据(数据附加在URL中)。$_POST
: 接收通过method="post"
提交的数据(数据通过HTTP请求体传输)。$_REQUEST
: 合并了$_GET
、$_POST
和$_COOKIE
的数据(不推荐,因安全性低)。
示例:获取数据
php
// 通过POST获取表单字段
$username = $_POST['username'];
$password = $_POST['password'];
3. GET vs POST的区别
3.1 底层原理
- GET请求 :
- 数据通过URL参数传输,如
process.php?name=John&age=25
。 - 浏览器历史记录和服务器日志会保存完整URL。
- 数据通过URL参数传输,如
- POST请求 :
- 数据在HTTP请求体中传输,不可见。
- 适用于敏感操作(如修改数据库)。
3.2 使用场景扩展
- GET的典型场景 :
- 搜索引擎关键词传递:
search.php?q=keyword
。 - 分页导航:
articles.php?page=2
。
- 搜索引擎关键词传递:
- POST的典型场景 :
- 用户注册、登录、支付操作。
- 上传文件或提交大型文本(如博客文章)。
特性 | GET | POST |
---|---|---|
数据位置 | URL参数(可见) | HTTP请求体(不可见) |
数据长度限制 | 受URL长度限制(约2048字符) | 无限制(服务器配置可能限制) |
安全性 | 不适合敏感数据(如密码) | 相对更安全 |
缓存/书签 | 可缓存、可收藏为书签 | 不可缓存 |
典型用途 | 搜索、分页等非敏感操作 | 登录、注册、文件上传 |
4. 安全性注意事项
-
过滤输入:始终验证和清理用户输入,避免SQL注入、XSS攻击。
php$username = filter_input(INPUT_POST, 'username', FILTER_SANITIZE_STRING);
-
防止CSRF:使用令牌(Token)验证请求来源。
-
文件上传 :需设置
enctype="multipart/form-data"
,并通过$_FILES
处理。
5. 完整示例
HTML表单(form.html
):
html
<form action="process.php" method="post">
邮箱:<input type="email" name="email">
密码:<input type="password" name="password">
<input type="submit" value="登录">
</form>
PHP处理脚本(process.php
):
php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$email = $_POST['email'];
$password = $_POST['password'];
// 简单验证
if (!empty($email) && !empty($password)) {
echo "登录成功!邮箱:$email";
} else {
echo "请填写所有字段!";
}
}
6. 其他传值方式
- 文件上传 :使用
$_FILES
处理。 - 隐藏字段 :
<input type="hidden" name="id" value="123">
传递隐藏数据。 - Session/Cookie :结合
$_SESSION
和$_COOKIE
管理用户状态。
二,表单传值方式
1. 基础传值方式
(1) GET 方法
-
特点:
- 数据通过URL参数传递(如
?key1=value1&key2=value2
)。 - 数据可见、长度受限(约2048字符)。
- 可缓存、可书签保存。
- 数据通过URL参数传递(如
-
适用场景:
- 搜索、分页、筛选等非敏感操作。
-
示例:
html<form action="search.php" method="get"> <input type="text" name="keyword"> <input type="submit" value="搜索"> </form>
php// PHP获取数据 $keyword = $_GET['keyword'];
(2) POST 方法
-
特点:
- 数据通过HTTP请求体传输,不可见。
- 无长度限制(受服务器配置影响)。
- 不可缓存,适合敏感操作。
-
适用场景:
- 用户登录、注册、文件上传等。
-
示例:
html<form action="login.php" method="post"> <input type="text" name="username"> <input type="password" name="password"> <input type="submit" value="登录"> </form>
php$username = $_POST['username']; $password = $_POST['password'];
2. 扩展传值方式
(1) 隐藏字段(Hidden Fields)
-
用途:
- 传递无需用户填写但后端需要的数据(如用户ID、令牌)。
-
示例:
html<form action="update.php" method="post"> <input type="hidden" name="user_id" value="123"> <input type="text" name="new_email"> <input type="submit" value="更新邮箱"> </form>
php$user_id = $_POST['user_id'];
(2) 文件上传(File Upload)
-
特点:
- 需设置表单的
enctype="multipart/form-data"
。 - 通过
$_FILES
超全局数组处理文件。
- 需设置表单的
-
示例:
html<form action="upload.php" method="post" enctype="multipart/form-data"> <input type="file" name="avatar"> <input type="submit" value="上传头像"> </form>
php$file_name = $_FILES['avatar']['name']; $tmp_path = $_FILES['avatar']['tmp_name']; move_uploaded_file($tmp_path, "uploads/$file_name");
3. 高级传值技术
(1) AJAX 异步传值
-
用途:
- 不刷新页面提交数据,提升用户体验。
-
示例(使用JavaScript Fetch API):
javascript// 前端代码 document.getElementById('myForm').addEventListener('submit', function(e) { e.preventDefault(); const formData = new FormData(this); fetch('api/save.php', { method: 'POST', body: formData }) .then(response => response.json()) .then(data => console.log(data)); });
php// PHP返回JSON响应 header('Content-Type: application/json'); echo json_encode(['status' => 'success']);
(2) Session 和 Cookie
-
Session:
- 服务器端存储用户状态,通过
$_SESSION
访问。
phpsession_start(); $_SESSION['user_id'] = 123; // 存储 $userId = $_SESSION['user_id']; // 读取
- 服务器端存储用户状态,通过
-
Cookie:
- 客户端存储数据,通过
$_COOKIE
访问。
phpsetcookie('theme', 'dark', time() + 3600); // 设置 $theme = $_COOKIE['theme']; // 读取
- 客户端存储数据,通过
4. RESTful API 传值方式
(1) HTTP 方法扩展
-
PUT/DELETE:
- 用于更新或删除资源(需通过AJAX或框架模拟)。
php// 伪代码:通过POST模拟PUT if ($_POST['_method'] === 'PUT') { // 处理更新逻辑 }
(2) JSON 数据传值
-
前端发送JSON:
javascriptfetch('api/users', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name: 'John', age: 30 }) });
-
PHP接收JSON:
php$data = json_decode(file_get_contents('php://input'), true); $name = $data['name'];
5. 安全性对比与选择建议
传值方式 | 安全性 | 适用场景 | 注意事项 |
---|---|---|---|
GET | 低 | 公开数据查询 | 避免传递敏感信息 |
POST | 中 | 表单提交、敏感操作 | 结合HTTPS使用 |
Session | 高 | 用户登录状态、跨页面数据共享 | 及时销毁Session防止会话固定 |
Cookie | 中 | 客户端偏好设置 | 避免存储敏感数据 |
AJAX/JSON | 高 | 前后端分离、API交互 | 需验证来源和CORS配置 |
6. 最佳实践
-
始终验证输入:
php$email = filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL); if (!$email) die("邮箱格式无效");
-
防范CSRF:
php// 生成Token $_SESSION['csrf_token'] = bin2hex(random_bytes(32)); // 表单中嵌入 <input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>"> // 验证Token if ($_POST['csrf_token'] !== $_SESSION['csrf_token']) die("非法请求");
-
使用HTTPS:
- 对敏感数据传输强制启用HTTPS。
三,接收数据方式
1. 基础数据接收方式
(1) 通过 $_GET
接收URL参数
-
场景 :处理
method="get"
表单或URL中的查询参数(如?id=123
)。 -
示例:
php// URL: example.com?name=John&age=25 $name = $_GET['name'] ?? '未填写姓名'; // 使用空合并运算符避免未定义错误 $age = filter_input(INPUT_GET, 'age', FILTER_VALIDATE_INT); // 过滤并验证为整数
(2) 通过 $_POST
接收表单数据
-
场景 :处理
method="post"
的表单提交。 -
示例:
php// 表单字段:<input type="text" name="email"> $email = $_POST['email'] ?? ''; // 使用过滤器验证邮箱格式 $email = filter_var($email, FILTER_VALIDATE_EMAIL); if (!$email) { die("邮箱格式无效"); }
(3) 通过 $_REQUEST
接收混合数据
-
注意 :
$_REQUEST
合并了$_GET
、$_POST
和$_COOKIE
,但不推荐使用(安全性低,优先级不可控)。 -
示例:
php$data = $_REQUEST['key']; // 可能来自GET、POST或COOKIE
2. 复杂数据接收方式
(1) 接收数组数据
-
场景:表单中多选框(Checkboxes)或同名字段。
-
HTML示例:
html<input type="checkbox" name="hobbies[]" value="reading"> 阅读 <input type="checkbox" name="hobbies[]" value="sports"> 运动
-
PHP处理:
php$hobbies = $_POST['hobbies'] ?? []; if (!empty($hobbies)) { foreach ($hobbies as $hobby) { echo htmlspecialchars($hobby); // 转义输出防止XSS } }
(2) 接收文件数据($_FILES
)
-
场景 :文件上传表单(需设置
enctype="multipart/form-data"
)。 -
HTML示例:
html<form action="upload.php" method="post" enctype="multipart/form-data"> <input type="file" name="file_upload"> </form>
-
PHP处理:
php$file = $_FILES['file_upload']; if ($file['error'] === UPLOAD_ERR_OK) { $tmp_name = $file['tmp_name']; $target_path = "uploads/" . basename($file['name']); move_uploaded_file($tmp_name, $target_path); }
(3) 接收JSON数据
-
场景:前端通过AJAX发送JSON格式数据(如API请求)。
-
PHP处理:
php$json_data = file_get_contents('php://input'); // 读取原始输入流 $data = json_decode($json_data, true); // 转换为关联数组 if (json_last_error() !== JSON_ERROR_NONE) { die("JSON解析失败"); } $username = $data['username'] ?? '';
3. 安全性处理
(1) 输入过滤与验证
-
推荐函数 :
filter_input()
、filter_var()
-
示例:
php// 过滤并验证整数 $id = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT); if ($id === false || $id < 1) { die("ID无效"); } // 清理字符串(移除标签和空格) $comment = filter_input(INPUT_POST, 'comment', FILTER_SANITIZE_STRING); $comment = trim($comment);
(2) 防止SQL注入
-
使用预处理语句(PDO示例):
php$pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass'); $stmt = $pdo->prepare("SELECT * FROM users WHERE email = :email"); $stmt->execute(['email' => $_POST['email']]); $user = $stmt->fetch();
(3) 防止跨站请求伪造(CSRF)
-
生成并验证Token:
phpsession_start(); // 生成Token if (empty($_SESSION['csrf_token'])) { $_SESSION['csrf_token'] = bin2hex(random_bytes(32)); } // 表单中嵌入Token <input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>"> // 验证Token if ($_POST['csrf_token'] !== $_SESSION['csrf_token']) { die("非法请求"); }
4. 其他数据接收方式
(1) 命令行参数($argv
和 $argc
)
-
场景:PHP CLI(命令行界面)脚本接收参数。
-
示例:
bashphp script.php arg1 arg2
php// script.php print_r($argv); // 输出:Array([0] => script.php, [1] => arg1, [2] => arg2)
(2) 接收HTTP头部信息(getallheaders()
或 $_SERVER
)
-
场景:获取请求头信息(如认证Token)。
-
示例:
php$headers = getallheaders(); $auth_token = $headers['Authorization'] ?? '';
(3) 处理PUT/DELETE请求
-
场景:RESTful API中处理非POST请求。
-
示例:
phpif ($_SERVER['REQUEST_METHOD'] === 'PUT') { parse_str(file_get_contents('php://input'), $put_data); $id = $put_data['id']; // 处理更新逻辑 }
5. 最佳实践总结
-
始终检查变量是否存在:
php// 使用 isset() 或空合并运算符 $value = $_POST['key'] ?? 'default';
-
优先使用过滤器函数:
php$email = filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL);
-
避免直接使用未过滤的数据:
php// 错误示例(易受XSS攻击) echo $_GET['content']; // 正确做法 echo htmlspecialchars($_GET['content']);
-
严格限制文件上传类型和大小:
php$allowed_types = ['image/jpeg', 'image/png']; if (!in_array($_FILES['file']['type'], $allowed_types)) { die("只允许上传JPEG和PNG图片"); }
四,PHP处理复选框
1. HTML复选框基础
1.1 正确命名复选框
-
关键点 :使用数组形式命名(
name="字段名[]"
),以便PHP接收多个值。 -
示例:
html<form action="process.php" method="post"> <input type="checkbox" name="hobbies[]" value="reading"> 阅读 <input type="checkbox" name="hobbies[]" value="coding"> 编程 <input type="checkbox" name="hobbies[]" value="sports"> 运动 <input type="submit" value="提交"> </form>
1.2 动态生成复选框
-
场景:从数据库或配置中加载选项。
-
PHP动态生成示例:
php$allowedHobbies = ['reading', 'coding', 'sports', 'music']; foreach ($allowedHobbies as $hobby) { echo '<input type="checkbox" name="hobbies[]" value="' . htmlspecialchars($hobby) . '"> ' . $hobby; }
2. PHP接收复选框数据
2.1 接收数组数据
-
使用
$_POST
或$_GET
:php$selectedHobbies = $_POST['hobbies'] ?? [];
- 若用户未勾选任何复选框,
$_POST['hobbies']
将不存在,需用空合并运算符 (??
) 避免错误。
- 若用户未勾选任何复选框,
2.2 验证与过滤数据
-
检查是否为数组:
phpif (!is_array($selectedHobbies)) { die("非法请求"); }
-
过滤非法值:
php$allowedValues = ['reading', 'coding', 'sports', 'music']; $validHobbies = array_intersect($selectedHobbies, $allowedValues); if (count($validHobbies) === 0) { die("请至少选择一个有效兴趣"); }
3. 安全性处理
3.1 防止XSS攻击
-
转义输出:
phpforeach ($validHobbies as $hobby) { echo htmlspecialchars($hobby, ENT_QUOTES, 'UTF-8'); }
3.2 防止SQL注入
-
预处理语句示例(PDO):
php$stmt = $pdo->prepare("INSERT INTO user_hobbies (user_id, hobby) VALUES (:user_id, :hobby)"); foreach ($validHobbies as $hobby) { $stmt->execute([ 'user_id' => $userId, 'hobby' => $hobby ]); }
4. 常见问题与解决方案
问题1:只接收到最后一个值
- 原因 :复选框未使用数组命名(如
name="hobby"
)。 - 解决 :确保命名格式为
name="字段名[]"
。
问题2:未选中时报错 Undefined index
-
原因 :直接访问
$_POST['hobbies']
而未检查是否存在。 -
解决 :使用空合并运算符或
isset()
:php$selectedHobbies = isset($_POST['hobbies']) ? $_POST['hobbies'] : [];
问题3:动态生成的选项被篡改
- 场景:用户提交了不在允许列表中的值。
- 解决 :通过
array_intersect()
过滤非法值(见2.2节)。
5. 完整示例
HTML表单(form.html
)
html
<form action="process.php" method="post">
<fieldset>
<legend>选择你的兴趣:</legend>
<?php
$hobbies = ['reading' => '阅读', 'coding' => '编程', 'sports' => '运动'];
foreach ($hobbies as $value => $label) {
echo '<label><input type="checkbox" name="hobbies[]" value="' . htmlspecialchars($value) . '"> ' . htmlspecialchars($label) . '</label><br>';
}
?>
</fieldset>
<input type="submit" value="提交">
</form>
PHP处理脚本(process.php
)
php
<?php
// 1. 接收数据并验证
$selectedHobbies = $_POST['hobbies'] ?? [];
if (!is_array($selectedHobbies)) {
die("非法请求");
}
// 2. 过滤合法值
$allowedHobbies = ['reading', 'coding', 'sports'];
$validHobbies = array_intersect($selectedHobbies, $allowedHobbies);
if (empty($validHobbies)) {
die("请至少选择一个兴趣");
}
// 3. 安全存储到数据库(示例)
try {
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'user', 'pass');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$stmt = $pdo->prepare("INSERT INTO user_hobbies (user_id, hobby) VALUES (?, ?)");
foreach ($validHobbies as $hobby) {
$stmt->execute([1, $hobby]); // 假设用户ID为1
}
echo "保存成功!选择的兴趣:" . implode(', ', $validHobbies);
} catch (PDOException $e) {
die("数据库错误:" . $e->getMessage());
}
6. 高级应用
6.1 将数据存储为JSON
-
场景:单个字段存储多个选项(非关系型数据)。
-
示例:
php$hobbiesJson = json_encode($validHobbies); // 存入数据库 $stmt = $pdo->prepare("UPDATE users SET hobbies = ? WHERE id = ?"); $stmt->execute([$hobbiesJson, $userId]);
6.2 回显已选中的复选框
-
场景:编辑表单时显示用户之前的选择。
-
示例:
php$userHobbies = ['reading', 'sports']; // 从数据库读取 foreach ($allowedHobbies as $value => $label) { $checked = in_array($value, $userHobbies) ? 'checked' : ''; echo '<input type="checkbox" name="hobbies[]" value="' . $value . '" ' . $checked . '> ' . $label; }
项目示例:留言管理系统
1. 项目结构
bash
public/
├── index.php # 表单页面 & 处理结果
└── style.css # 一点简单样式
2. 代码
2.1 public/index.php
php
<?php
/**
* index.php --- 简易留言板 Demo
*
* 运行方式(任选其一):
* 1) PHP 内置服务器:php -S localhost:8000 -t .
* 2) Docker:docker run -it --rm -p 8000:80 -v "$PWD":/var/www/html php:8.3-apache
*
* 生产环境注意:
* - 请添加 CSRF token、防爆破、验证码等安全措施
* - 若要写数据库,请使用 PDO 并开启预处理防止 SQL 注入
* - 推荐使用 PRG 模式避免重复提交
*/
/* ---------- 0) 初始化 ---------- */
// 开启 Session(如果后续要加 CSRF,可在此使用)
// session_start();
// 设置默认时区,防止 date() 提示 warning
date_default_timezone_set('Asia/Shanghai');
// 统一输出编码(部分低版本 PHP CLI 默认 ISO-8859-1)
ini_set('default_charset', 'UTF-8');
// 定义变量以便模板区直接使用
$result = null;
$error = null;
/* ---------- 1) 业务逻辑:仅在 POST 时处理 ---------- */
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// 使用 null 合并运算符防 Notice
$name = trim($_POST['name'] ?? '');
$message = trim($_POST['message'] ?? '');
// ---- (1) 基础验证 ----
if ($name === '' || $message === '') {
$error = '姓名和留言内容均不能为空!';
} elseif (mb_strlen($name, 'UTF-8') > 30) {
$error = '姓名请控制在 30 个字符以内!';
} else {
// ---- (2) 业务存储 / 发送邮件 / 写日志 ----
// ★此处仅做演示;若要入库请使用 PDO + prepared statement
$result = [
'name' => htmlspecialchars($name, ENT_QUOTES, 'UTF-8'),
'message' => htmlspecialchars($message, ENT_QUOTES, 'UTF-8'),
'time' => date('Y-m-d H:i:s'),
'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown',
];
// ---- (3) 若用 PRG,取消注释下面两行 ----
// $_SESSION['flash'] = $result; // 存 flash 数据
// header('Location: '.$_SERVER['PHP_SELF']); exit;
}
}
/* ---------- 2) 取回 flash 数据(如果采用了 PRG) ---------- */
// if (isset($_SESSION['flash'])) {
// $result = $_SESSION['flash'];
// unset($_SESSION['flash']);
// }
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>PHP 表单传值 Demo</title>
<link rel="stylesheet" href="style.css">
<!-- 基础安全请求头,可按需调整 -->
<?php
header('X-Frame-Options: SAMEORIGIN');
header("Content-Security-Policy: default-src \'self\'");
?>
</head>
<body>
<h1>留言板(示例)</h1>
<!-- ---------- 3) 结果区 ---------- -->
<?php if ($result): ?>
<section class="success">
<h2>提交成功!</h2>
<p>
<strong><?= $result['name'] ?></strong>
于 <?= $result['time'] ?> 留言:
</p>
<!-- nl2br 把换行转换为 <br>;white-space:pre-wrap 在 CSS 里也能实现 -->
<blockquote><?= nl2br($result['message']) ?></blockquote>
<small>IP:<?= $result['ip'] ?></small>
</section>
<?php elseif ($error): ?>
<section class="error"><?= $error ?></section>
<?php endif; ?>
<!-- ---------- 4) 表单区 ---------- -->
<form action="" method="post" autocomplete="off">
<label>
姓名:
<input type="text"
name="name"
maxlength="30"
required
value="<?= isset($name) ? htmlspecialchars($name, ENT_QUOTES, 'UTF-8') : '' ?>">
</label>
<label>
留言:
<textarea name="message"
rows="5"
required><?= isset($message) ? htmlspecialchars($message, ENT_QUOTES, 'UTF-8') : '' ?></textarea>
</label>
<button type="submit">提交</button>
</form>
</body>
</html>
2.2 public/style.css
css
body{font-family:system-ui, sans-serif;max-width:680px;margin:40px auto;padding:0 1rem;line-height:1.6}
h1{margin-bottom:1rem}
form{display:flex;flex-direction:column;gap:1rem}
label{display:flex;flex-direction:column;font-weight:600}
input,textarea{font:inherit;padding:.5rem;border:1px solid #ccc;border-radius:6px}
button{padding:.6rem 1.2rem;border:none;border-radius:6px;cursor:pointer;background:#007aff;color:#fff;font-weight:600}
button:hover{opacity:.9}
.success, .error{padding:1rem;border-radius:6px;margin-bottom:1rem}
.success{background:#e8f9e9;border:1px solid #4caf50}
.error{background:#ffeef0;border:1px solid #f44336}
blockquote{margin:.5rem 0;padding-left:1rem;border-left:4px solid #ccc;font-style:italic;white-space:pre-wrap}