Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/regex/17.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
让PHP停止替换$\u Get或$\u POST数组中的“.”字符?_Php_Regex_Postback - Fatal编程技术网

让PHP停止替换$\u Get或$\u POST数组中的“.”字符?

让PHP停止替换$\u Get或$\u POST数组中的“.”字符?,php,regex,postback,Php,Regex,Postback,如果我用传递PHP变量。通过$\u GET PHP自动将其名称替换为\u个字符。例如: <?php echo "url is ".$_SERVER['REQUEST_URI']."<p>"; echo "x.y is ".$_GET['x.y'].".<p>"; echo "x_y is ".$_GET['x_y'].".<p>"; 。。。我的问题是:我有没有办法阻止这一切?我一辈子都不知道我做了什么才配得到这个 我运行的PHP版本是5.2.4-2ub

如果我用传递PHP变量。通过$\u GET PHP自动将其名称替换为\u个字符。例如:

<?php
echo "url is ".$_SERVER['REQUEST_URI']."<p>";
echo "x.y is ".$_GET['x.y'].".<p>";
echo "x_y is ".$_GET['x_y'].".<p>";
。。。我的问题是:我有没有办法阻止这一切?我一辈子都不知道我做了什么才配得到这个


我运行的PHP版本是5.2.4-2ubuntu5.3。

发生这种情况的原因是PHP的旧register\u globals功能。这个字符不是变量名中的有效字符,因此PHP将其转换为下划线以确保兼容性


简而言之,在URL变量中使用句点不是一个好的做法。

下面是PHP.net对其使用原因的解释:

传入变量名中的点 通常,PHP不会改变 变量的名称,当它们是 传递到脚本中。但是, 应该注意的是,dot周期, 句号不是中的有效字符 PHP变量名。因此,, 看看它:

<?php
$varname.ext;  /* invalid variable name */
?>
现在,什么 解析器看到的是一个名为 $varname,后跟字符串 串联运算符,后跟 裸字符串,即不带引号的字符串 与任何已知的密钥或 保留字“ext”。显然,这 没有达到预期的效果

因此,重要的是 请注意,PHP将自动 替换传入变量中的任何点 带下划线的名称

那是我的

此外,根据这些规则,其他字符将转换为下划线:

PHP转换为uuu下划线的字段名字符的完整列表如下所示,而不仅仅是点:

