PHP输入流 php://input 原理与应用详解

江岸
江岸
发布时间:2026-10-15 | 2人浏览过
原创

php://input是PHP中用于读取未处理的原始POST数据的流接口,无需特殊ini配置且比$HTTP_RAW_POST_DATA更省内存。本文通过测试不同Content-Type(如表单、XML)和HTTP方法(POST/GET)下的数据流向,深入解析其与$_POST的全局变量映射关系,帮助开发者理解为何在XML-RPC等场景下必须使用php://input而非$_POST。

php://input 的核心定义

根据PHP官方文档,php://input允许你读取未经处理的原始POST数据。它是$HTTP_RAW_POST_DATA的一个内存占用更小的替代方案,且不需要任何特殊的php.ini指令。其核心限制在于:当表单的 enctype="multipart/form-data" 时,该流不可用。

为了准确理解这一机制,我们需要从以下三个维度进行拆解:

  1. 它如何读取POST数据以及何时读取
  2. 它为何不能用于 multipart/form-data
  3. 它与 $HTTP_RAW_POST_DATA 及 $_POST 的真实差异

操作一:对比 application/x-www-form-urlencoded 类型

在常规的表单提交中,Content-Type通常为 application/x-www-form-urlencoded。此时,HTTP请求体的数据会被PHP自动解析并填充到 $_POST 数组中。让我们通过一个测试来观察 php://input 此时的表现。

第1步:准备服务端接收脚本

创建文件 phpinput_server.php,用于打印接收到的原始数据和解构后的POST数据:

<?php
// phpinput_server.php
$raw_post_data = file_get_contents('php://input', 'r');
echo "-------$_POST------------------\n";
echo var_dump($_POST) . "\n";
echo "-------php://input-------------\n";
echo $raw_post_data . "\n";
?>

第2步:构造并提交模拟POST请求

编写客户端脚本 phpinput_post.php,模拟发送键值对数据:

<?php
// phpinput_post.php
$http_entity_body = 'n=' . urldecode('perfgeeks') . '&p=' . urldecode('7788');
$http_entity_type = 'application/x-www-form-urlencoded';
$http_entity_length = strlen($http_entity_body);
$host = '192.168.0.6'; // 替换为你的服务器IP
$port = 80;
$path = '/phpinput_server.php';

$fp = fsockopen($host, $port, $error_no, $error_desc, 30);
if ($fp) {
    fputs($fp, "POST {$path} HTTP/1.1\r\n");
    fputs($fp, "Host: {$host}\r\n");
    fputs($fp, "Content-Type: {$http_entity_type}\r\n");
    fputs($fp, "Content-Length: {$http_entity_length}\r\n");
    fputs($fp, "Connection: close\r\n\r\n");
    fputs($fp, $http_entity_body . "\r\n\r\n");

    while (!feof($fp)) {
        $d .= fgets($fp, 4096);
    }
    fclose($fp);
    echo $d;
}
?>

第3步:观察运行结果

执行脚本后,服务端输出显示:$_POST 解析出了两个元素,而 php://input 输出了原始的查询字符串格式 n=perfgeeks&p=7788。此时,两者内容实质一致,只是 $_POST 经过了URL解码和结构化处理。这印证了当Content-Type为 application/x-www-form-urlencoded 时,PHP会将Request Body填入 $_POST,且 php://input 与该Body完全一致。

操作二:测试非标准 Content-Type (如 XML)

如果请求体中包含的是XML数据,例如XML-RPC调用,情况将发生变化。此时Content-Type通常指定为 text/xml 或类似类型,PHP不会将其解析为 $_POST 数组。

第1步:构造XML请求脚本

创建 phpinput_xmlrpc.php,发送一段XML内容:

<?php
// phpinput_xmlrpc.php
$http_entity_body = "<?xml version=\"1.0\"?><methodcall><name>jt_userinfo</name></methodcall>";
$http_entity_type = 'text/xml';
// ... 其余头部设置同前 ...
// ... 连接并发送代码 ... 
?>

第2步:验证数据流向

执行上述脚本,服务端 php://input 成功输出了完整的XML字符串。然而,$_POST 打印为空数组 array(0) { }。

结论:只有当Content-Type被识别为 application/x-www-form-urlencoded 或 multipart/form-data 时,PHP才会自动填充 $_POST。对于其他类型(如XML),$_POST 为空,但 php://input 依然能完整读取HTTP实体体(Entity Body)中的数据。这也是为什么XML-RPC服务端需要直接用 file_get_contents('php://input') 获取数据的原因。

操作三:验证 GET 方法与 multipart/form-data 限制

GET 方法无法通过 php://input 读取

当使用GET方法提交数据时,参数位于URL的Query String部分(即PATH字段之后),HTTP请求体(Entity Body)通常为空。因此,即使强行在Header中指定Content-Length,php://input 也无法获取到GET参数,因为数据根本不在Body里。$_GET 则负责解析Query String。

miltipart/form-data 导致 php://input 为空

在文件上传场景中,表单必须设置 enctype="multipart/form-data"。这种格式使用Boundary分隔多个部分,结构复杂。PHP内核会直接将这部分数据解析并分配给 $_FILES 和 $_POST,而不会写入 php://input 流。

测试发现,即使HTTP请求中确实存在包含文件的Body数据,php://input 的输出也为空。这是由PHP的设计决定的:在处理文件上传类型时,关闭对 php://input 的读取。

php://input 与 $HTTP_RAW_POST_DATA 对比

$HTTP_RAW_POST_DATA 是旧版本PHP中用于保存原始POST数据的变量。它的主要区别在于:

  1. 启用条件:必须在php.ini中设置 always_populate_raw_post_data = On 才能生效,否则默认为FALSE。
  2. 资源消耗:它会将整个POST数据加载到内存变量中,相比流的按需读取,内存压力更大。
  3. 功能一致性:只要启用,$HTTP_RAW_POST_DATA 的内容与 php://input 读取到的内容是相同的。

鉴于 php://input 默认可用且更高效,官方建议优先使用 php://input。

简要总结

掌握 php://input 的关键在于理解HTTP请求的结构与PHP的解析逻辑:

  • 数据来源:它只读取HTTP Request Body,不读取URL参数(GET)。
  • 适用场景:适用于所有非 multipart/form-data 的POST请求,特别是JSON API、XML-RPC或非标准表单提交。
  • 局限:无法读取文件上传(multipart/form-data)的数据;若Body为空,则返回空字符串。
以上就是php://input的详细原理与应用测试,更多关于PHP底层机制的资料请关注本站其它相关文章!
相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表