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 表单使用数组语法(例如<input name="user[1]" type="text" />
)。它在这种形式下工作正常($_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_memory
和 max_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 中跟踪了这个问题,最后发现它符合 max_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_size
、memory_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的主要内容,如果未能解决你的问题,请参考以下文章
cs4 中缺少 insertAt 和 removeAt 向量方法,但存在于文档中