PHP“php:// input”vs $ _POST

时间:2012-01-17 11:08:13

标签: php ajax input

在与来自JQuery的Ajax请求交互时,我被指示使用方法php://input而不是$_POST。我不明白的是使用此方法与$_POST$_GET的全局方法相比的好处。

6 个答案:

答案 0 :(得分:393)

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

PHP超全局$_POST,只有应该包装

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

这是因为这些是must be supported by user agents唯一的内容类型。所以服务器和PHP传统上不希望接收任何其他内容类型(这并不意味着他们不能)。

因此,如果您只是发布一个好的旧HTML form,请求看起来像这样:

POST /page.php HTTP/1.1

key1=value1&key2=value2&key3=value3

但是如果你正在使用Ajax,这个probaby还包括用类型(string,int,bool)和结构(数组,对象)交换更复杂的数据,所以在大多数情况下JSON是最好的选择。但是具有JSON-payload的请求看起来像这样:

POST /page.php HTTP/1.1

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

内容现在是application/json(或至少没有上述内容),因此PHP的$_POST - 包装器不知道如何处理(尚未)。

数据仍然存在,您无法通过包装器访问它。因此,您需要使用file_get_contents('php://input')as long as it's not multipart/form-data-encoded)以原始格式自行获取。

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

答案 1 :(得分:43)

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数组更有用。这仅适用于POSTed数据采用可识别格式的情况,通常为application/x-www-form-urlencoded(有关详细信息,请参阅http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4)。

答案 2 :(得分:24)

如果发布数据格式错误,$ _POST将不包含任何内容。然而,php://输入将具有格式错误的字符串。

例如,有一些ajax应用程序,它们不能形成正确的上传文件的后键值序列,只是将所有文件作为发布数据转储,没有变量名称或任何东西。 $ _POST将为空,$ _FILES也为空,并且php://输入将包含精确文件,写为字符串。

答案 3 :(得分:4)

首先,有关PHP的基本知识。

PHP并非旨在显式地为您提供纯REST(GET,POST,PUT,PATCH,DELETE)之类的用于处理HTTP请求的接口。

但是,$_POST$_GET$_FILES superglobals以及功能filter_input_array()对于普通人/外行的需求非常有用。 / p>

$_POST(和$_GET)的第一大隐藏优势是您的输入数据被PHP自动 url解码。您甚至都不需要考虑这样做,尤其是对于标准GET请求中的查询字符串参数。

但是,您将学到更多...

话虽如此,随着您对编程知识的了解不断提高,并想使用JavaScript的XmlHttpRequest对象(某些情况下为jQuery),您会发现这种方案的局限性。

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

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

因此,如果要将数据值发送到服务器上的PHP,并使其显示在$_POST超全局变量中,则必须urlencode在客户端并以键/值对的形式发送所述数据,这对新手来说是一个不方便的步骤(特别是在尝试确定URL的不同部分是否需要不同形式的urlencoding:常规,原始等)时。

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

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

通常,如果您正在执行带有HTML表单的正常,同步(重新绘制整个页面时)的HTTP请求,则用户代理(网络浏览器)将为您urlencode您的表单数据。如果您想使用XmlHttpRequest对象进行异步HTTP请求,那么您必须生成一个urlencoded字符串并发送它,如果您希望该数据显示在$_POST超全局变量中

您与JavaScript有何联系? :-)

从JavaScript数组或对象转换为超文本编码的字符串困扰着许多开发人员(即使使用Form Data之类的新API)。他们宁愿只能够发送JSON,并且使客户端代码更加高效。

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

PHP,有什么用?

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

媒体类型太多(过去是MIME类型)

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

  • application / xml
  • applicaiton / json
  • application / yaml(尽管未列出IANA的正式名称)

看看IANA定义了多少media-types(以前是MIME类型)。

看看有多少HTTP headers

php://输入法或破产法

使用php://input流使您可以绕开PHP强制世界上的保姆/手持式抽象级别。 :-)强大的力量带来巨大的责任!

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

  1. 确定是否已指示正确的 HTTP方法(GET,POST, PUT,PATCH,DELETE等)
  2. 确定是否已发送HTTP Content-Type 标头。
  3. 确定“内容类型”的是否是所需的媒体 类型。
  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 stream filter example)似乎需要这样的开始和结束编码。

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

第二部分:与流数据相关

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

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

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

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

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

第四部分:与数据值相关

  1. 过滤输入数据。

  2. 验证输入数据。

现在看到了吗?

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

答案 4 :(得分: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 :(得分:-2)

使用方法的简单示例

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