chr32空间 chr46。点 chr91[开口方括号 chr128-chr159各种
因此,看起来您似乎被它困住了,所以您必须使用我刚才使用的方法将脚本中的下划线转换回点。

早就回答了这个问题,但实际上有一个更好的答案或解决方法。PHP允许您在最短的时间内完成,因此您可以执行以下操作:

$query_string = file_get_contents('php://input');
这将为您提供查询字符串格式的$\u POST数组,句点应为

然后,如果需要,可以按照


对于OpenID参数非常有用,其中包含“.”和“u”,每个参数都有一定的含义!

我对这个问题的解决方案既快又脏,但我仍然喜欢它。我只是想发布一个在表单上检查的文件名列表。我使用base64_encode对标记中的文件名进行编码,然后用base64_encode对其进行解码64_在使用之前先进行解码。

之所以会出现这种情况,是因为在变量名中句点是无效字符,而句点在PHP的实现中非常重要,因此目前还没有简单的修复方法

同时,您可以通过以下方式解决此问题:

通过以下任一方式访问原始查询数据:php://input 用于POST数据或用于GET数据的$\u服务器['QUERY\u STRING'] 使用转换函数。 下面的转换函数PHP>=5.4将每个键值对的名称编码为十六进制表示形式,然后执行常规解析;完成后,它将十六进制名称还原为原始形式:

function parse_qs($data)
{
    $data = preg_replace_callback('/(?:^|(?<=&))[^=[]+/', function($match) {
        return bin2hex(urldecode($match[0]));
    }, $data);

    parse_str($data, $values);

    return array_combine(array_map('hex2bin', array_keys($values)), $values);
}

// work with the raw query string
$data = parse_qs($_SERVER['QUERY_STRING']);

在研究了Rok的解决方案后,我提出了一个版本,该版本解决了我下面的答案、crb的上面以及Rok的解决方案中的局限性。请参阅a

@crb的回答是一个良好的开端,但也存在一些问题

它会重新处理所有内容,这太过分了;只有那些名称中有.的字段需要重新处理。 它无法以与本机PHP处理相同的方式处理数组,例如对于foo.bar[]这样的键。 下面的解决方案解决了这两个问题。请注意,它自最初发布以来已经更新。这比我在测试中的上述答案快50%左右,但不会处理数据具有相同密钥或提取的密钥相同的情况,例如foo.bar和foo_bar都提取为foo_bar

<?php

public function fix2(&$target, $source, $keep = false) {                       
    if (!$source) {                                                            
        return;                                                                
    }                                                                          
    preg_match_all(                                                            
        '/                                                                     
        # Match at start of string or &                                        
        (?:^|(?<=&))                                                           
        # Exclude cases where the period is in brackets, e.g. foo[bar.blarg]
        [^=&\[]*                                                               
        # Affected cases: periods and spaces                                   
        (?:\.|%20)                                                             
        # Keep matching until assignment, next variable, end of string or   
        # start of an array                                                    
        [^=&\[]*                                                               
        /x',                                                                   
        $source,                                                               
        $matches                                                               
    );                                                                         

    foreach (current($matches) as $key) {                                      
        $key    = urldecode($key);                                             
        $badKey = preg_replace('/(\.| )/', '_', $key);                         

        if (isset($target[$badKey])) {                                         
            // Duplicate values may have already unset this                    
            $target[$key] = $target[$badKey];                                  

            if (!$keep) {                                                      
                unset($target[$badKey]);                                       
            }                                                                  
        }                                                                      
    }                                                                          
}                                                                              

这种方法是Rok Kralj的改进版本,但需要进行一些调整,以提高效率,避免对未受影响的密钥进行不必要的回调、编码和解码,并正确处理数组密钥

A是可用的,欢迎任何反馈或建议

public function fix(&$target, $source, $keep = false) {                        
    if (!$source) {                                                            
        return;                                                                
    }                                                                          
    $keys = array();                                                           

    $source = preg_replace_callback(                                           
        '/                                                                     
        # Match at start of string or &                                        
        (?:^|(?<=&))                                                           
        # Exclude cases where the period is in brackets, e.g. foo[bar.blarg]
        [^=&\[]*                                                               
        # Affected cases: periods and spaces                                   
        (?:\.|%20)                                                             
        # Keep matching until assignment, next variable, end of string or   
        # start of an array                                                    
        [^=&\[]*                                                               
        /x',                                                                   
        function ($key) use (&$keys) {                                         
            $keys[] = $key = base64_encode(urldecode($key[0]));                
            return urlencode($key);                                            
        },                                                                     
    $source                                                                    
    );                                                                         

    if (!$keep) {                                                              
        $target = array();                                                     
    }                                                                          

    parse_str($source, $data);                                                 
    foreach ($data as $key => $val) {                                          
        // Only unprocess encoded keys                                      
        if (!in_array($key, $keys)) {                                          
            $target[$key] = $val;                                              
            continue;                                                          
        }                                                                      

        $key = base64_decode($key);                                            
        $target[$key] = $val;                                                  

        if ($keep) {                                                           
            // Keep a copy in the underscore key version                       
            $key = preg_replace('/(\.| )/', '_', $key);                        
            $target[$key] = $val;                                              
        }                                                                      
    }                                                                          
}                                                                              

这个函数的工作是我在2013年暑假提出的一个好主意

它符合标准,并具有深度数组支持,例如a.a[x][b.a]=10。它在后台使用parse_str,并进行一些有针对性的预处理

function fix($source) {
    $source = preg_replace_callback(
        '/(^|(?<=&))[^=[&]+/',
        function($key) { return bin2hex(urldecode($key[0])); },
        $source
    );

    parse_str($source, $post);

    $result = array();
    foreach ($post as $key => $val) {
        $result[hex2bin($key)] = $val;
    }
    return $result;
}
对于低于5.4的PHP:使用base64_编码而不是bin2hex,使用base64_解码而不是hex2bin。

如果想找到任何方法让PHP停止替换$\u get或$\u POST数组中的“.”字符,那么其中一种方法就是修改PHP的源代码,在这种情况下,它相对简单

警告:修改PHP C源代码是一个高级选项

也可以看到这一点,这表明同样的修改

要进行探索,您需要:

下载 禁用。更换检查 /反对 图1、制作并部署定制的PHP版本 源代码更改本身并不重要,只需要更新main/php_变量。c:

.... /*确保变量名中没有空格或点,而不是二进制安全的*/ 对于p=var*Pp++{ 如果*p=''/*| |*p='.*/{ *p=''; .... 注:与原| |*p=='.'相比,'已被注释掉

示例输出:

给定a.a[]=bb&a.a[]=bb&c%20c=dd的查询字符串,
在上面的评论中突出显示了Johan的一个实际答案,我只是将我的整个帖子包装在一个顶级数组中,它完全绕过了这个问题,不需要大量处理

以你所做的形式

<input name="data[database.username]">  
<input name="data[database.password]">  
<input name="data[something.else.really.deep]">  
对我来说,这是一个两行的更改,因为我的视图完全是模板化的


仅供参考。我在字段名中使用点来编辑分组数据的树。

好吧,我下面包含的函数getRealPostArray不是一个很好的解决方案,但它处理数组并支持两个名称:alpha_beta和alpha.beta:

  <input type='text' value='First-.' name='alpha.beta[a.b][]' /><br>
  <input type='text' value='Second-.' name='alpha.beta[a.b][]' /><br>
  <input type='text' value='First-_' name='alpha_beta[a.b][]' /><br>
  <input type='text' value='Second-_' name='alpha_beta[a.b][]' /><br>
var_dump getRealPostArray生成:

Array ( [a.a] => Array ( [0] => bb [1] => BB ) [c_c] => dd )
  'alpha_beta' => 
    array (size=1)
      'a.b' => 
        array (size=4)
          0 => string 'First-.' (length=7)
          1 => string 'Second-.' (length=8)
          2 => string 'First-_' (length=7)
          3 => string 'Second-_' (length=8)
  'alpha.beta' => 
    array (size=1)
      'a.b' => 
        array (size=2)
          0 => string 'First-.' (length=7)
          1 => string 'Second-.' (length=8)
  'alpha_beta' => 
    array (size=1)
      'a.b' => 
        array (size=2)
          0 => string 'First-_' (length=7)
          1 => string 'Second-_' (length=8)
该功能的价值在于:

function getRealPostArray() {
  if ($_SERVER['REQUEST_METHOD'] !== 'POST') {#Nothing to do
      return null;
  }
  $neverANamePart = '~#~'; #Any arbitrary string never expected in a 'name'
  $postdata = file_get_contents("php://input");
  $post = [];
  $rebuiltpairs = [];
  $postraws = explode('&', $postdata);
  foreach ($postraws as $postraw) { #Each is a string like: 'xxxx=yyyy'
    $keyvalpair = explode('=',$postraw);
    if (empty($keyvalpair[1])) {
      $keyvalpair[1] = '';
    }
    $pos = strpos($keyvalpair[0],'%5B');
    if ($pos !== false) {
      $str1 = substr($keyvalpair[0], 0, $pos);
      $str2 = substr($keyvalpair[0], $pos);
      $str1 = str_replace('.',$neverANamePart,$str1);
      $keyvalpair[0] = $str1.$str2;
    } else {
      $keyvalpair[0] = str_replace('.',$neverANamePart,$keyvalpair[0]);
    }
    $rebuiltpair = implode('=',$keyvalpair);
    $rebuiltpairs[]=$rebuiltpair;
  }
  $rebuiltpostdata = implode('&',$rebuiltpairs);
  parse_str($rebuiltpostdata, $post);
  $fixedpost = [];
  foreach ($post as $key => $val) {
    $fixedpost[str_replace($neverANamePart,'.',$key)] = $val;
  }
  return $fixedpost;
}

使用crb时,我希望整体重新创建$\u POST数组,但请记住,您仍然必须确保在客户端和服务器上正确编码和解码。了解字符何时真正无效以及何时真正有效非常重要。此外,在使用I之前,人们仍应始终转义客户端数据使用任何数据库命令都不会出现异常


我建议仅在个别情况下使用此选项,但我不确定将此选项放在主头文件顶部的负面影响。

我当前基于上一个主题回复的解决方案:

function parseQueryString($data)
{
    $data = rawurldecode($data);   
    $pattern = '/(?:^|(?<=&))[^=&\[]*[^=&\[]*/';       
    $data = preg_replace_callback($pattern, function ($match){
        return bin2hex(urldecode($match[0]));
    }, $data);
    parse_str($data, $values);

    return array_combine(array_map('hex2bin', array_keys($values)), $values);
}

$_GET = parseQueryString($_SERVER['QUERY_STRING']);

启用register\u globals也不是一个好主意。事实上,如果可能的话,应该立即禁用它。register\u globals实际上是禁用的,这是PHP5中的默认值。>字符不是变量名中的有效字符。不幸的是,我不想将其用作变量名,我将其作为$\u GET字典中的键保留,因此PHP中的“深思熟虑”并没有增加任何价值:-啊,好吧……不管register\u globals是开还是关。PHP仍然执行替换。要使用GET参数替换file\u GET\u contentsphp://input 使用$\u服务器['QUERY\u STRING'],您可以使用$\u服务器['cookies']对cookies执行同样的操作这是一个很好的开始,但它有几个问题。它不处理数组值,例如foo.bar[]=blarg不会作为数组结束,它将作为称为foo.bar[]的标量变量结束。它也有很多开销,因为它会重新处理所有值,而不管其中是否有句点。请参阅,这解决了Rok实现中的问题。出于某种原因,$query\u string=file\u get\u contents'php://input“;为我返回一个空字符串。如果需要将此字符串用于其他w通过发送,我确实需要变量中的uu?@Rob我根据您的问题添加了输出;它按预期工作,因为我不碰下划线。注意:这是一个经过编辑的解决方案,稍后复制了我的代码,我的想法请参见更改日志。它应该由版主删除。显然,它足够好,您可以使用bin我的想法是2hex,那么我们可以放弃这个毫无意义的宿怨吗?好吧,我只是用它来代替base64编码。好处?什么都没有,除了一点加速。为什么要编辑一个完美的解决方案来复制别人的?-1.为什么?1.空格%20也是一个特殊字符,可以转换为下划线。2.你的代码预处理所有数据,因为即使你说你不需要,所有的人都必须扫描所有的东西。3.你的代码在这样的例子中失败:a.b[10]=11.关于空间,你是对的,谢谢。我的解释已经指出我的方法不处理数组,所以我不太清楚你为什么要指出。preg_match_都必须处理一个字符串,而不是提取和重新处理所有未受影响的键和值,所以你也有点偏离轨道。也就是说,你使用PAR的方法e_string看起来是一种有趣的方法,稍加调整可能会更好:你说你只提取受影响的密钥,但就计算复杂性而言,你没有。你是说,因为你有某种随机访问权,只提取受影响的密钥,但即使没有受影响的密钥,你也必须访问整个me莫莉。如果你有一篇文章有100兆的数据,你提取什么并不重要,这两种方法都是线性的。事实上,你使用了上面提到的In_数组函数,这会让复杂度变得更糟。我只看了一次100兆,没有把它分开,这会立即使内存加倍,然后再把它分开,再加倍在crb的方法中,我也比较了这一点。大O表示法根本没有考虑内存使用,而且这个实现在_数组中也没有使用。另外,如果您想运行一些测试,您会注意到abov

e仍然显著更快;不是在^2上,而是在^2上,但一种线性方法仍然可以比另一种更快。。。而这个是,;这种方法的另一个主要优点是,在根本没有工作要做的情况下,速度优势最大,即没有为钥匙提供句点或空格;这意味着,如果您将它放入处理所有请求中,它的开销最小,因为与多次提取和编码所有密钥相比,一个正则表达式几乎不起作用;下面的其他答案确实为原始问题提供了答案。@ElYobo,@JeremyRuten;很好地解释了原因?我正在使用PHP5.4,PHP仍然在这样做。我也想知道为什么它还没有被弃用。我只能看到保留它的两个原因;register_globals从5.3开始就不推荐使用,为了方便手动执行register globals所做的操作,在这种情况下,应该由执行此操作的人员负责映射他们认为适合IMO的var名称。我假设向后兼容?好的一点,随着全球注册的方式渡渡鸟这一奇怪的功能可以同样去。有了php7,全球注册已经骑到日落,但问题仍然存在。繁荣这对我来说非常有效,谢谢El Yobo/Rok。在CodeIgniter 2.1.3项目中使用它。我会注意到,如果输入的值没有%20个实体,例如“Some Key=Some Value”,那么这个函数的输出是“Some_Key=Some Value”,也许可以调整正则表达式?可以调整正则表达式以捕获未经url编码的空格。。。但是,如果您的源代码尚未进行url编码,那么可能会有其他问题,因为处理总是对字符串进行解码和编码,那么parse_str调用将再次进行url解码。您正在尝试解析哪些尚未编码的内容?谢谢您的属性。不过,我可能会警告您,您的代码可能会执行得更差,因为POST通常只有几百个字节。我更喜欢这里的简单。你在什么地方建立了基准吗?我很想看看它在哪种情况下更慢,因为我测试过的所有东西的速度都在你的速度和两倍之间。我怀疑不同之处在于测试的对象类型:你可以很容易地在我的要点中添加一些计时检查,看看它是如何进行的,为什么不将你的内容与相同的输入进行比较,并发布结果和时间?谢谢。如果您有时间,也请为深数组a[2][5]更新它。@Johan,深数组可以工作。[2][5]=10产生array1{[a]=>array1{[2]=>array1{[5]=>string2 10}}}。哦,我知道了,它确实产生了,只是测试了一下。Php不转换数组索引中的点等,只有数组名的顶层有问题:Php_触及了这个[nochangeshere][nochangeshere]。伟大的谢谢。我很想看看你们的基准测试,因为这和我几个月前做的测试有冲突。此外,我刚刚遇到过这样一种情况,即我需要在已发布的文件字段中处理句点,而这些字段还没有地址;有什么想法吗?你很快就会看到,目前没有时间,但你可以展示你的想法。*文件上载需要multipart/form数据类型,该数据类型不会传递给php://input. 因此,这仍然是非常黑客的事情。请看:无论如何,你永远不应该这样做,+1的努力。确实是非常优雅和实用的解决方案,附带的好处是保持表单数据良好的名称空间。这完全解决了问题,应该是被接受的答案。请添加一些解释,这将有助于所有阅读你的答案的人。。。你为什么不把所有的点转换成某种标记,比如说,转换成~~然后发布呢?收到VAR后,您可以将其重新转换回来。。这是因为有时我们需要贴下划线。。如果从检索查询本身将所有的“to.s”重新转换,我们将失去它们。您可以像concatfirstname一样将用户名“concatfirstname”和“lastname”合并为用户名。@Kaspar Mary。。。数据库设置为包含“用户名”和“状态”列,用户名存储为firstname.lastname,因此我不能在sql中使用任何concat,因为它们已准备好用concat表示。@谢谢您的评论!在Rob有趣的问题上,为什么没有删除注释
$posdata = $_POST['data'];
  <input type='text' value='First-.' name='alpha.beta[a.b][]' /><br>
  <input type='text' value='Second-.' name='alpha.beta[a.b][]' /><br>
  <input type='text' value='First-_' name='alpha_beta[a.b][]' /><br>
  <input type='text' value='Second-_' name='alpha_beta[a.b][]' /><br>
  'alpha_beta' => 
    array (size=1)
      'a.b' => 
        array (size=4)
          0 => string 'First-.' (length=7)
          1 => string 'Second-.' (length=8)
          2 => string 'First-_' (length=7)
          3 => string 'Second-_' (length=8)
  'alpha.beta' => 
    array (size=1)
      'a.b' => 
        array (size=2)
          0 => string 'First-.' (length=7)
          1 => string 'Second-.' (length=8)
  'alpha_beta' => 
    array (size=1)
      'a.b' => 
        array (size=2)
          0 => string 'First-_' (length=7)
          1 => string 'Second-_' (length=8)
function getRealPostArray() {
  if ($_SERVER['REQUEST_METHOD'] !== 'POST') {#Nothing to do
      return null;
  }
  $neverANamePart = '~#~'; #Any arbitrary string never expected in a 'name'
  $postdata = file_get_contents("php://input");
  $post = [];
  $rebuiltpairs = [];
  $postraws = explode('&', $postdata);
  foreach ($postraws as $postraw) { #Each is a string like: 'xxxx=yyyy'
    $keyvalpair = explode('=',$postraw);
    if (empty($keyvalpair[1])) {
      $keyvalpair[1] = '';
    }
    $pos = strpos($keyvalpair[0],'%5B');
    if ($pos !== false) {
      $str1 = substr($keyvalpair[0], 0, $pos);
      $str2 = substr($keyvalpair[0], $pos);
      $str1 = str_replace('.',$neverANamePart,$str1);
      $keyvalpair[0] = $str1.$str2;
    } else {
      $keyvalpair[0] = str_replace('.',$neverANamePart,$keyvalpair[0]);
    }
    $rebuiltpair = implode('=',$keyvalpair);
    $rebuiltpairs[]=$rebuiltpair;
  }
  $rebuiltpostdata = implode('&',$rebuiltpairs);
  parse_str($rebuiltpostdata, $post);
  $fixedpost = [];
  foreach ($post as $key => $val) {
    $fixedpost[str_replace($neverANamePart,'.',$key)] = $val;
  }
  return $fixedpost;
}
<?php
unset($_POST);
$_POST = array();
$p0 = explode('&',file_get_contents('php://input'));
foreach ($p0 as $key => $value)
{
 $p1 = explode('=',$value);
 $_POST[$p1[0]] = $p1[1];
 //OR...
 //$_POST[urldecode($p1[0])] = urldecode($p1[1]);
}
print_r($_POST);
?>
function parseQueryString($data)
{
    $data = rawurldecode($data);   
    $pattern = '/(?:^|(?<=&))[^=&\[]*[^=&\[]*/';       
    $data = preg_replace_callback($pattern, function ($match){
        return bin2hex(urldecode($match[0]));
    }, $data);
    parse_str($data, $values);

    return array_combine(array_map('hex2bin', array_keys($values)), $values);
}

$_GET = parseQueryString($_SERVER['QUERY_STRING']);