变量变量处理顺序:变化PHP7

Variable variables handling order: changes in PHP 7

现在新的PHP 7.0.0出来了,有点担心所谓的'variable variables'.

求值顺序的变化

this page 上,在 'Changes to variable handling' 下,显示了一个 table,其中包含表达式示例及其在 PHP 5 和 PHP 7 中的处理顺序。列出的四个表达式是:

$$foo['bar']['baz']
$foo->$bar['baz']
$foo->$bar['baz']()
Foo::$bar['baz']()

给定以下字符串和数组:

$qux = 'quux';
$foo = array('bar' => array('baz' => 'qux'));

table $$foo['bar']['baz'] 中的第一个表达式在 PHP 5 中被解释为以 $foo['bar']['baz'] 中的值命名的变量的值,因此值$qux,也就是'quux'.

然而,在 PHP 7 中,据我了解,相同的表达式将被解释为一个以 $foo 中的值命名的变量,因此我期望 PHP 通知对于 'array to string conversion',因为 $foo 是一个数组。

table 中的其他示例似乎是同一主题的变体。

当然,我很好奇为什么 PHP 7 中会发生这种变化(具体来说,为什么这种变化比向后兼容更重要),但是,这不是 suitable 的问题所以。我的问题比较实际:

解决这种不兼容性的推荐方法是什么?

当然,在有问题的表达式中添加大括号会有所帮助(${$foo['bar']['baz']}$foo->{$bar['baz']}$foo->{$bar['baz']}()Foo::{$bar['baz']}()),但这非常麻烦,将通过大量旧代码,搜索相对较少的事件...

否则,这四个示例是唯一可能的语法变体吗?也就是说,我可以创建一个 RegExp 和 grep 所有违规代码吗?可能存在哪些其他变体?

https://wiki.php.net/rfc/uniform_variable_syntax

您别无选择,只能手动重构它们。除非你能想出一个正则表达式来查找所有使用变量的变量语法。

关于原因"why"。统一的变量语法允许我们使用数据结构的属性(如数组索引和 return 值),就像我们使用 "chaining" 对象方法一样。

变量优先顺序的更改是此增强功能的牺牲品。我认为值得。

Rasmus Lerdorf 编写了一个静态分析工具,可以发现这些所谓的统一变量语法问题,称为 Phan https://github.com/etsy/phan

Phan 有选项 -b, --backward-compatibility-checks 检查潜在的 PHP 5 -> PHP 7 BC 问题。

使用 sed 转换代码以解决 PHP7 统一变量语法问题

您只需要找到 $$::$->$ 的所有实例并在需要的地方添加大括号:

find . -name "*.php"  -exec grep -l '\->$' {} \;|while read f; do
  echo $f;  grep -H '\->$' $f ; 
  # do some sed magic here to add braces
done

find . -name "*.php"  -exec grep -l '$$\w*\[' {} \;|while read f; do 
  echo $f;  grep -H '$$\w*\[' $f ;
  # do some sed magic here to add braces
done

find . -name "*.php"  -exec grep -l '::$' {} \;|while read f; do 
  echo $f;  grep -H '::$' $f ;
  # do some sed magic here to add braces
done

也许有人知道正确的 sed 语法,所以我会在这里添加它。

我已经注释掉对象前的指针实例 &

find . -name "*.php"  -exec grep -l new {} \;|while read f; do
  sed -i -e 's~=\s*\&\s*new~= /*\&*/ new~g' "$f">/tmp/a;
done

我添加了评论而不是仅仅删除了 &,以便能够解决稍后可能发生的错误。

步骤 1,寻找问题表达式

使用grep和一些魔术正则表达式很难找到,因为它有很多因素。

Phan https://github.com/etsy/phan 可以解决它,使用选项 -b, --backward-compatibility 它检查潜在的 PHP 5 -> PHP 7 公元前问题。它可能有点重,因为它会查找常见问题。

如果你想要一个无需配置的工具,你可以试试

PHP-迁移 https://github.com/monque/PHP-Migration.

它会解析代码两次,第一次是PHP 7,第二次是PHP 5。然后比较AST结果中的节点,如果发现不同,意味着当它 运行 在 PHP 5/7 之间时会发生不同的行为,因此您可以导航到此工具报告的行并手动检查代码。

$ cat demo.php
<?php

$$foo['bar']['baz'];
$foo->$bar['baz'];
$foo->$bar['baz']();
Foo::$bar['baz']();

$ php bin/phpmig demo.php

File: demo.php
--------------------------------------------------------------------------------
Found 4 spot(s), 4 identified
--------------------------------------------------------------------------------
    3 | WARNING    | * | 7.0.0 | Different behavior between PHP 5/7
    4 | WARNING    | * | 7.0.0 | Different behavior between PHP 5/7
    5 | WARNING    | * | 7.0.0 | Different behavior between PHP 5/7
    6 | WARNING    | * | 7.0.0 | Different behavior between PHP 5/7
--------------------------------------------------------------------------------

步骤 2,修复它

现在您有一个包含您应该修复的文件和行号的列表。

1、手动修复,慎重测试推荐

2、通过PHP-Parser PrettyPrinter

生成代码
<?php

use PhpParser\ParserFactory;
use PhpParser\PrettyPrinter;

// Parse in PHP 5 mode
$parser = (new ParserFactory())->create(ParserFactory::ONLY_PHP5);
$printer = new PrettyPrinter\Standard();

$code = <<<'EOC'
<?php

$$foo['bar']['baz'];
$foo->$bar['baz'];
$foo->$bar['baz']();
Foo::$bar['baz']();
EOC;

$stmts = $parser->parse($code);
$code = $printer->prettyPrintFile($stmts);
echo $code."\n";