如何使用正则表达式在PHP代码中捕获未引用的数组索引并将其引用?

时间:2019-05-03 13:44:42

标签: php regex

PHP 7.2将未定义的常量错误从通知升级为警告,并建议以后它们将返回完整错误。

我正在尝试找到一种通过脚本(最好是通过正则表达式)修复这些问题的方法,我可以运行该正则表达式来解析站点上的每个PHP文件,查找所有令人讨厌的代码,并对其进行修复。

我找到了多个示例来解决一个变体,但没有一个解决另一个变体,这就是我正在寻求帮助的一个例子。

这是一个示例文件:

<?php

$array[foo] = "bar"; 
// this should become 
// $array['foo'] = "bar"

echo "hello, my name is $array[foo] and it's nice to meet you"; 
// would need to become 
// echo "hello, my name is " . $array['foo'] . " and it's nice to meet you";

?>

我已经看到了很多用于标识和更改第一种类型的选项,但是对于第二种类型则没有,因为未定义的常量位于字符串中。在那种情况下,解析器将需要:

  1. $array[foo]替换为$array['foo']
  2. 找到整个变量,事先用引号引起来,并放置一个。两侧,然后再重新打开报价

编辑:理想情况下,一个正则表达式可以一遍处理示例代码中的两个示例-即添加对号,并且如果标识在字符串内,则还添加引号/点。

2 个答案:

答案 0 :(得分:1)

$array[foo] = "bar"; 
// this should become 
// $array['foo'] = "bar"

是的,这始终会触发通知,并且一直是不良做法。

echo "hello, my name is $array[foo] and it's nice to meet you"; 
// would need to become 
// echo "hello, my name is " . $array['foo'] . " and it's nice to meet you";

否,此样式从未触发通知,现在也不会触发。实际上,PHP文档中的it's used as an example。 PHP永远不会删除在字符串中插入数组变量的功能。


您的第一个案例很容易抓住这样的东西:

$str = '$array[foo] = "bar";';
echo preg_replace("/(\\$[a-z_][a-z0-9_]*)\\[([a-z][a-z0-9_]*)\\]/", "$1['$2']", $str);

但是当然只需要在字符串之外捕获。

与任何复杂的语法一样,正则表达式永远不会像语法特定的解析器那样可靠。由于您正在解析PHP代码,因此最准确的解决方案是使用PHP's own token parser

$php = <<< 'PHP'
<?php
$array[foo] = "bar"; // this line should be the only one altered.
$array['bar'] = "baz";
echo "I'm using \"$array[foo]\" and \"$array[bar]\" in a sentence";
echo 'Now I\'m not using "$array[foo]" and "$array[bar]" in a sentence';
PHP;

$tokens = token_get_all($php);
$in_dq_string = false;
$last_token = null;
$output = "";

foreach ($tokens as $token) {
    if ($last_token === "[" && is_array($token) && $token[0] === 319 && !$in_dq_string) {
        $output .= "'$token[1]'";
    } elseif (is_array($token)) {
        $output .= $token[1];
    } else {
        if ($token === "\"") {
            $in_dq_string = !$in_dq_string;
        }
        $output .= $token;
    }
    $last_token = $token;
}

echo $output;

输出:

<?php
$array['foo'] = "bar"; // this line should be the only one altered.
$array['bar'] = "baz";
echo "I'm using \"$array[foo]\" and \"$array[bar]\" in a sentence";
echo 'Now I\'m not using "$array[foo]" and "$array[bar]" in a sentence';

此代码需要考虑一些边缘情况,例如,当您有意使用常量作为数组索引时。

答案 1 :(得分:0)

这并不完美,但可以安全运行多次(example

$str = 'echo "hello, my name is $array[foo] and it\'s nice to meet you";';
echo preg_replace_callback('/\".*(\$.*\[[^\'].*[^\']\]).*\"/', function($match) {
    $search = ['[', ']'];
    $replace = ["['", "']"];
    $array = '" . ' . str_replace($search, $replace, $match[1]) . ' . "';

    return str_replace($match[1], $array, $match[0]);
}, $str);

正则表达式的作用是将其自身限制为双引号字符串(\")。然后,我们寻找$var[val],不带记号'。捕获到它之后,就可以通过执行两阶段str_replace的回调来运行它。第一个使用匹配的正则表达式将匹配的$var[val]用双引号引起来并插入刻度线,第二个使用找到的正则表达式匹配项将其插入整个字符串中

它不会做得很好。如果您有$array[foo] $array[bar],它将以

结束
" . $array['foo'] . "" . $array['bar'] . "

漂亮,但仍然有效的代码