第十一章-PHP表单传值
第十一章-PHP表单传值
一,核心概念
1. 表单的基本结构(HTML)
通过HTML的<form>
标签定义表单,关键属性包括:
action
: 指定处理表单数据的PHP脚本路径(如action="process.php"
)。method
: 定义数据传输方式,常用GET
或POST
。
<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
的数据(不推荐,因安全性低)。
示例:获取数据
// 通过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攻击。
$username = filter_input(INPUT_POST, 'username', FILTER_SANITIZE_STRING);
-
防止CSRF:使用令牌(Token)验证请求来源。
-
文件上传:需设置
enctype="multipart/form-data"
,并通过$_FILES
处理。
5. 完整示例
HTML表单(form.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
):
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参数传递(如
-
适用场景:
- 搜索、分页、筛选等非敏感操作。
-
示例:
<form action="search.php" method="get"><input type="text" name="keyword"><input type="submit" value="搜索"> </form>
// PHP获取数据 $keyword = $_GET['keyword'];
(2) POST 方法
-
特点:
- 数据通过HTTP请求体传输,不可见。
- 无长度限制(受服务器配置影响)。
- 不可缓存,适合敏感操作。
-
适用场景:
- 用户登录、注册、文件上传等。
-
示例:
<form action="login.php" method="post"><input type="text" name="username"><input type="password" name="password"><input type="submit" value="登录"> </form>
$username = $_POST['username']; $password = $_POST['password'];
2. 扩展传值方式
(1) 隐藏字段(Hidden Fields)
-
用途:
- 传递无需用户填写但后端需要的数据(如用户ID、令牌)。
-
示例:
<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>
$user_id = $_POST['user_id'];
(2) 文件上传(File Upload)
-
特点:
- 需设置表单的
enctype="multipart/form-data"
。 - 通过
$_FILES
超全局数组处理文件。
- 需设置表单的
-
示例:
<form action="upload.php" method="post" enctype="multipart/form-data"><input type="file" name="avatar"><input type="submit" value="上传头像"> </form>
$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):
// 前端代码 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返回JSON响应 header('Content-Type: application/json'); echo json_encode(['status' => 'success']);
(2) Session 和 Cookie
-
Session:
- 服务器端存储用户状态,通过
$_SESSION
访问。
session_start(); $_SESSION['user_id'] = 123; // 存储 $userId = $_SESSION['user_id']; // 读取
- 服务器端存储用户状态,通过
-
Cookie:
- 客户端存储数据,通过
$_COOKIE
访问。
setcookie('theme', 'dark', time() + 3600); // 设置 $theme = $_COOKIE['theme']; // 读取
- 客户端存储数据,通过
4. RESTful API 传值方式
(1) HTTP 方法扩展
-
PUT/DELETE:
- 用于更新或删除资源(需通过AJAX或框架模拟)。
// 伪代码:通过POST模拟PUT if ($_POST['_method'] === 'PUT') {// 处理更新逻辑 }
(2) JSON 数据传值
-
前端发送JSON:
fetch('api/users', {method: 'POST',headers: { 'Content-Type': 'application/json' },body: JSON.stringify({ name: 'John', age: 30 }) });
-
PHP接收JSON:
$data = json_decode(file_get_contents('php://input'), true); $name = $data['name'];
5. 安全性对比与选择建议
传值方式 | 安全性 | 适用场景 | 注意事项 |
---|---|---|---|
GET | 低 | 公开数据查询 | 避免传递敏感信息 |
POST | 中 | 表单提交、敏感操作 | 结合HTTPS使用 |
Session | 高 | 用户登录状态、跨页面数据共享 | 及时销毁Session防止会话固定 |
Cookie | 中 | 客户端偏好设置 | 避免存储敏感数据 |
AJAX/JSON | 高 | 前后端分离、API交互 | 需验证来源和CORS配置 |
6. 最佳实践
-
始终验证输入:
$email = filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL); if (!$email) die("邮箱格式无效");
-
防范CSRF:
// 生成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
)。 -
示例:
// URL: example.com?name=John&age=25 $name = $_GET['name'] ?? '未填写姓名'; // 使用空合并运算符避免未定义错误 $age = filter_input(INPUT_GET, 'age', FILTER_VALIDATE_INT); // 过滤并验证为整数
(2) 通过 $_POST
接收表单数据
-
场景:处理
method="post"
的表单提交。 -
示例:
// 表单字段:<input type="text" name="email"> $email = $_POST['email'] ?? ''; // 使用过滤器验证邮箱格式 $email = filter_var($email, FILTER_VALIDATE_EMAIL); if (!$email) {die("邮箱格式无效"); }
(3) 通过 $_REQUEST
接收混合数据
-
注意:
$_REQUEST
合并了$_GET
、$_POST
和$_COOKIE
,但不推荐使用(安全性低,优先级不可控)。 -
示例:
$data = $_REQUEST['key']; // 可能来自GET、POST或COOKIE
2. 复杂数据接收方式
(1) 接收数组数据
-
场景:表单中多选框(Checkboxes)或同名字段。
-
HTML示例:
<input type="checkbox" name="hobbies[]" value="reading"> 阅读 <input type="checkbox" name="hobbies[]" value="sports"> 运动
-
PHP处理:
$hobbies = $_POST['hobbies'] ?? []; if (!empty($hobbies)) {foreach ($hobbies as $hobby) {echo htmlspecialchars($hobby); // 转义输出防止XSS} }
(2) 接收文件数据($_FILES
)
-
场景:文件上传表单(需设置
enctype="multipart/form-data"
)。 -
HTML示例:
<form action="upload.php" method="post" enctype="multipart/form-data"><input type="file" name="file_upload"> </form>
-
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处理:
$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()
-
示例:
// 过滤并验证整数 $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示例):
$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:
session_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(命令行界面)脚本接收参数。
-
示例:
php script.php arg1 arg2
// script.php print_r($argv); // 输出:Array([0] => script.php, [1] => arg1, [2] => arg2)
(2) 接收HTTP头部信息(getallheaders()
或 $_SERVER
)
-
场景:获取请求头信息(如认证Token)。
-
示例:
$headers = getallheaders(); $auth_token = $headers['Authorization'] ?? '';
(3) 处理PUT/DELETE请求
-
场景:RESTful API中处理非POST请求。
-
示例:
if ($_SERVER['REQUEST_METHOD'] === 'PUT') {parse_str(file_get_contents('php://input'), $put_data);$id = $put_data['id'];// 处理更新逻辑 }
5. 最佳实践总结
-
始终检查变量是否存在:
// 使用 isset() 或空合并运算符 $value = $_POST['key'] ?? 'default';
-
优先使用过滤器函数:
$email = filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL);
-
避免直接使用未过滤的数据:
// 错误示例(易受XSS攻击) echo $_GET['content']; // 正确做法 echo htmlspecialchars($_GET['content']);
-
严格限制文件上传类型和大小:
$allowed_types = ['image/jpeg', 'image/png']; if (!in_array($_FILES['file']['type'], $allowed_types)) {die("只允许上传JPEG和PNG图片"); }
四,PHP处理复选框
1. HTML复选框基础
1.1 正确命名复选框
-
关键点:使用数组形式命名(
name="字段名[]"
),以便PHP接收多个值。 -
示例:
<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动态生成示例:
$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
:$selectedHobbies = $_POST['hobbies'] ?? [];
- 若用户未勾选任何复选框,
$_POST['hobbies']
将不存在,需用空合并运算符 (??
) 避免错误。
- 若用户未勾选任何复选框,
2.2 验证与过滤数据
-
检查是否为数组:
if (!is_array($selectedHobbies)) {die("非法请求"); }
-
过滤非法值:
$allowedValues = ['reading', 'coding', 'sports', 'music']; $validHobbies = array_intersect($selectedHobbies, $allowedValues); if (count($validHobbies) === 0) {die("请至少选择一个有效兴趣"); }
3. 安全性处理
3.1 防止XSS攻击
-
转义输出:
foreach ($validHobbies as $hobby) {echo htmlspecialchars($hobby, ENT_QUOTES, 'UTF-8'); }
3.2 防止SQL注入
-
预处理语句示例(PDO):
$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()
:$selectedHobbies = isset($_POST['hobbies']) ? $_POST['hobbies'] : [];
问题3:动态生成的选项被篡改
- 场景:用户提交了不在允许列表中的值。
- 解决:通过
array_intersect()
过滤非法值(见2.2节)。
5. 完整示例
HTML表单(form.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
// 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
-
场景:单个字段存储多个选项(非关系型数据)。
-
示例:
$hobbiesJson = json_encode($validHobbies); // 存入数据库 $stmt = $pdo->prepare("UPDATE users SET hobbies = ? WHERE id = ?"); $stmt->execute([$hobbiesJson, $userId]);
6.2 回显已选中的复选框
-
场景:编辑表单时显示用户之前的选择。
-
示例:
$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. 项目结构
public/├── index.php # 表单页面 & 处理结果└── style.css # 一点简单样式
2. 代码
2.1 public/index.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"><!-- 基础安全请求头,可按需调整 --><?phpheader('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"requiredvalue="<?= 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
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}