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


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键值序列上传文件,只是转储所有的文件作为post数据,没有变量名或任何东西。 $_POST为空,$_FILES也为空,php://input将包含精确的文件,写为字符串。

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

PHP的超全局变量$_POST,只能包装任意一种类型的数据

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

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

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

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的$ _post包装器还不知道如何处理它。

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

这也是访问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)最大的隐藏优势是PHP会自动对输入数据进行url解码。您甚至从来没有想过必须这样做,特别是对于标准GET请求中的查询字符串参数,或与POST请求一起提交的HTTP主体数据。

其他HTTP请求方法

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

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

然而,你会学到更多……

话虽如此,随着PHP编程知识的提高,并希望使用JavaScript的XmlHttpRequest对象(对于某些人来说是jQuery),您就会看到这种方案的局限性。

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

应用程序/ x-www-form-urlencoded, 多部分/格式

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

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

php://input, but…

即使你使用php://input而不是$_POST作为你的HTTP POST请求体数据,它也不会与multipart/form-data的HTTP内容类型一起工作,这是你在HTML表单上使用的内容类型,当你想要允许文件上传!

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

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

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

PHP手册:PHP://输入

input是一个只读流,允许你读取原始数据 从请求体…php://input不可用 enctype = " multipart /格式”。

PHP框架拯救?

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

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

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

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

你和JavaScript有多熟?:-)

从JavaScript数组或对象到urlencoded字符串的转换困扰着许多开发人员(即使是像Form Data这样的新api)。他们更希望能够发送JSON,而且这样做对客户端代码来说会更有效。

记住(眨眼,眨眼),一般的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://输入流允许您绕过php强加给世界的保姆/手把手级别的抽象。:-)能力越大责任越大!

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

确定是否指示了正确的HTTP方法(GET、POST、 放置,修补,删除,…) 确定是否传输了HTTP内容类型报头。 确定Content-Type的值是否是所需的媒体 类型。 确定发送的数据是否是格式良好的XML / JSON / YAML / 等。 如果需要,将数据转换为PHP数据类型:数组或 对象。 如果这些基本检查或转换失败,则抛出异常!

字符编码呢?

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

两个关键问题。

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

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

你需要一个:

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

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

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

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

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

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

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

Content-Encoding: gzip

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

第一部分:HTTP请求相关

确定是否指示了正确的HTTP方法(GET、POST、 放置,修补,删除,…) 确定是否传输了HTTP内容类型报头。 确定Content-Type的值是否是所需的媒体 类型。

第二部分:流数据相关

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

第三部分:数据类型相关

判断发送的数据是否是格式良好的XML / JSON / YMAL / 等。

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

如果需要,将数据转换为PHP数据类型:数组或 对象。

第四部分:数据价值相关

筛选输入数据。 验证输入数据。

现在你明白了吗?

$_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);