php://input 是 PHP 提供的唯讀流,用於讀取 未經解析的原始 HTTP 請求體。它在 XML-RPC、REST API、Webhook 接收、非標準 Content-Type 處理等場景不可或缺。本文通過抓包實驗,系統對比 $_POST、$HTTP_RAW_POST_DATA、php://input 三者異同,揭示核心規則與常見誤區。
| 場景 | Content-Type | $_POST | php://input | $HTTP_RAW_POST_DATA |
|---|---|---|---|---|
| 普通表單提交 | application/x-www-form-urlencoded | ✅ 解析後數組 | ✅ 原始字符串 | ✅ 需開啟 always_populate_raw_post_data |
| JSON/XML API | application/json, text/xml | ❌ 空數組 | ✅ 原始字符串 | ✅ 同上 |
| 文件上傳 | multipart/form-data | ✅ 解析後數組(不含文件) | ❌ 空字符串 | ❌ 空 |
| GET 請求 | 無 Body | ❌ | ❌ 空 | ❌ 空 |
Content-Type: application/x-www-form-urlencoded 或 multipart/form-data 時自動解析填充,且經過 urldecode()。Content-Length 的請求體原始字節,不解析、不解碼,內存效率高,無需 php.ini 配置。always_populate_raw_post_data = On,同樣不支持 multipart/form-data,PHP 7.0 已移除。測試腳本 phpinput_server.php 同時輸出三者:
<?php
$raw = file_get_contents('php://input', 'r');
echo "-------\$_POST------------------\n"; var_dump($_POST);
echo "-------php://input-------------\n"; echo $raw . "\n";
?>
HTTP 請求體: n=perfgeeks&p=7788
$_POST: array('n'=>'perfgeeks','p'=>'7788')
php://input: n=perfgeeks&p=7788 ← 內容一致,格式不同
HTTP 請求體:$_POST: array(0) {} ← 空! php://input: 完整 XML 字符串 ← 唯一可用數據源 jt_userinfo
HTTP 請求體: 包含 boundary 分隔的多部分數據
$_POST: array('n'=>'perfgeeks') ← 僅普通字段
php://input: 空字符串 ← PHP 刻意屏蔽
HTTP 請求體: (空) php://input: 空 ← GET 數據在 URL Query String,不在 Body
Content-Type = application/x-www-form-urlencoded 且 POST 時,$_POST 與 php://input 內容等價(前者解碼後數組,後者原始字符串)。php://input **無法讀取** multipart/form-data,**無法讀取** GET 查詢字符串。php://input 與 $HTTP_RAW_POST_DATA 內容始終一致,但前者無需配置、內存效率更高、PHP 7+ 推薦唯一選擇。$_GET 數據來自 URL Path 中的 Query String,與請求體無關。<?php
// 通用原始數據讀取器
function getRawInput() {
// PHP 5.6+ 可直接用 php://input
// 兼容舊版本:優先 php://input,回退 $HTTP_RAW_POST_DATA
$raw = file_get_contents('php://input');
if ($raw === '' && isset($GLOBALS['HTTP_RAW_POST_DATA'])) {
$raw = $GLOBALS['HTTP_RAW_POST_DATA'];
}
return $raw;
}
// JSON API 典型用法
$raw = getRawInput();
$data = json_decode($raw, true);
if (json_last_error() !== JSON_ERROR_NONE) {
http_response_code(400);
exit('Invalid JSON');
}
// 業務邏輯...
?>
| 坑位 | 現象 | 原因 | 對策 |
|---|---|---|---|
| 文件上傳 php://input 為空 | 無法獲取上傳數據 | PHP 刻意屏蔽 multipart/form-data | 用 $_FILES 處理文件,$_POST 處理字段 |
| 重複讀取 php://input | 第二次讀取為空 | 流不可重風 | 讀取後存變量復用,或用 tmpfile 緩存 |
| 大請求體內存暴漲 | OOM | 一次性讀入內存 | 分塊讀取 fopen('php://input','r') + fread 循環 |
| Content-Length 缺失 | 讀取截斷/掛起 | Chunked Transfer Encoding | 確保客戶端發送 Content-Length 或升級 PHP 7.1+ 支持 chunked |
徹底理解 php://input 的適用邊界,是構建健壯 API 接口、Webhook 接收端、非標準協議適配器的前提。