Skip to content

$_REQUEST — 请求参数合集

概述

$_REQUEST 是 PHP 中的一个超全局变量,它默认包含了 $_GET$_POST$_COOKIE 三个数组的内容。其设计初衷是为了方便开发者统一访问请求参数,而不必区分数据来源。然而,在实际开发中,$_REQUEST 的使用被广泛认为是一种不推荐的做法,存在安全性和可维护性方面的问题。

前置知识

在阅读本节之前,你需要了解:

  • $_GET$_POST$_COOKIE 的基本用法
  • HTTP 请求方法(GET/POST)的区别
  • PHP 配置指令 request_ordervariables_order

基础概念

$_REQUEST 的组成

$_REQUEST 默认包含以下三个超全局变量的数据:

$_REQUEST
├── $_GET    — URL 查询字符串参数
├── $_POST   — POST 请求体参数
└── $_COOKIE — HTTP Cookie 数据

数据来源优先级

当三个来源中存在同名的键时,优先级由 php.ini 中的 request_order 指令决定:

ini
; 默认值
request_order = "GP"

; GP 含义:
; G = $_GET
; P = $_POST
; C = $_COOKIE

历史变化

在 PHP 4.3.0 到 5.3.x 中,request_order 默认值是 "GP"。在 PHP 5.4.0+ 中,默认值仍然是 "GP",这意味着 $_REQUEST 默认不包含 $_COOKIE。如果需要包含 Cookie 数据,需要手动修改为 "GPC"

语法与代码

$_REQUEST 基本使用

php
<?php
declare(strict_types=1);

// URL: /page.php?id=5
// Cookie: id=10
// POST Body: id=3

// $_GET['id']    = 5
// $_POST['id']   = 3
// $_COOKIE['id'] = 10

// $_REQUEST 取决于 request_order 配置
// request_order = "GP" 时:
echo $_REQUEST['id']; // 可能是 3 或 5(取决于优先级)

// request_order = "GPC" 时:
echo $_REQUEST['id']; // 可能是 5(GET 优先)、3(POST 优先)或 10(COOKIE 优先)

$_REQUEST 与 $_GET/$_POST 的区别

php
<?php
declare(strict_types=1);

// 场景一:URL 中包含 id 参数
// URL: /update.php?id=100
// POST Body: id=200

// 使用 $_GET 明确获取 URL 参数
$getId = (int)($_GET['id'] ?? 0); // 100

// 使用 $_POST 明确获取 POST 参数
$postId = (int)($_POST['id'] ?? 0); // 200

// 使用 $_REQUEST —— 你不知道 id 来自哪里
$requestId = (int)($_REQUEST['id'] ?? 0); // 可能是 100 或 200

echo "GET id: {$getId}, POST id: {$postId}, REQUEST id: {$requestId}";

request_order 配置的影响

php
<?php
declare(strict_types=1);

/**
 * 检查当前 request_order 配置
 */
function checkRequestOrder(): string
{
    return ini_get('request_order'); // 返回如 "GP" 或 "GPC"
}

/**
 * 安全获取请求参数 —— 明确指定来源
 */
function getParam(string $key, mixed $default = null, string $source = 'request'): mixed
{
    return match ($source) {
        'get'    => $_GET[$key] ?? $default,
        'post'   => $_POST[$key] ?? $default,
        'cookie' => $_COOKIE[$key] ?? $default,
        default  => $_REQUEST[$key] ?? $default,
    };
}

echo '当前 request_order: ' . checkRequestOrder();

// 明确从 GET 获取
$page = getParam('page', 1, 'get');
// 明确从 POST 获取
$name = getParam('name', '', 'post');

详细说明

为什么不推荐使用 $_REQUEST

$_REQUEST 将 Cookie 数据与请求参数混在一起。攻击者可以通过伪造 Cookie 来覆盖预期的 GET/POST 参数:

php
<?php
declare(strict_types=1);

// 假设应用使用 $_REQUEST['user_id'] 获取当前用户 ID
// 攻击者在 Cookie 中设置 user_id=1(管理员 ID)
// 即使 GET/POST 中没有 user_id 参数,攻击者的 Cookie 也会生效

// 不安全
$userId = (int)($_REQUEST['user_id'] ?? 0);

// 安全:明确从会话中获取用户身份
$userId = (int)($_SESSION['user_id'] ?? 0);

2. 参数来源不明确

使用 $_REQUEST 时,你无法知道一个参数到底是来自 URL、表单还是 Cookie。这会导致:

  • 调试困难:难以追踪参数的真正来源
  • 逻辑冲突:同名参数在不同来源中可能有不同含义
  • 安全漏洞:无法对特定来源实施不同的验证策略

3. 优先级导致的意外行为

php
<?php
declare(strict_types=1);

// 场景:重置密码功能
// GET: /reset.php?token=abc123  (邮件中的重置链接)
// POST: token=abc123              (用户提交新密码表单)

