【问题标题】:Variable variables handling order: changes in PHP 7变量处理顺序:PHP 7 中的变化
【发布时间】:2016-03-09 14:52:13
【问题描述】:

随着新的 PHP 7.0.0 的发布,我有点担心所谓的“变量”的评估顺序的变化。

this page,在“变量处理的更改”下,显示了一个表格,其中包含表达式示例及其在 PHP 5 和 PHP 7 中的处理顺序。列出的四个表达式是:

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

给定以下字符串和数组:

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

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

但是,据我了解,在 PHP 7 中,相同的表达式将被解释为一个名为 $foo 中的值的变量,因此我希望 PHP 通知用于“数组到字符串的转换”,因为 @987654330 @ 是一个数组。

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

当然,我很好奇为什么 PHP 7 会改变这一点(具体来说,为什么这种改变比向后兼容更重要),但是,这对于 SO 来说不是一个合适的问题。我的问题更实际:

应对这种不兼容性的推荐方法是什么?

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

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

【问题讨论】:

  • 真的那么麻烦吗?您只需要找到$$->$ 的所有实例并在需要的地方添加大括号。如果您有多个比$$foo 更复杂的实例,那么无论如何您的代码都有问题。将其视为重构的机会。 :)
  • P.S.如果您有良好的测试,那么您甚至不需要这样做。看看哪些测试在 5.x 下失败并在 7 下通过。然后修复你的代码。
  • 谢谢 Ed Cottrell,你认为 $$->$::$ 是唯一值得一看的吗?我还想念其他哪些人? (我可以处理误报,不会太多)。
  • Phan 应该会为您识别 UVS 问题以及其他各种潜在的迁移问题
  • @MartenKoetsier 应该这样做。请注意,::$ 相对于其他人来说会给你很多误报。另外,仅供参考,如果您使用 PHPStorm(或想尝试演示),PHPStorm 10 会自动执行很多 PHP 7 兼容性检查。 (我没有隶属关系;我只是喜欢这个产品。)

标签: php migration incompatibility variable-variables php-7


【解决方案1】:

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

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

【讨论】:

  • 不幸的是,Phan 需要 PHP 7+,而我还没有(还没有!)。但是,我查看了他们的兼容性检查源,发现仅引用了$$var[]$foo->$bar['baz'];(都在函数visitDim 中的src/Phan/Analyze/BreadthFirstVisitor.php 中)。这些指的是测试 0047 和 0051。这让我期待搜索 $$->$ (@Ed Cottrell) 确实可以解决问题!
  • 我也搜索了其他检测方法,但没有阳性结果。也许将来会出现一些轻量级的解析器。同时,您的回答至少帮助解决了我的问题,所以现在,它被接受了。谢谢。
【解决方案2】:

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

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

关于“为什么”的原因。统一的变量语法允许我们像使用对象方法的“链接”一样使用数据结构的属性(如数组索引和返回值)。

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

【讨论】:

  • 感谢您的链接。阅读这有助于我更好地理解它。 “为什么”的问题至少得到了回答!最后,我可能确实会手动重构。主要问题是寻找实例,这可能已经通过 Phan 的测试得到充分解决。
【解决方案3】:

使用 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

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

【讨论】:

    【解决方案4】:

    第一步,寻找问题表达方式

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

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

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

    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";
    

    【讨论】:

      猜你喜欢
      • 2012-05-12
      • 2015-08-03
      • 2018-07-26
      • 1970-01-01
      • 2021-04-10
      • 2014-01-22
      • 2010-12-26
      • 1970-01-01
      • 2010-09-17
      相关资源
      最近更新 更多