Menu

PHP $_GET 和 $_POST:读取 URL 参数和表单数据

$_GET 保存 URL 的查询字符串(?page=2 对应 $_GET['page']),$_POST 保存用 method="post" 提交的表单字段。本文讲解如何安全地读取两者、用 http_build_query 构建链接、检查 $_SERVER['REQUEST_METHOD'],以及如何在 GET 和 POST 之间选择。

本页包含可运行的编辑器。编辑、运行,立即查看输出。

$_GET 是 URL 查询字符串组成的数组,所以 article.php?id=42&lang=en 会得到 $_GET['id'] === '42' 和 $_GET['lang'] === 'en'。$_POST 是用 method="post" 提交的表单字段组成的数组。读取两者时都要给出默认值,$_GET['id'] ?? null,因为只要没有传这个参数,对应的键就不存在。

运行这段代码,然后点击链接:每次点击都会重新运行脚本,$_GET 中填入的是链接里的查询字符串。

$_GET['lang'] 是 URL 里写的任何内容,而不仅仅是你输出的那三个链接。任何人都可以在地址栏里输入 ?lang=<script>,所以这段代码是在 $languages 里查找这个值,而不是直接输出它,并且在唯一输出请求数据的地方做了转义。

从查询字符串读取数字

$_GET 中的每个值都是字符串。对于页码,可以用 filter_var() 一步完成验证和转换,并在值缺失、不是数字或超出范围时回退到默认值。点击几个页码,再试试 Page 999 和 Page abc 这两个链接。

对于 "3",filter_var() 返回整数 3;对于其他任何字符串,返回 default 选项的值,所以 $page 始终是一个可用的 int。其他验证器见 filter_var 页面。

用 $_POST 读取表单数据

method="post" 的表单会在请求体中发送字段,PHP 按每个输入框的 name 把它们放进 $_POST。$_SERVER['REQUEST_METHOD'] 告诉你当前请求是第一次访问(GET)还是提交(POST)。填写表单并点 Send,就能看到这两种情况。

空的文本框仍然会被发送,值为 "",所以提交之后,即使访问者什么都没输入,$_POST['title'] 也是存在的。在服务器上 REQUEST_METHOD 总是有值;?? 'GET' 只是为了在命令行用 php 运行文件时不出警告。验证并重新显示表单的完整模式见表单页面。

使用 method="get" 的搜索表单

如果表单的结果应该是一个可以分享的 URL,比如搜索框,就用 GET。浏览器会把字段转换成查询字符串(?q=sort&level=beginner),脚本从 $_GET 中读取它们,和链接完全一样。搜索一下 array 或 string。

用 http_build_query 构建链接

手写 "?q=$q&page=2",一旦 $q 中包含空格、& 或 # 就会出错。http_build_query() 会帮你对每个值编码;当结果放进 href 时还要经过 htmlspecialchars(),因为在 HTML 中 & 应该写成 &amp;。

PHP 正是用带两个参数的 parse_str() 从查询字符串构建 $_GET 的,所以它很适合用来测试。一定要传第二个参数;旧的单参数写法会直接创建变量,已在 PHP 8 中移除。

$_REQUEST 以及为什么要避免使用它

$_REQUEST 是把 $_GET 和 $_POST 合并成的一个数组。PHP 自带的 php.ini 文件设置了 request_order = "GP",所以两者包含相同的键时,POST 的值优先;如果没有这个设置,Cookie 也会被合并进来:

<?php
// POST /save.php?id=1 with a form field id=2
echo $_GET['id'];      // "1"
echo $_POST['id'];     // "2"
echo $_REQUEST['id'];  // "2"

它看起来很方便,但隐藏了值的来源。一个根据 $_REQUEST['id'] 删除记录的处理程序,用一个普通链接就能触发,而这正是只允许 POST 的操作想要防止的。应该按名字读取 $_GET 或 $_POST。

GET 和 POST:该用哪一个

GETPOST
数据放在哪里在 URL 中:?q=php&page=2在请求体中
用什么读取$_GET$_POST
书签、分享、后退按钮可以不可以(浏览器会询问是否重新发送)
长度受 URL 长度限制(几千个字符以内比较安全)受 post_max_size 限制(默认 8M)
文件上传不支持支持,需要 enctype="multipart/form-data"(读取 $_FILES)
适用场景搜索、筛选、排序、分页登录、注册、保存、删除、支付

最重要的一条规则:GET 请求不能修改任何东西。 浏览器会预取链接,爬虫会跟随链接,人们会分享链接,所以一个 ?delete=5 链接迟早会被某个本不打算删除任何东西的程序访问。两种方法本身都不加密:HTTPS 同时保护两者,POST 只是让数据不出现在 URL、浏览器历史记录和服务器日志中。

常见问题

PHP 中怎么获取 URL 里的参数?

从 $_GET 读取并给出默认值:对于 page.php?id=42,$id = $_GET['id'] ?? null; 得到字符串 "42"。所有值都是字符串(id[]= 时是数组),所以要转换并验证,例如用 filter_var($id, FILTER_VALIDATE_INT)。

$_GET 和 $_POST 有什么区别?

$_GET 来自 URL 的查询字符串,所以它是可见的、可以收藏为书签,长度也有限制;适合搜索、筛选和分页。$_POST 来自用 method="post" 提交的表单的请求体;凡是会修改数据的操作,比如保存、购买或登录,都应该用它。

PHP 中怎么判断 GET 参数是否存在?

用 isset($_GET['q']),键不存在或值为 null 时它返回 false。如果还想把空值(?q=)也当作不存在,就比较去掉空白后的值:trim($_GET['q'] ?? '') !== ''。

PHP 中应该使用 $_REQUEST 吗?

通常不应该。$_REQUEST 合并了 $_GET 和 $_POST(根据 request_order 的设置,可能还包括 Cookie),所以你无法分辨一个值来自哪里,而且一个链接就能提供你本以为来自表单的值。应该明确地读取 $_GET 或 $_POST。

PHP 中怎么判断请求是 GET 还是 POST?

比较 $_SERVER['REQUEST_METHOD']:if ($_SERVER['REQUEST_METHOD'] === 'POST') { ... }。这比 isset($_POST['submit']) 更可靠,因为当按钮没有 name,或者表单由 JavaScript 或其他脚本发送而不带这个字段时,后者就会失效。

Coddy 编程语言插图

和 Coddy 一起学编程

开始