// 如果 request_order = "GP",GET 的 token 会覆盖 POST 的 token
// 在某些框架中,这可能导致验证逻辑混乱

// 推荐:明确区分来源
$getToken  = $_GET['token'] ?? '';
$postToken = $_POST['token'] ?? '';

$_REQUEST 的适用场景(有限)

$_REQUEST 在极少数场景下是可接受的:

  1. 快速原型开发:开发阶段快速获取参数
  2. 统一的参数读取接口:当 GET 和 POST 参数的含义完全相同时
  3. 简单的搜索/过滤表单:表单可能通过 GET 或 POST 提交
php
<?php
declare(strict_types=1);

// 可接受的使用场景:搜索页面
// 搜索表单可能通过 GET(URL 分享)或 POST 提交
$searchTerm = filter_input(INPUT_REQUEST, 'q', FILTER_SANITIZE_SPECIAL_CHARS);
$page       = filter_input(INPUT_REQUEST, 'page', FILTER_VALIDATE_INT) ?: 1;

if ($searchTerm !== null && $searchTerm !== '') {
    echo "搜索: {$searchTerm}, 第 {$page} 页";
}

与 filter_input 的配合

php
<?php
declare(strict_types=1);

// filter_input 支持 INPUT_REQUEST 常量
// 等价于从 $_REQUEST 中获取并过滤

// 获取并验证
$id = filter_input(INPUT_REQUEST, 'id', FILTER_VALIDATE_INT);
if ($id === false || $id === null) {
    echo "无效的 ID";
}

// 批量获取
$inputs = filter_input_array(INPUT_REQUEST, [
    'action' => FILTER_SANITIZE_SPECIAL_CHARS,
    'id'     => FILTER_VALIDATE_INT,
    'token'  => FILTER_SANITIZE_FULL_SPECIAL_CHARS,
]);

实战示例

模拟 $_REQUEST 的安全替代方案

php
<?php
declare(strict_types=1);

/**
 * 安全的请求参数管理器
 * 明确区分不同来源的参数
 */
class SafeRequest
{
    private array $getParams;
    private array $postParams;
    private array $cookieParams;

    public function __construct(
        array $get = null,
        array $post = null,
        array $cookie = null
    ) {
        $this->getParams    = $get ?? $_GET;
        $this->postParams   = $post ?? $_POST;
        $this->cookieParams = $cookie ?? $_COOKIE;
    }

    /**
     * 从 GET 参数获取
     */
    public function query(string $key, mixed $default = null): mixed
    {
        return $this->getParams[$key] ?? $default;
    }

    /**
     * 从 POST 参数获取
     */
    public function post(string $key, mixed $default = null): mixed
    {
        return $this->postParams[$key] ?? $default;
    }

    /**
     * 从 Cookie 获取
     */
    public function cookie(string $key, mixed $default = null): mixed
    {
        return $this->cookieParams[$key] ?? $default;
    }

    /**
     * 优先从 POST 获取,其次 GET(明确优先级,不使用 $_REQUEST)
     */
    public function input(string $key, mixed $default = null): mixed
    {
        return $this->postParams[$key]
            ?? $this->getParams[$key]
            ?? $default;
    }

    /**
     * 仅当 POST 和 GET 参数一致时才返回值(额外安全检查)
     */
    public function requireConsistent(string $key, mixed $default = null): mixed
    {
        $getVal  = $this->getParams[$key] ?? null;
        $postVal = $this->postParams[$key] ?? null;

        if ($getVal !== null && $postVal !== null && $getVal !== $postVal) {
            trigger_error(
                "参数 {$key} 在 GET 和 POST 中值不一致",
                E_USER_WARNING
            );
            return $default;
        }

        return $postVal ?? $getVal ?? $default;
    }
}

// 使用示例
$request = new SafeRequest();

// 明确从 GET 获取
$page = $request->query('page', 1);

// 明确从 POST 获取
$email = $request->post('email');

// POST 优先,GET 备用(语义明确)
$keyword = $request->input('keyword', '');

注意事项

1. request_order 配置的重要性

不同服务器环境可能有不同的 request_order 配置,这会导致 $_REQUEST 的行为不一致。部署到不同环境时可能出现难以排查的 Bug。

2. PHP 5.4+ 的变化

PHP 5.4.0 修改了 request_order 的默认值,移除了默认包含的 Cookie。如果你从旧版本升级,需要检查代码是否依赖 $_REQUEST 中的 Cookie 数据。

3. 性能影响

$_REQUEST 是由 GET、POST、Cookie 数据合并而成的副本,在处理大量参数时会有额外的内存开销。

4. 框架的替代方案

主流 PHP 框架(Laravel、Symfony 等)都提供了自己的请求对象来替代 $_REQUEST

php
<?php
// Laravel 示例
$id = $request->input('id');        // POST/GET 合并获取
$id = $request->query('id');        // 仅 GET
$id = $request->post('id');        // 仅 POST
$id = $request->cookie('name');    // 仅 Cookie

