Порядок обработки переменных: изменения в PHP 7
С выходом нового PHP 7.0.0 я немного обеспокоен изменениями в порядке оценки так называемых "переменных переменных".
На этой странице в разделе "Изменения в обработке переменных" отображается таблица с примерами выражений с порядком их обработки в 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-уведомление для преобразования массива в строку, так как $foo
это массив.
Другие примеры в таблице, кажется, являются вариацией этой же темы.
Конечно, мне любопытно, почему это изменилось в PHP 7 (в частности, почему это изменение важнее, чем обратная совместимость), однако, этот вопрос не подходит для SO. Мой вопрос более практичный:
Каков был бы рекомендуемый способ справиться с этой несовместимостью?
Конечно, добавление фигурных скобок в оскорбительные выражения поможет (${$foo['bar']['baz']}
, $foo->{$bar['baz']}
, $foo->{$bar['baz']}()
а также Foo::{$bar['baz']}()
), но это очень громоздко, просматривая тонны старого кода, ища относительно немного случаев...
Иначе, являются ли эти четыре примера единственно возможными вариациями синтаксиса? То есть я могу создать RegExp и grep
весь обидный код? Какие еще варианты могут существовать?
4 ответа
Расмус Лердорф написал инструмент статического анализа, который может выявлять эти так называемые проблемы с унифицированным синтаксисом переменных, который называется Phan https://github.com/etsy/phan
Фан имеет возможность -b, --backward-compatibility-checks
проверить наличие потенциальных проблем с PHP 5 -> PHP 7 BC.
https://wiki.php.net/rfc/uniform_variable_syntax
У вас действительно нет выбора, кроме как перефакторировать их вручную. Если вы не можете придумать регулярное выражение, чтобы найти применение синтаксису переменной переменной.
По поводу причины "почему". Унифицированный синтаксис переменных позволяет нам использовать свойства структур данных (например, индексы массива и возвращаемые значения) так же, как мы используем цепочку методов объекта.
Изменение порядка приоритета переменной переменной было жертвой этого улучшения. Стоит это по моему мнению.
Шаг 1, Поиск выражения проблемы
Это слишком трудно найти с помощью grep
и немного магического регулярного выражения, потому что у него много факторов.
Фан 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";
Преобразуйте ваш код с sed
решить проблемы PHP7 Uniform Variable Syntax
Вам просто нужно найти все экземпляры $$
, ::$
а также ->$
и добавьте фигурные скобки, где это необходимо:
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
Я добавил комментарии вместо того, чтобы просто удалить &
, чтобы иметь возможность решать ошибки, которые могут возникнуть позже.