PHP 缺少一些 $_POST 值,但存在于 php://input

Posted

技术标签:

【中文标题】PHP 缺少一些 $_POST 值,但存在于 php://input【英文标题】:PHP some $_POST values missing but are present in php://input 【发布时间】:2011-07-01 23:14:10 【问题描述】:

我有一个非常大的 html 表单(包含行表,其中包含多个输入),我需要通过 POST 请求将其提交给 php 脚本。 问题是 PHP 的 $_POST 超全局变量中没有出现某些值。

我检查了(使用 Firebug 扩展)这些值实际上是由浏览器发送到服务器的。

$_POST 被填充,但缺少一些值。

我使用以下方法检查了原始请求:

$raw_post = file_get_contents('php://input');

并且返回的字符串具有值。它们只是没有被解析为 $_POST 数组。我注意到的奇怪的事情是,似乎 php://input 值在一定长度后被剪切,并且字符串的其余部分没有通过 $_POST。

我考虑了 post_max_size 和 memory_limit 并将它们设置为较大的值:

memory_limit = 256M
post_max_size = 150M
但根据 php 文档,如果请求大于 post_max_size,则 $_POST 不应包含任何值。

由于表单和请求太大,我无法在此处发布,但我可以发布用于调试问题的 php 脚本:


var_dump($file = file_get_contents('php://input'));
var_dump($_POST);
//... then i parsed the $file

服务器版本:Apache/2.2.9 (Debian) PHP版本:PHP 5.3.2-0.dotdeb.2

谁能解释这种奇怪的 PHP 行为的原因,我应该怎么做(更改 php 设置、代码?)在处理表单时使用 $_POST 数组?

编辑:要清楚:不仅缺少值。 $_POST 也不包含这些键。

例如原始帖子片段:

t_dodparam%5B198%5D=&t_dodparam2%5B198%5D=&t_kolejnosc%5B198%5D=199&n_indeks=201&n_wartosc=testtesttest

密钥 't_dodparam' 在 post 中,它有 key 198。其余参数丢失(ex t_dodparam2 在 post 中,但它没有像 198 这样的键,并且在 $_POST 中没有像 n_wartosc 这样的键)

【问题讨论】:

你能给我们举一个缺少参数值的例子吗? 这里是原始帖子的片段:t_dodparam%5B198%5D=&t_dodparam2%5B198%5D=&t_kolejnosc%5B198%5D=199&n_indeks=201&n_wartosc=testtesttest。 t_dodparam[198] 在后值中,但其余部分缺失 “&”不是表示参数的结束和新参数的开始吗? 并且您的原始数据中没有名称为 198 的参数 我的原始数据中有参数t_dodparam2[198]='',或者t_kolejnosc[198],它们不包含在$_POST中。正如 Jean Hominal 指出的那样,& 符号表示参数的结束,但这只是表明已发布空值(例如空文本字段) 【参考方案1】:

PHP 修改包含字符空格、点、左方括号和其他字符的字段以与已弃用的 register_globals 兼容

您可以在此处的 cmets 中找到很多解决方法: PHP: Variables From External Sources

例如(发帖人评论):

<?php
//Function to fix up PHP's messing up POST input containing dots, etc.
function getRealPOST() 
    $pairs = explode("&", file_get_contents("php://input"));
    $vars = array();
    foreach ($pairs as $pair) 
        $nv = explode("=", $pair);
        $name = urldecode($nv[0]);
        $value = urldecode($nv[1]);
        $vars[$name] = $value;
    
    return $vars;

?>

【讨论】:

谢谢,这正是我所做的,它解决了问题,(作为一种解决方法)但我仍然不知道是什么原因。我认为方括号应该不是问题,因为许多 html 表单使用数组语法(例如&lt;input name="user[1]" type="text" /&gt;)。它在这种形式下工作正常($_POST['t_dodparam'][198] 存在并等于“”),只是输入数据在此之后被剪切。 你是对的,这是一个非常疯狂的行为!你已经检查过 max_input_time 和 max_execution_time 了吗? 这是 max_input_time 问题。非常感谢! 另外,为了将来参考,我建议查看 suhosin 补丁设置(如果已安装)。它有一些默认值,来自 Ubuntu 等系统上的软件包,限制了超全局变量的大小。 我知道这是一个旧线程 - 只需要指出方括号不是问题,但 ONE OPEN 方括号可能是个问题。【参考方案2】:

我刚刚通过在我的 PHP 配置文件中向 max_input_vars 添加一个值来解决此问题。根据this。它是在 5.3.9 中引入的,但经过一些软件包升级后,我在 5.3.2 中遇到了这个问题。

max_input_vars 的默认值为 1000,这对于我的表单来说太小了。

【讨论】:

请注意,此 var 不能在运行时设置。 :( 这是我一直在寻找的答案。谢谢。 如果您只是在 POST 中“缺少”变量,并且您的表单非常大,这可能就是答案...... 你可以在.htaccess中添加php_value max_input_vars 10000这一行来设置 php_value max_input_vars 100000 是否存在某种漏洞?【参考方案3】:

有许多不同的事情可能会导致这种情况。最好检查您的错误日志。导致此症状的许多因素会将消息放入错误日志中,但不会在您的 PHP 应用程序中显示错误。

一些可能的原因:

苏霍辛

Suhosin 是 PHP 的扩展,旨在保护服务器和用户免受 PHP 应用程序和 PHP 核心中已知和未知缺陷的影响。它所做的其中一件事是限制 $_POST、$_GET、$_REQUEST 和 $_COOKIE 的大小。如果您的问题是 Suhosin,您的日志中会出现错误,例如

ALERT - 超出配置的 POST 变量限制 - 删除变量 'foo'

解决方法很简单,只需要增加php.ini中允许的最大变量数即可。如果您没有 suhosin 部分,只需创建一个。像这样:

[suhosin]
suhosin.request.max_vars = 1000 # Default is 200
suhosin.post.max_vars = 1000 # Default is 200

还有其他 suhosin 设置可能导致此问题,但这些是最可能的候选设置。

无效的表单字段名称

过去 PHP 有一个名为 register_globals 的设置(现已弃用),它可以自动将 GET 和 POST 变量转换为 PHP 变量。因此,如果您的表单有字段 'foo' 和 'bar',那么当提交该表单时,变量 $foo 和 $bar 将被自动创建。但是,有几个字符在 PHP 变量名中无效(空格、点、开方括号等)。根据您使用的字符,变量可能会去除无效字符、缺少其值或未设置。尽管不再使用 register_globals,但 PHP 在构建 $_POST、$_GET、$_REQUEST 和 $_COOKIE 时仍然会去除这些字符。如果您无法修复表单字段的值,您可以尝试以下方法:

<?php
/**
 * Converts raw POST data into an array.
 * 
 * Does not work for hierarchical POST data.
 *
 * @return array
 */
function real_post() 
  static $post;
  if (!isset($post)) 
    $pairs = explode("&", file_get_contents("php://input"));
    $post = array();
    foreach ($pairs as $pair) 
      $x = explode("=", $pair);
      $post[rawurldecode($x[0])] = rawurldecode($x[1]);
    
  
  return $post;

?>

【讨论】:

实际上,名称中的一个点导致我的 $_POST 变量出现问题。谢谢@Dalin【参考方案4】:

如何使用"parse_str" 将查询字符串转换为php 结构?这个函数是 http_build_query 的逆函数。

    $b = array();
    parse_str(file_get_contents("php://input"), $b);

【讨论】:

这对我帮助很大。【参考方案5】:

我正在发布 Jquery 的 .ajax() 函数。我试图从$_POST 检索我的数据,但它不完整。然后我发现这篇文章让我走上了正轨。但是,上面描述的 getRealPOST() 方法对我不起作用——它不能很好地处理多维和嵌套数组。相反,我使用了 PHP 的 parse_str() 方法,它成功了,而且更简洁:

$rawdata = file_get_contents('php://input');
$urldecoded = urldecode($rawdata);
parse_str($urldecoded, $parsed);

$data = $parsed['data'];

(在我的 JS 中,我发布了一个对象,其中有效负载位于 data 属性中。您的可能会有所不同。)

我也进入了我的 php.ini 并启动了 max_memorymax_input_vars,但它似乎并没有解决我的问题。我还花了一些时间寻找一条红鲱鱼,因为我正在使用我的错误日志来打印原始数据,我忘记了 error_log 对打印的字符数有限制,除非你记得增加它。

无论如何,希望这对那些发现自己正在与这个问题作斗争的人有所帮助。

【讨论】:

【参考方案6】:

使用此函数获取真实数据

function get_real_post() 

    function set_nested_value(&$arr, &$keys, &$value) 
        $key = array_shift($keys);
        if (count($keys)) 
            // Got deeper to go
            if (!array_key_exists($key, $arr)) 
                // Make sure we can get deeper if we've not hit this key before
                $arr[$key] = array();
             elseif (!is_array($arr[$key])) 
                // This should never be relevant for well formed input data
                throw new Exception("Setting a value and an array with the same key: $key");
            
            set_nested_value($arr[$key], $keys, $value);
         elseif (empty($key)) 
            // Setting an Array
            $arr[] = $value;
         else 
            // Setting an Object
            $arr[$key] = $value;
        
    

    $input = array();
    $parts = array();

    $rawdata=file_get_contents("php://input");
    $rawdata=urldecode($rawdata);
    $pairs = explode("&", $rawdata);
    foreach ($pairs as $pair) 
        $key_value = explode("=", $pair, 2);
        preg_match_all("/([a-zA-Z0-9_]*)(?:\[([^\[\]]*(?:(?R)[^\[\]]*)*)\])?/", urldecode($key_value[0]), $parts);
        $keys = array($parts[1][0]);
        if (isset($parts[2][0])&&$parts[2][0]!="") 
            array_pop($parts[2]); // Remove the blank one on the end
            $keys = array_merge($keys, $parts[2]);
        
        $value = urldecode($key_value[1]);
        if ($value == "true") 
            $value = true;
         else if ($value == "false") 
            $value = false;
         else if (is_numeric($value)) 
            if (strpos($value, ".") !== false) 
                $num = floatval($value);
             else 
                $num = intval($value);
            
            if (strval($num) === $value) 
                $value = $num;
            
        
        set_nested_value($input, $keys, $value);
    
    return $input;

【讨论】:

【参考方案7】:

我通过搜索找到了这个答案,并且觉得我应该提供一个替代问题。就我而言,我的帖子不是太大,但我在字段中提交的值没有显示在 $_POST 数组中。事实证明,我不小心在表单的下方有另一个同名字段。所以:

<form>
   <input type="text" name="field1">
   <input type="text" name="field2">
   <input type="text" name="field3">
   <input type="text" name="field1">
   <input type="submit">
</form>

$_POST 变量填充来自该表单的数据时,您的第一个字段中的值将被最后一个同名字段中的值覆盖。如果第一个字段是必需的并且您填写了一个值,但最后一个字段不是必需的并且提交为空,您将看到与此问题类似的症状,因为值 $_POST['field1'] 将显示最后一个字段的值表单中的元素是空的。

TLDR:

确保检查表单中的重复字段名称!

【讨论】:

【参考方案8】:

供日后参考: 在 Ubuntu 机器上,我一直在为这个问题苦苦挣扎很久,并且曾经采用与上述类似的解决方法来挽救这一天。 我现在在我的 php.ini 中跟踪了这个问题,最后发现它符合 ma​​x_input_nesting_level = 0 我评论了上面的行,重新启动了 apache,一切都修复了。

【讨论】:

【参考方案9】:

我遇到了同样的问题,结果我使用 AJAX 根据表单中的其他输入动态修改输入字段。 ajax 函数重新创建了输入,但未能包含输入名称。

【讨论】:

【参考方案10】:

在尝试修复 javascript 对象键(包括名称中的方括号)的错误时遇到了这篇(诚然是旧的)帖子,然后 PHP 变得困惑并且表现得好像它甚至从未听说过嵌套。 @Dalin 有一个很好的基本响应,但它不会创建嵌套数组,也不会将值类型转换为布尔值/数字 - 因此我的版本(get_real_post() 也在 GitHub 上)。

尽管有名字,这对于 _GET 应该同样有效(即,php://input 抓取的任何东西)。

/**
 * Gets the _POST data with correct handling of nested brackets:
 * "path[to][data[nested]]=value"
 * "path"
 *    -> "to"
 *       -> "data[nested]" = value
 * @return array
 */
function get_real_post() 

    function set_nested_value(&$arr, &$keys, &$value) 
        $key = array_shift($keys);
        if (count($keys)) 
            // Got deeper to go
            if (!array_key_exists($key, $arr)) 
                // Make sure we can get deeper if we've not hit this key before
                $arr[$key] = array();
             elseif (!is_array($arr[$key])) 
                // This should never be relevant for well formed input data
                throw new Exception("Setting a value and an array with the same key: $key");
            
            set_nested_value($arr[$key], $keys, $value);
         elseif (empty($key)) 
            // Setting an Array
            $arr[] = $value;
         else 
            // Setting an Object
            $arr[$key] = $value;
        
    

    $input = array();
    $parts = array();
    $pairs = explode("&", file_get_contents("php://input"));
    foreach ($pairs as $pair) 
        $key_value = explode("=", $pair, 2);
        preg_match_all("/([a-zA-Z0-9]*)(?:\[([^\[\]]*(?:(?R)[^\[\]]*)*)\])?/", urldecode($key_value[0]), $parts);
        $keys = array($parts[1][0]);
        if (!empty($parts[2][0])) 
            array_pop($parts[2]); // Remove the blank one on the end
            $keys = array_merge($keys, $parts[2]);
        
        $value = urldecode($key_value[1]);
        if ($value == "true") 
            $value = true;
         else if ($value == "false") 
            $value = false;
         else if (is_numeric($value)) 
            if (strpos($value, ".") !== false) 
                $num = floatval($value);
             else 
                $num = intval($value);
            
            if (strval($num) === $value) 
                $value = $num;
            
        
        set_nested_value($input, $keys, $value);
    
    return $input;

【讨论】:

【参考方案11】:

我遇到了同样的问题。我的表单是在循环中创建输入元素。最多发布 1000 个输入元素,print_r($_POST); 显示所有发布的值。

当表单中的输入元素编号在 1000 以上时,print_r($_POST) 就消失了。好像表格根本没有发布。我通过谷歌搜索到达了您的 ***.com 页面。

是的,增加post_max_sizememory_limit 等并没有解决任何问题。我有实际经验,将memory_limit 增加到超过128M 也可能很危险。一旦我们的实时 Apache 托管服务器挂断。我是实验者:-)

max_input_vars 是唯一的限制因素。

echo ini_get('max_input_vars');

显示默认为 1000。 奇怪的是,php.ini 中没有 max_input_vars。 反正我加了

max_input_vars=10000 

在我的 php.ini 中并重新启动 Apache。

现在echo ini_get('max_input_vars'); 显示它已增加到10000,并且我的大型表单数据在发布后可能会被捕获。问题解决了。

我看到 max_input_vars 是 PHP_INI_PERDIR 类型。这意味着我无法使用 ini_set('max_input_vars', 10000); 更改需要更高值的单个 php 页面的值;

【讨论】:

【参考方案12】:

我遇到了类似的问题,修改 MAX_INPUT_VARS 解决了它。

值得检查您发送的内容和收到的内容。就我而言,AJAX 调用包含所有值的格式化数组,但 VAR_DUMP($this->input->post());揭示缺失值。

所以,正如这里有人所说的那样,显而易见的答案是 - max_input_vars。

【讨论】:

【参考方案13】:

您需要更改 php.ini 文件的 max_input_vars 值

第 1 步: 找到 php.ini 文件第 1(a) 步: 打印 php 信息值以找出所有 PHP 预定义配置。 p>

echo phpinfo(); //will print the php info values

步骤 1(b): 记下上一步打印的 phpinfo 值中的 php.ini 文件位置,如下图所示

步骤 2: 编辑 php.ini 文件。步骤 2(a): 找到变量 max_input_vars 并删除它前面的分号并设置值到 10000 如下所示

max_input_vars = 10000

第 3 步:重启 apache

sudo service apache2 restart

【讨论】:

【参考方案14】:

我遇到了同样的问题,我找到了一个非常有价值的解决方案,而不会增加 .ini 中的任何限制或大小。 我们知道默认情况下 max_input_vars = 1000,这是完美的。 只需做一件事连接数组值并尝试将变量数量精确到 1000 以下。 每个变量名称计数一个而不是它们的值。 例如:

<code>
$i = 10; //counts 1
$i = array(1,2,3,4,5); //counts 5
</code>

你可以这样做:

<code>
$i = 1_2_3_4_5; // counts 1
</code>

希望理解。

【讨论】:

以上是关于PHP 缺少一些 $_POST 值,但存在于 php://input的主要内容,如果未能解决你的问题,请参考以下文章

无法访问 php $_POST 值

cs4 中缺少 insertAt 和 removeAt 向量方法,但存在于文档中

能够使用 $_GET 但不能接收数据 $_POST [重复]

php href 传递参数问题

枚举中缺少值或空字符串

PHP - 提交日期/时间/从foreach循环中选择输入值到$ _POST不返回任何内容