PHP“PHP: / / input"与$ _POST

我已经被指示使用方法php://input而不是$_POST与JQuery Ajax请求交互时。我不明白的是使用这个与$_POST$_GET的全局方法的好处。

282150 次浏览

php://input可以给你数据的原始字节。如果POST数据是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)。

如果post数据格式不正确,$_POST将不包含任何内容。然而,php://input将会有畸形的字符串。

例如,有一些ajax应用程序,上传文件时没有形成正确的post key-value序列,只是将所有文件转储为post数据,没有变量名或任何东西。 $_POST为空,$_FILES也为空,php://input将包含精确的文件,写为字符串

原因是php://input返回请求的http头之后的所有原始数据,而不管内容类型是什么。

PHP超全局变量$_POST,只有应该是包装数据

  • application/x-www-form-urlencoded(简单表单帖子的标准内容类型)或
  • multipart/form-data(主要用于文件上传)

这是因为这些是必须用户代理支持. xml中唯一的内容类型。因此,服务器和PHP传统上不期望接收任何其他内容类型(这并不意味着它们不能)。

所以,如果你简单地POST一个好的旧HTML form,请求看起来像这样:

POST /page.php HTTP/1.1


key1=value1&key2=value2&key3=value3

但如果您经常使用Ajax,这可能还包括用类型(字符串、int、bool)和结构(数组、对象)交换更复杂的数据,因此在大多数情况下JSON是最佳选择。但是带有json有效负载的请求应该是这样的:

POST /page.php HTTP/1.1


{"key1":"value1","key2":"value2","key3":"value3"}

内容现在是application/json(或者至少不是上面提到的任何一个),所以PHP的__abc1包装器(还)不知道如何处理它。

数据仍然在那里,只是不能通过包装器访问它。所以你需要自己用file_get_contents('php://input') (只要它不是__abc1编码的)获取原始格式的数据。

这也是访问xml数据或任何其他非标准内容类型的方法。

简单的例子如何使用它

 <?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;  }
?>

首先,关于PHP的一个基本事实。

PHP的设计初衷并不是明确地为您提供一个纯REST (GET, POST, PUT, PATCH, DELETE)类的接口来处理HTTP请求

然而,$_SERVER$_COOKIE$_POST$_GET,和$_FILES 已经,以及函数filter_input_array()对于普通人/外行的需求非常有用。

$_POST(和$_GET)最大的隐藏优势是你的输入数据是url-由PHP自动解码。你甚至从来没有想过必须这样做,特别是对于标准GET请求中的查询字符串参数,或与POST请求一起提交的HTTP主体数据。

其他HTTP请求方法

那些研究底层HTTP协议及其各种请求方法的人会了解到有许多HTTP请求方法,包括经常引用的PUTPATCH(在谷歌的Apigee中没有使用)和DELETE

在PHP中,当不使用POST时,没有用于获取HTTP请求体数据的超全局函数或输入过滤器函数。罗伊·菲尔丁的弟子们要做什么?: -)

然而,你会学到更多……

话虽如此,当你的PHP编程知识提高,想要使用JavaScript的XmlHttpRequest对象(有些是jQuery)时,你就会看到这种方案的局限性。

$_POST限制你在HTTP Content-Type报头中使用两种媒体类型:

  1. application/x-www-form-urlencoded,
  2. multipart/form-data

因此,如果你想在服务器上向PHP发送数据值,并让它显示在$_POST超全局变量中,然后你必须在客户端urlencode它,并将数据作为键/值对发送——这对于新手来说是一个不方便的步骤(特别是当试图弄清楚URL的不同部分是否需要不同形式的urlencoding: normal, raw等..)。

对于所有jQuery用户,$.ajax()方法在将JSON传输到服务器之前将它们转换为URL编码的键/值对。你可以通过设置processData: false来覆盖这个行为。只要阅读$.ajax()文档,不要忘记在Content-Type头中发送正确的媒体类型。

php://input, but…

即使你使用php://input而不是$_POST来处理你的HTTP POST请求体数据,它也会使用不工作和一个HTTP Content-Typemultipart/form-data。这是你在HTML表单上使用的内容类型,当你想允许文件上传时!

<form enctype="multipart/form-data" accept-charset="utf-8" action="post">
<input type="file" name="resume">
</form>

因此,在传统PHP中,要处理来自HTTP POST请求的各种内容类型,您将学习使用$_POSTfilter_input_array(POST)$_FILESphp://input。在PHP中不可能只使用一个通用的HTTP POST请求输入源。

你不能通过$_POSTfilter_input_array(POST)php://input获取文件,也不能通过filter_input_array(POST)$_POST获取JSON/XML/YAML。

PHP Manual: PHP://input . sh

input是一个只读流,允许你读取原始数据 从请求体…php://输入是不可用 with enctype =“多部分/ form-data" . < / p >

PHP框架拯救?

像Codeigniter 4和Laravel这样的PHP框架使用facade来为上述对象提供一个更干净的接口(IncomingRequestRequest对象)。这就是为什么专业的PHP开发人员使用框架而不是原始PHP。

当然,如果您喜欢编程,您可以设计自己的facade对象来提供框架的功能。正是因为我花了时间来研究这个问题,我才能够写下这个答案。

URL编码吗?搞什么鬼!!???

