PHP输入流 php://input与$_POST

分享于:2020-12-14 15:47:59

例如在开发微信支付接口时,微信服务器使用post异步返回的是xml格式数据流,这时应用服务器使用$_POST全局变量是接收不到数据的,必须使用代码:

file_get_contents("php://input");

获取,php://input 是个可以访问请求的原始数据的只读流。


php://input 与 $_POST

通过代码演示两种方式获取数据有什么差异。


server.php 是接收数据的服务端PHP脚本文件

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


1)当Content-Type是application/x-www-form-urlencoded


post.php 是发送数据的客户端PHP脚本文件

<?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 = 'www.phpnanshen.com';
$port = 80;
$path = '/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");
 
 $d = '';
  while (!feof($fp)) {
    $d .= fgets($fp, 4096);
  }
  fclose($fp);
  echo $d;
}


在cli下执行post.php,得到结果:


2020-12-14_152749.png


file_get_contents('php://input')获取到的输入流是和http_entity_body基本一致的,而$_POST则是对获取的数据流进一步处理成了数组。


如果请求数据中不携带Content-Type,其结果和上面的一样。


2)当Content-Type是text/xml


post.php

<?php
$http_entity_body ='<?xml version="1.0"><methodcall><name>jt_userinfo</name></methodcall>';
$http_entity_type = 'text/xml';
$http_entity_length = strlen($http_entity_body);
$host = 'www.phpnanshen.com';
$port = 80;
$path = '/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");
 
 $d = '';
  while (!feof($fp)) {
    $d .= fgets($fp, 4096);
  }
  fclose($fp);
  echo $d;
}


结果:


2020-12-14_152749.png


file_get_contents('php://input')获取到的输入流还是和http_entity_body基本一致,而$_POST则是空。


这也就是开头提到的,微信支付异步返回通知时,无法通过$_POST获取微信服务器返回的参数数据。因为微信服务器请求头部信息Content-Type也是text/xml,应用服务器只有通过file_get_contents('php://input')才能获取输入流,也就是微信服务器返回的XML格式数据。


3)表单上传文件时

<form enctype="multipart/form-data" action="phpinput_server.php" method="POST" >
    <input type="text" name="n"  />
    <input type="file" name="f" />
    <input type="submit" value="upload now" />
</form>

php://input是无效的,因为在上传文件时会将该次http请求头部(head)中的Content-Type设置为multipart/form-dataenctype="multipart/form-data" 的时候php://input是无效的。 

multipart/form-data表示以POST方法提交表单数据,它还伴随了文件上传,所以会跟application/x-www-form-urlencoded数据格式不一样。它会以一更种更合理的,更高效的数据格式传递给服务端。


备注:


php://input读取不到$_GET数据。是因为$_GET数据作为query_path写在http请求头部(header)的PATH字段,而不是写在http请求的body部分。


php://input 可以读取由Content-Length指定长度的值,不管是POST方式或者GET方法提交过来的数据。一般GET方法提交数据时,http request entity body部分都为空。


来源:http://www.nowamagic.net/academy/detail/12220520