Answers:
原因是php://input
无论内容类型如何,都将在请求的HTTP标头之后返回所有原始数据。
PHP superglobal $_POST
仅应包装以下任一数据
application/x-www-form-urlencoded
(用于简单表单发布的标准内容类型)或multipart/form-data
(主要用于文件上传)这是因为这些是用户代理必须支持的唯一内容类型。因此,服务器和PHP传统上不希望接收任何其他内容类型(这并不意味着它们不能)。
因此,如果您只是简单地发布旧的HTML form
,则请求看起来像这样:
POST /page.php HTTP/1.1
key1=value1&key2=value2&key3=value3
但是,如果您经常使用Ajax,那么这种可能性还包括使用类型(字符串,整数,布尔值)和结构(数组,对象)交换更复杂的数据,因此在大多数情况下,JSON是最佳选择。但是带有JSON有效负载的请求看起来像这样:
POST /page.php HTTP/1.1
{"key1":"value1","key2":"value2","key3":"value3"}
现在的内容将是application/json
(或至少没有上述内容),因此PHP的$_POST
-wrapper不知道该如何处理。
数据仍然在那里,您只是无法通过包装器访问它。因此,您需要使用file_get_contents('php://input')
(只要未multipart/form-data
编码)原始格式来获取它。
这也是您访问XML数据或任何其他非标准内容类型的方式。
application/json
其识别为$_POST
数组的有效数据源。甚至已经发布了专门针对该支持的请求。
php://input
可以给您数据的原始字节。如果POSTED数据是JSON编码的结构(对于AJAX POST请求通常是这种情况),这很有用。
这是一个用于执行此操作的函数:
/**
* Returns the JSON encoded POST data, if any, as an object.
*
* @return Object|null
*/
private function retrieveJsonPostData()
{
// get the raw POST data
$rawData = file_get_contents("php://input");
// this returns null if not valid json
return json_decode($rawData);
}
$_POST
当您处理传统POST提交的表单中的键值数据时,该数组更有用。通常仅在POST数据采用可识别的格式时才有效application/x-www-form-urlencoded
(有关详细信息,请参见http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4)。
true
第二个参数传递给json_decode
,它将返回一个关联数组。
PHP并非旨在显式地为您提供纯REST(GET,POST,PUT,PATCH,DELETE)之类的用于处理HTTP请求的接口。
然而,$_POST
,$_GET
,和$_FILES
超全局变量和函数filter_input_array()
是一般人的/外行的需求是非常有用的。
$_POST
(和$_GET
)的第一大隐藏优势是PHP会自动对输入数据进行url解码。您甚至都无需考虑必须这样做,尤其是对于标准GET请求中的查询字符串参数。
话虽如此,随着您对编程知识的了解不断提高,并想使用JavaScript的XmlHttpRequest
对象(某些情况下为jQuery),您会发现这种方案的局限性。
$_POST
限制您在HTTP Content-Type
标头中使用两种媒体类型:
application/x-www-form-urlencoded
和multipart/form-data
因此,如果要将数据值发送到服务器上的PHP,并使其显示在$_POST
superglobal中,则必须在客户端上对它进行urlencode,然后将这些数据作为键/值对发送,这对于新手来说是不方便的步骤(尤其是在尝试确定URL的不同部分是否需要不同形式的urlencoding:常规,原始等)时。)
对于所有jQuery用户,该$.ajax()
方法都是先将JSON转换为URL编码的键/值对,然后再将其传输到服务器。您可以通过设置覆盖此行为processData: false
。只需阅读$ .ajax()文档,别忘了在Content-Type标头中发送正确的媒体类型。
通常,如果您正在使用HTML表单执行正常的,同步的(重新绘制整个页面时)HTTP请求,则用户代理(网络浏览器)将为您使用表单代码。如果要使用该XmlHttpRequest
对象执行异步HTTP请求,则如果要使该数据显示在$_POST
superglobal中,则必须构造一个urlencoded字符串并将其发送。
从JavaScript数组或对象转换为urlencode字符串会困扰许多开发人员(即使使用诸如Form Data的新API )。他们宁愿只能够发送JSON,并且使客户端代码更有效。
请记住(眨眼,眨眼),一般的Web开发人员不会学习XmlHttpRequest
直接使用对象,全局函数,字符串函数,数组函数以及正则表达式,例如您和I ;-)。对他们进行Urlencoding是一场噩梦。;-)
PHP缺乏直观的XML和JSON处理功能,这使许多人大失所望。您可能会认为它现在已经成为PHP的一部分(叹气)。
XML,JSON和YAML都具有可以放入HTTP Content-Type
标头中的媒体类型。
查看IANA定义了多少种媒体类型(以前称为MIME类型)。
查看有多少个HTTP标头。
使用php://input
流使您可以绕开PHP强制的婴儿保姆/手持抽象级别。:-) 拥有权利的同时也被赋予了重大的责任!
现在,在处理流传输的数据值之前php://input
,您应该/必须做一些事情。
啊,哈!是的,您可能希望发送到应用程序中的数据流是UTF-8编码的,但是如何知道它是否是?
php://input
。您是否要先处理流数据而不先知道有多少?那是一个可怕的主意。您不能仅依靠HTTP Content-Length
标头来指导流输入的大小,因为它可以被欺骗。
您将需要:
您是否要在不知道流的当前编码的情况下尝试将流数据转换为UTF-8?怎么样?iconv流过滤器(iconv流过滤器示例)似乎想要这样的开始和结束编码。
'convert.iconv.ISO-8859-1/UTF-8'
因此,如果您认真负责,则需要:
(Update:'convert.iconv.UTF-8/UTF-8'
将所有内容强制为UTF-8,但是您仍然必须考虑iconv库可能不知道如何翻译的字符。换句话说,您必须确定如何定义无法翻译字符时应采取的措施:1)插入一个虚拟字符,2)失败/抛出和异常)。
您不能仅依靠HTTP Content-Encoding
标头,因为这可能表示类似于压缩的内容,如下所示。这不是您要对iconv做出决定的依据。
Content-Encoding: gzip
第一部分:HTTP请求相关
第二部分:流数据相关
第三部分:与数据类型有关
(请记住,数据仍然可以是URL编码的字符串,然后必须对其进行解析和URL解码)。
第四部分:数据值相关
过滤输入数据。
验证输入数据。
该$_POST
超全局,与输入范围的php.ini设置一起,是外行简单。但是,在使用流时,处理字符编码更加直观和有效,因为无需遍历超全局变量(通常是数组)来检查输入值是否正确。
因此,我编写了一个函数,该函数将从php:// input流获取POST数据。
因此,这里的挑战是切换到PUT,DELETE或PATCH请求方法,并仍然获取随该请求发送的帖子数据。
我可能是为面临类似挑战的人分享的。下面的功能是我想出的,它可以正常工作。希望对您有所帮助!
/**
* @method Post getPostData
* @return array
*
* Convert Content-Disposition to a post data
*/
function getPostData() : array
{
// @var string $input
$input = file_get_contents('php://input');
// continue if $_POST is empty
if (strlen($input) > 0 && count($_POST) == 0 || count($_POST) > 0) :
$postsize = "---".sha1(strlen($input))."---";
preg_match_all('/([-]{2,})([^\s]+)[\n|\s]{0,}/', $input, $match);
// update input
if (count($match) > 0) $input = preg_replace('/([-]{2,})([^\s]+)[\n|\s]{0,}/', '', $input);
// extract the content-disposition
preg_match_all("/(Content-Disposition: form-data; name=)+(.*)/m", $input, $matches);
// let's get the keys
if (count($matches) > 0 && count($matches[0]) > 0)
{
$keys = $matches[2];
foreach ($keys as $index => $key) :
$key = trim($key);
$key = preg_replace('/^["]/','',$key);
$key = preg_replace('/["]$/','',$key);
$key = preg_replace('/[\s]/','',$key);
$keys[$index] = $key;
endforeach;
$input = preg_replace("/(Content-Disposition: form-data; name=)+(.*)/m", $postsize, $input);
$input = preg_replace("/(Content-Length: )+([^\n]+)/im", '', $input);
// now let's get key value
$inputArr = explode($postsize, $input);
// @var array $values
$values = [];
foreach ($inputArr as $index => $val) :
$val = preg_replace('/[\n]/','',$val);
if (preg_match('/[\S]/', $val)) $values[$index] = trim($val);
endforeach;
// now combine the key to the values
$post = [];
// @var array $value
$value = [];
// update value
foreach ($values as $i => $val) $value[] = $val;
// push to post
foreach ($keys as $x => $key) $post[$key] = isset($value[$x]) ? $value[$x] : '';
if (is_array($post)) :
$newPost = [];
foreach ($post as $key => $val) :
if (preg_match('/[\[]/', $key)) :
$k = substr($key, 0, strpos($key, '['));
$child = substr($key, strpos($key, '['));
$child = preg_replace('/[\[|\]]/','', $child);
$newPost[$k][$child] = $val;
else:
$newPost[$key] = $val;
endif;
endforeach;
$_POST = count($newPost) > 0 ? $newPost : $post;
endif;
}
endif;
// return post array
return $_POST;
}
如何使用它的简单示例
<?php
if(!isset($_POST) || empty($_POST)) {
?>
<form name="form1" method="post" action="">
<input type="text" name="textfield"><br />
<input type="submit" name="Submit" value="submit">
</form>
<?php
} else {
$example = file_get_contents("php://input");
echo $example; }
?>