通常情况下,如果你正在对一个HTML表单进行正常的、同步的(当整个页面重新绘制时)HTTP请求,用户代理(web浏览器)将为你对表单数据进行urlencode。如果你想使用XmlHttpRequest对象进行异步HTTP请求,那么你必须创建一个urlencoded字符串并发送它,如果你想要该数据显示在$_POST超全局变量中

你和JavaScript有多熟?: -)

从JavaScript数组或对象转换为urlencoded字符串困扰着许多开发人员(即使是使用像表单数据这样的新api)。他们更愿意仅仅能够发送JSON,并且它将是更高效的客户端代码这样做。

记住(wink, wink),一般的web开发人员不会像你我一样学习直接使用XmlHttpRequest对象、全局函数、字符串函数、数组函数和正则表达式;-)。Urlencoding对他们来说是一场噩梦。: -)

PHP给出了什么?

PHP缺乏直观的XML和JSON处理,这让很多人望而却步。您可能认为它现在已经是PHP的一部分了(唉)。

如此多的媒体类型(过去的MIME类型)

XML、JSON和YAML都有可以放入HTTP Content-Type报头的媒体类型。

  • 应用程序/ xml
  • 应用/ json
  • application/yaml(虽然IANA没有列出官方名称)

看看IANA定义了多少媒体类型(以前是MIME类型)。

看看有多少HTTP头信息

php://input或bust

使用php://input流可以让你绕过PHP强加给世界的保姆/手把手级别的抽象。:-)能力越大责任越大!

现在,在你处理通过php://input流的数据值之前,你应该/必须做几件事。

  1. 确定是否已经指示了正确的HTTP方法 (GET, POST, PUT, PATCH, DELETE,…)
  2. 确定是否传输了HTTP 内容类型报头。
  3. 确定Content-Type的价值是否是所需的媒体 李类型。< / >
  4. 判断发送的数据是否是形成良好的 XML / JSON / YAML / 李等。< / > 如有必要,将转换数据转换为PHP数据类型:数组或 李对象。< / >
  5. 如果这些基本检查或转换失败,则抛出异常 !

字符编码呢?

啊,哈哈!是的,您可能希望发送到应用程序中的数据流是UTF-8编码的,但是如何知道它是否是UTF-8编码的呢?

两个关键问题。

  1. 你不知道有多少数据通过php://input
  2. 您不能确定数据流的当前编码。

您是否打算在不知道流数据有多少的情况下处理流数据?这是个糟糕的主意。你不能完全依赖HTTP Content-Length头来指导流输入的大小,因为它可能被欺骗。

你需要一个:

  1. 流大小检测算法。
  2. 应用程序定义的流大小限制(Apache / Nginx / PHP限制可能太宽泛)。

您是否打算在不知道流的当前编码的情况下尝试将流数据转换为UTF-8 ?怎么做?iconv流过滤器(Iconv流过滤器的例子)似乎需要一个开始和结束编码,就像这样。

'convert.iconv.ISO-8859-1/UTF-8'

因此,如果你是认真的,你需要:

  1. 流编码检测算法。
  2. 动态/运行时流过滤器定义算法(因为你无法先验地知道起始编码)。

(更新: 'convert.iconv.UTF-8/UTF-8'将强制所有内容转换为UTF-8,但你仍然必须考虑到iconv库可能不知道如何翻译的字符。换句话说,你必须在某种程度上定义当字符无法翻译时采取的操作:1)插入一个虚拟字符,2)失败/抛出和异常)。

你不能完全依赖于HTTP Content-Encoding报头,因为这可能表示如下所示的压缩。关于iconv,这不是你想做的决定。

Content-Encoding: gzip

因此,一般的步骤可能是……

第一部分:HTTP请求相关

  1. 确定是否已经指示了正确的HTTP方法 (GET, POST, PUT, PATCH, DELETE,…)
  2. 确定是否传输了HTTP 内容类型报头。
  3. 确定Content-Type的价值是否是所需的媒体 李类型。< / >

第二部分:流数据相关

  1. 确定输入流的大小(可选,但推荐)。
  2. 确定输入流的编码。
  3. 如果需要,将输入流转换为所需的字符 李编码(utf - 8)。< / >
  4. 如有必要,反转任何应用程序级别的压缩或加密,然后重复步骤4、5和6。

第三部分:数据类型相关

  1. 判断发送的数据是否是形成良好的 XML / JSON / YMAL / 李等。< / >

(请记住,数据仍然可以是URL编码的字符串,然后必须解析和URL解码)。

    如有必要,将转换数据转换为PHP数据类型:数组或 李对象。< / >

第四部分:数据价值相关

  1. 过滤输入数据。

  2. 验证输入数据。

现在你明白了吗?

$_POST超全局变量,以及php.ini对输入限制的设置,对于外行来说更简单。然而,当使用流时,处理字符编码更加直观和有效,因为不需要通过超全局变量(通常是数组)来检查输入值是否有正确的编码。

if (strtoupper($_SERVER['REQUEST_METHOD']) != 'POST') {
throw new Exception('Only POST requests are allowed');
}


// Make sure Content-Type is application/json
$content_type = isset($_SERVER['CONTENT_TYPE']) ? $_SERVER['CONTENT_TYPE'] : '';
if (stripos($content_type, 'application/json') === false) {
throw new Exception('Content-Type must be application/json');
}


// Read the input stream
$body = file_get_contents("php://input");
 

// Decode the JSON object
$object = json_decode($body, true);