PHP _GET 与 $_POST
PHP 超全局变量(Superglobals)是内置的全局变量,它们在脚本的全部作用域中始终可用。这些特殊的变量存储了关于服务器、客户端、当前脚本等众多信息。本章将重点介绍两个最常用的超全局变量:$_GET 和 $_POST。它们是处理 HTML 表单提交数据的基石,允许 PHP 脚本读取来自客户端发送的数据,从而实现动态和交互式的 Web 应用程序。
1. 什么是超全局变量
超全局变量是 PHP 提供的一组数组变量,你可以从脚本的任何地方访问它们,无论当前处于什么作用域。这意味着它们可以在函数、方法以及全局脚本区域内直接使用,而不需要像普通的全局变量那样使用 global 关键字显式声明。每一个超全局变量都有其特定的用途,用于存储与请求、会话、服务器或环境相关的不同类型的数据。
举个例子,假设你在函数外部定义了一个普通变量。要在函数内部访问它,你通常需要将其作为参数传递,或者声明为 global。超全局变量绕过了这个限制,使得特定的核心数据可以被全局无障碍访问。
2. $_GET 超全局变量
$_GET 超全局变量是一个关联数组,包含了通过 URL 参数传递给当前脚本的所有变量。当用户点击链接或使用 HTTP GET 方法提交表单时,数据会被附加到 URL 的末尾,形成所谓的“查询字符串”(Query String)。$_GET 数组会自动解析这个查询字符串,并将各个参数转换为键值对(Key-Value pairs)供你使用。
2.1 $_GET 是如何工作的
当数据通过 GET 方法发送时,它会出现在 URL 中的问号 (?) 之后。每个参数都是一个键值对,参数之间用连字符 (&) 分隔。例如:www.example.com/script.php?name=John&age=30。在这个 URL 中,name 和 age 是键(Key),而 John 和 30 是它们对应的值(Value)。
PHP 会自动填充 $_GET 数组:
$_GET['name']的值将是'John'$_GET['age']的值将是'30'
2.2 $_GET 的实际应用案例
案例 1:通过链接传递数据
想象一个简单的产品目录,用户点击某个产品来查看其详细信息。产品 ID 可以通过 URL 传递。
<?php
// product_list.php (产品列表页)
echo '<a href="product_details.php?id=101&category=electronics">查看产品 101</a><br>';
echo '<a href="product_details.php?id=102&category=books">查看产品 102</a>';
?>然后,在 product_details.php 中就可以获取这些数据:
<?php
// product_details.php (产品详情页)
if (isset($_GET['id'])) {
$productId = $_GET['id'];
echo "产品 ID: " . htmlspecialchars($productId) . "<br>";
} else {
echo "未指定产品 ID。<br>";
}
if (isset($_GET['category'])) {
$productCategory = $_GET['category'];
echo "分类: " . htmlspecialchars($productCategory) . "<br>";
} else {
echo "未指定分类。<br>";
}
?>在这个例子中,htmlspecialchars() 函数被用来防止跨站脚本攻击 (XSS),它将特殊字符转换为 HTML 实体。在输出用户提供的数据时,这是一项至关重要的安全措施。
案例 2:简单的搜索功能
搜索栏通常使用 GET 方法来发送搜索词。
<form action="search_results.php" method="GET">
<label for="query">搜索:</label>
<input type="text" id="query" name="search_query">
<input type="submit" value="搜索">
</form>当用户输入 "PHP basics" 并提交表单时,URL 可能会变成 search_results.php?search_query=PHP+basics。
<?php
// search_results.php (搜索结果页)
if (isset($_GET['search_query'])) {
$searchQuery = $_GET['search_query'];
echo "你的搜索词是: " . htmlspecialchars($searchQuery) . "<br>";
// 在真实应用中,你会使用这个查询词从数据库中获取结果。
} else {
echo "请输入搜索词。";
}
?>URL 中的 + 号代表一个空格,$_GET 会自动将其解码为空格字符。
2.3 $_GET 的使用场景与注意事项
- 书签与分享: 带有 GET 参数的 URL 可以很容易地被加入书签和分享,因为页面的状态(如搜索结果、过滤后的列表)直接反映在 URL 中。
- 无状态操作: GET 请求通常用于从服务器获取数据,这类请求不应该修改服务器端的状态。
- 局限性:URL 长度限制: 大多数浏览器和服务器对 URL 的长度有限制(通常为 2048 个字符),这使得 GET 不适合传递大量数据。安全性: 通过 GET 发送的数据在 URL、浏览器历史记录和服务器日志中都是可见的。因此,它绝不能用于发送密码或个人隐私等敏感信息。幂等性 (Idempotence): GET 请求应当是幂等的,意思是执行多次相同的请求,产生的结果应该和执行一次是一样的(它不应对服务器产生副作用)。
3. $_POST 超全局变量
$_POST 超全局变量是一个关联数组,包含了通过 HTTP POST 方法传递给当前脚本的变量。与 GET 不同,通过 POST 发送的数据不会附加在 URL 的末尾。相反,它被包含在 HTTP 请求的请求体(Body)中。这使得 $_POST 非常适合安全地发送大量数据和敏感信息,因为数据不会直接暴露。
3.1 $_POST 是如何工作的
当使用 POST 方法提交表单时,浏览器会将表单数据打包在 HTTP 请求体中发送。PHP 会自动解析这个请求体,并将表单字段的 name 属性作为键,将用户输入的内容作为值,填充到 $_POST 数组中。
3.2 $_POST 的实际应用案例
案例 1:用户注册表单
注册表单通常使用 POST 来发送用户数据,包括密码等敏感细节。
<form action="process_registration.php" method="POST">
<label for="username">用户名:</label>
<input type="text" id="username" name="username" required><br><br>
<label for="password">密码:</label>
<input type="password" id="password" name="password" required><br><br>
<label for="email">邮箱:</label>
<input type="email" id="email" name="email" required><br><br>
<input type="submit" value="注册">
</form>process_registration.php 脚本将使用 $_POST 来访问这些数据:
<?php
// process_registration.php (处理注册请求)
if ($_SERVER["REQUEST_METHOD"] == "POST") { // 检查表单是否通过 POST 提交
$username = htmlspecialchars($_POST['username']);
$email = htmlspecialchars($_POST['email']);
$password = $_POST['password']; // 密码在存储前必须进行哈希加密!
echo "注册成功,欢迎: " . $username . "<br>";
echo "邮箱: " . $email . "<br>";
// 在真实应用中,你需要对密码进行哈希处理并将用户数据存入数据库。
// 例如: $hashedPassword = password_hash($password, PASSWORD_DEFAULT);
// 然后将 $hashedPassword 存入数据库
} else {
echo "访问被拒绝。此页面只能通过表单提交访问。";
}
?>使用 $_SERVER["REQUEST_METHOD"] == "POST" 进行检查是一种常见做法,它确保脚本只有在接收到 POST 请求时才执行处理逻辑。这可以防止用户直接在浏览器地址栏访问处理脚本。注意:对于密码,必须在存储前对其进行哈希处理(哈希加密),这在安全最佳实践中至关重要。这里为了简单起见只是演示获取过程,在真实场景中绝对不能直接输出或明文存储密码。
案例 2:提交评论
评论表单通常也使用 POST 来发送评论内容和用户详情。
<form action="add_comment.php" method="POST">
<label for="author">你的名字:</label>
<input type="text" id="author" name="author" required><br><br>
<label for="comment_text">评论内容:</label><br>
<textarea id="comment_text" name="comment_text" rows="5" cols="40" required></textarea><br><br>
<input type="hidden" name="article_id" value="123"> <input type="submit" value="发布评论">
</form>add_comment.php 脚本:
<?php
// add_comment.php (处理评论发布)
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$author = htmlspecialchars($_POST['author']);
$commentText = htmlspecialchars($_POST['comment_text']);
$articleId = htmlspecialchars($_POST['article_id']); // 从隐藏字段获取
echo $author . " 在文章 ID " . $articleId . " 下发表了评论:<br>";
echo "<blockquote>" . $commentText . "</blockquote>";
// 在真实应用中,这条评论会被存入数据库。
} else {
echo "无效的请求方法。";
}
?>隐藏的输入字段(<input type="hidden">)经常被用来发送那些不需要显示给用户看、但对后端处理必不可少的数据,比如这里的 article_id。
3.3 $_POST 的使用场景与注意事项
- 敏感数据:
POST是发送敏感信息(如密码、信用卡号)的首选,因为数据不会暴露在 URL 中。 - 大数据量: 通过
POST发送的数据量通常没有实际限制(尽管服务器配置可能会施加限制)。 - 状态变更操作:
POST请求通常用于修改服务器上数据的操作,例如创建新记录、更新现有记录或删除数据。 - 非幂等性:
POST请求通常不是幂等的。多次提交同一个POST请求可能会导致多次创建或更新(例如,连续点击两次提交按钮可能会创建两条完全相同的记录)。 - 浏览器历史/书签:
POST数据不会被存储在浏览器的历史记录或书签中,这使得它更适合用于敏感或事务性的操作。
4. Web 开发安全基础
在使用 $_GET 和 $_POST 时,安全是重中之重。绝对不能信任任何来自客户端的数据。恶意用户可能会伪造 URL 或表单提交来注入有害代码。
4.1 防御 XSS 跨站脚本攻击
正如前面案例所示,htmlspecialchars() 是防止 XSS 攻击的核心函数。它会将特殊的 HTML 字符(如 <、>、&、"、')转换为对应的 HTML 实体。如果你直接输出用户提供的数据而不进行转义,攻击者就可以注入 <script> 标签或其他 HTML 元素,从而在其他用户的浏览器中执行恶意代码。
// 不安全:直接输出用户输入
echo "你好," . $_GET['name']; // 如果 $_GET['name'] 是 "<script>alert('XSS!')</script>",这将非常危险。
// 安全:在输出前转义用户输入
echo "你好," . htmlspecialchars($_GET['name']); // 会安全地输出 "你好,<script>alert('XSS!')</script>"4.2 防御 SQL 注入
如果你将 $_GET 或 $_POST 数据直接用于 SQL 查询(例如,根据表单提交的用户名从数据库检索详情),你必须使用带有参数化查询的预处理语句。直接将用户输入拼接进 SQL 字符串极其危险,会导致 SQL 注入攻击。虽然这个话题超出了本章的范围,但在你准备连接数据库时,这是一个必须牢记的关键概念。
5. 综合实战:构建简单的问候程序
让我们结合 $_GET 和 $_POST 的知识,构建一个简单的问候应用。我们将制作一个表单,既允许通过 POST 提交名字,也允许直接通过 GET 链接传递名字。
首先,创建一个 HTML 文件 greeting_form.html:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>问候表单</title>
</head>
<body>
<h2>打个招呼吧!</h2>
<form action="greet.php" method="POST">
<label for="name_post">输入你的名字 (POST 方式):</label><br>
<input type="text" id="name_post" name="user_name_post" required><br><br>
<input type="submit" value="使用 POST 问候">
</form>
<hr>
<h3>或者通过链接向别人问候:</h3>
<p>
<a href="greet.php?user_name_get=Alice">问候 Alice</a><br>
<a href="greet.php?user_name_get=Bob">问候 Bob</a><br>
<a href="greet.php?user_name_get=Charlie">问候 Charlie</a>
</p>
<hr>
<p>你也可以直接在 URL 中输入名字:</p>
<p><code>http://localhost/greet.php?user_name_get=你的名字</code></p>
</body>
</html>接下来,创建 PHP 脚本 greet.php:
<?php
// greet.php
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>问候页面</title>
</head>
<body>
<h1>你好呀!</h1>
<?php
$greetingName = '';
// 检查是否通过 POST 发送了名字
if (isset($_POST['user_name_post']) && !empty($_POST['user_name_post'])) {
$greetingName = htmlspecialchars($_POST['user_name_post']);
echo "<p>你好," . $greetingName . "! (来自 POST 数据)</p>";
}
// 检查是否通过 GET 发送了名字
else if (isset($_GET['user_name_get']) && !empty($_GET['user_name_get'])) {
$greetingName = htmlspecialchars($_GET['user_name_get']);
echo "<p>你好," . $greetingName . "! (来自 GET 数据)</p>";
}
// 如果没有提供名字
else {
echo "<p>哈喽!请提供你的名字以便我向你问好。</p>";
echo "<p>你可以使用上方的表单,或者使用类似 <code>greet.php?user_name_get=你的名字</code> 的 URL 参数。</p>";
}
?>
<p><a href="greeting_form.html">返回表单页面</a></p>
</body>
</html>要运行这个示例,请将这两个文件保存在你的 Web 服务器根目录(例如 XAMPP 的 htdocs 文件夹中),然后在浏览器中访问 http://localhost/greeting_form.html 进行测试。这个例子清晰地展示了如何利用 isset() 和 !empty() 在使用变量前进行检查,以及 htmlspecialchars() 的重要性。