变量变量处理顺序:变化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、手动修复,慎重测试推荐
生成代码
<?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";
现在新的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、手动修复,慎重测试推荐
生成代码<?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";