С выходом нового 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
весь обидный код? Какие еще варианты могут существовать?
Расмус Лердорф написал инструмент статического анализа, который может выявить эти так называемые проблемы с унифицированным переменным синтаксисом, которые называются Phan https://github.com/etsy/phan
Фан имеет возможность -b, --backward-compatibility-checks
проверить наличие потенциальных проблем с PHP 5 -> PHP 7 BC.
https://wiki.php.net/rfc/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
Я добавил комментарии вместо того, чтобы просто удалить &
, чтобы иметь возможность решать ошибки, которые могут возникнуть позже.
Шаг 1, Нахождение выражения проблемы
Это слишком трудно найти с помощью 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-парсер 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";