PHP“ php:// input”与$ _POST


243

我被指示使用该方法,php://input而不是$_POST与JQuery的Ajax请求进行交互时使用。我不了解使用vs $_POST或全局方法的好处$_GET


2
在绊倒这篇文章并阅读其真棒答案之前,我曾经使用“ hacks”在PHP端接收ajax调用!对于将来遇到同样问题的其他人,我希望搜索引擎也能阅读我的评论!:)
aderchox

Answers:


483

原因是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数据或任何其他非标准内容类型的方式。


39
对于“这也是您访问XML数据或任何其他非标准内容类型的方式”的
+1

@Quasdank我正在将JSON从Android应用程序发送到云中的php xampp服务器(stackoverflow.com/questions/36558261/…),但是当我尝试file_get_contents('php:// input')时,它无法正常工作,它只是返回string(0)。这曾经在我的本地计算机上工作,但是当我将其部署到云中时却无法工作。请问你能帮帮我吗?
The_Martian '16

1
值得注意的是,在对PHP的AJAX请求中使用XMLHttpRequest对象并不意味着必须发布JSON。这是额外的开销,但是您的客户端JavaScript可以转换为application / x-www-form-urlencoded格式。但是,转换可能不是数据类型的。
安东尼·鲁特里奇

有必要说两种公认的内容类型的限制在很大程度上是历史性的。没有什么能阻止PHP识别,即将application/json其识别为$_POST数组的有效数据源。甚至已经发布了专门针对该支持的请求。
AnrDaemon


53

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)。


7
值得注意的是,如果您将true第二个参数传递给json_decode,它将返回一个关联数组。
Vahid Amiri

28

如果发布数据格式错误,则$ _POST将不包含任何内容。但是,php:// input将具有格式错误的字符串。

例如,有一些ajax应用程序,它们没有形成用于上传文件的正确的发布键值序列,而只是将所有文件作为发布数据转储,没有变量名或其他任何内容。$ _POST将为空,$ _ FILES也将为空,并且php:// input将包含以字符串形式写入的确切文件。


22

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

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标头中使用两种媒体类型:

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

因此,如果要将数据值发送到服务器上的PHP,并使其显示在$_POSTsuperglobal中,则必须在客户端上对它进行urlencode,然后将这些数据作为键/值对发送,这对于新手来说是不方便的步骤(尤其是在尝试确定URL的不同部分是否需要不同形式的urlencoding:常规,原始等)时。)

对于所有jQuery用户,该$.ajax()方法都是先将JSON转换为URL编码的键/值对,然后再将其传输到服务器。您可以通过设置覆盖此行为processData: false。只需阅读$ .ajax()文档,别忘了在Content-Type标头中发送正确的媒体类型。

网址编码?有没有搞错!!!???

通常,如果您正在使用HTML表单执行正常的,同步的(重新绘制整个页面时)HTTP请求,则用户代理(网络浏览器)将为您使用表单代码。如果要使用该XmlHttpRequest对象执行异步HTTP请求,则如果要使该数据显示在$_POSTsuperglobal中,则必须构造一个urlencoded字符串并将其发送。

您与JavaScript有何联系?:-)

从JavaScript数组或对象转换为urlencode字符串会困扰许多开发人员(即使使用诸如Form Data的新API )。他们宁愿只能够发送JSON,并且使客户端代码更有效

请记住(眨眼,眨眼),一般的Web开发人员不会学习XmlHttpRequest直接使用对象,全局函数,字符串函数,数组函数以及正则表达式,例如您和I ;-)。对他们进行Urlencoding是一场噩梦。;-)

PHP,有什么用?

PHP缺乏直观的XML和JSON处理功能,这使许多人大失所望。您可能会认为它现在已经成为PHP的一部分(叹气)。

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

XML,JSON和YAML都具有可以放入HTTP Content-Type标头中的媒体类型。

  • 应用程序/ xml
  • applicaiton / json
  • 申请/ yaml(尽管未列出IANA的正式名称)

查看IANA定义了多少种媒体类型(以前称为MIME类型)。

查看有多少个HTTP标头

php://输入或破产

使用php://input流使您可以绕开PHP强制的婴儿保姆/手持抽象级别。:-) 拥有权利的同时也被赋予了重大的责任!

现在,在处理流传输的数据值之前php://input,您应该/必须做一些事情。

  1. 确定是否已指示正确的HTTP方法(GET,POST,PUT,PATCH,DELETE等)
  2. 确定是否已发送HTTP Content-Type标头。
  3. 确定Content-Type 的是否是所需的媒体类型。
  4. 确定发送的数据是否格式正确的 XML / JSON / YMAL /等等
  5. 如有必要,将数据转换为PHP数据类型:数组或对象。
  6. 如果这些基本检查或转换中的任何一个失败,则抛出异常

字符编码呢?

啊,哈!是的,您可能希望发送到应用程序中的数据流是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. 动态/运行时流过滤器定义算法(因为您不知道先验编码的开始)。

Update'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 Content-Type标头。
  3. 确定Content-Type 的是否是所需的媒体类型。

第二部分:流数据相关

  1. 确定输入流的大小(可选,但建议)。
  2. 确定输入流的编码。
  3. 如有必要,将输入流转换为所需的字符编码(UTF-8)。
  4. 如有必要,请撤消所有应用程序级别的压缩或加密,然后重复步骤4、5和6。

第三部分:与数据类型有关

  1. 确定发送的数据是否格式正确的 XML / JSON / YMAL /等等

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

  1. 如有必要,将数据转换为PHP数据类型:数组或对象。

第四部分:数据值相关

  1. 过滤输入数据。

  2. 验证输入数据。

现在看到了吗?

$_POST超全局,与输入范围的php.ini设置一起,是外行简单。但是,在使用流时,处理字符编码更加直观和有效,因为无需遍历超全局变量(通常是数组)来检查输入值是否正确。


1
哇!这个答案应该有更高的评级。非常感谢您将泛光灯带入黑暗。
Lox

归根结底,PHP会很好地更新基本默认值。但是,将HTTP请求方法与同名数据结构($ _GET,$ _ POST)耦合是逻辑上的谬误。重要的是(1)所需的HTTP请求方法,以及(2)是否存在带有该请求的请求数据(内容类型)。因此,与Perl一样,您将看到自己可能成为语言创建者/维护者的意愿的牺牲品。
Anthony Rutledge,

0

因此,我编写了一个函数,该函数将从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;
    }

-5

如何使用它的简单示例

 <?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;  }  
   ?>
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.