最佳实践

  1. 不使用 $_REQUEST:始终明确使用 $_GET$_POST 获取参数
  2. 明确数据来源:知道每个参数来自 GET 还是 POST
  3. 使用 filter_input:如果需要过滤,使用 INPUT_GETINPUT_POST
  4. 封装请求类:在项目中封装统一的请求访问层
  5. 检查服务器配置:确保 request_order 符合预期
  6. 代码审查:禁止团队成员使用 $_REQUEST
php
<?php
declare(strict_types=1);

// 反面示例:不推荐
function processForm(): void
{
    $name = $_REQUEST['name'] ?? '';    // 来源不明
    $id   = $_REQUEST['id'] ?? 0;      // 来源不明
}

// 正面示例:推荐
function processForm(): void
{
    $name = filter_input(INPUT_POST, 'name', FILTER_SANITIZE_SPECIAL_CHARS) ?? '';
    $id   = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT) ?? 0;
}

下一节

下一节将详细介绍 $_SESSION 超全局变量,了解 PHP 会话机制的原理和使用方法。

进阶用法

调试与测试技巧

php
<?php
declare(strict_types=1);

// 单元测试辅助函数
function createTestResource(): mixed
{
    return match (true) {
        default => new stdClass(),
    };
}

// 调试输出函数
function debugOutput(mixed , string  = ''): void
{
     =  ? ": " : '';
     .= print_r(, true);
    fwrite(STDERR,  . "\n");
}

// 性能基准测试
function benchmark(callable , int  = 1000): float
{
     = hrtime(true);
    for ($i = 0; $i < $iterations; $i++) {
        $fn();
    }
    return (hrtime(true) - $start) / 1e9;
}

日志记录实践

php
<?php
declare(strict_types=1);

/**
 * 简易日志记录器
 */
class SimpleLogger
{
    private string $logFile;
    private string $level = 'INFO';

    public function __construct(string $logFile)
    {
        $this->logFile = $logFile;
    }

    public function info(string $message, array $context = []): void
    {
        $this->log('INFO', $message, $context);
    }

    public function warning(string $message, array $context = []): void
    {
        $this->log('WARNING', $message, $context);
    }

    public function error(string $message, array $context = []): void
    {
        $this->log('ERROR', $message, $context);
    }

    private function log(string $level, string $message, array $context): void
    {
        $timestamp = date('Y-m-d H:i:s');
        $contextStr = $context ? ' ' . json_encode($context, JSON_UNESCAPED_UNICODE) : '';
        $line = "[{$timestamp}] [{$level}] {$message}{$contextStr}\n";
        file_put_contents($this->logFile, $line, FILE_APPEND | LOCK_EX);
    }
}

配置与环境检测

php
<?php
declare(strict_types=1);

// 环境检测工具
class EnvironmentChecker
{
    public static function checkRequirements(array $requirements): array
    {
        $results = [];
        foreach ($requirements as $name => $check) {
            $results[$name] = is_callable($check) ? $check() : false;
        }
        return $results;
    }

    public static function getSystemInfo(): array
    {
        return [
            'php_version' => PHP_VERSION,
            'os' => PHP_OS,
            'sapi' => PHP_SAPI,
            'memory_limit' => ini_get('memory_limit'),
            'max_execution_time' => ini_get('max_execution_time'),
            'loaded_extensions' => get_loaded_extensions(),
        ];
    }
}

常见问题排查

问题可能原因解决方案
连接超时网络问题/配置错误检查配置,增加超时时间
权限不足文件/目录权限使用 chmod/chown 修正
性能下降索引缺失/数据量大添加索引,优化查询
数据不一致并发冲突/事务残留使用锁机制和事务
内存溢出大数据集/未释放资源增大内存限制,分批处理

故障排除步骤

  1. 检查错误日志和异常信息
  2. 确认配置和环境是否正确
  3. 使用调试工具逐步排查
  4. 参考官方文档查找已知问题

版本兼容性说明

功能最低版本说明
基础功能PHP 8.1本文档基准版本
只读属性PHP 8.1public readonly 修饰符
枚举类型PHP 8.1enum 类型和 match 表达式
FiberPHP 8.1协程/轻量级并发
命名参数PHP 8.0foo(arg_name: value)
联合类型PHP 8.0`int
Null 安全运算符PHP 8.0$obj?->method()
析构器 promotionPHP 8.0__construct(public $x)
php
<?php
declare(strict_types=1);

// 版本兼容性检测
function ensureVersion(string $minVersion): void
{
    if (version_compare(PHP_VERSION, $minVersion, '<')) {
        throw new RuntimeException(
            sprintf('需要 PHP %s+, 当前版本: %s', $minVersion, PHP_VERSION)
        );
    }
}

ensureVersion('8.1.0');

参考链接