У меня есть большая коллекция php-файлов, написанных на протяжении многих лет, и мне нужно правильно заменить все короткие открытые теги на правильные явные открытые теги.
change "<?" into "<?php"
Я думаю, что это регулярное выражение будет правильно выбирать их:
<\?(\s|\n|\t|[^a-zA-Z])
который заботится о таких случаях, как
<?// <?/*
но я не уверен, как обрабатывать дерево всей папки и обнаруживать расширение .php-файла и применять регулярное выражение и сохранять файл после его изменения.
У меня такое чувство, что это может быть довольно просто, если вы освоите правильные инструменты. (В руководстве sed есть интересный взлом: 4.3 Пример / Переименовать файлы в нижний регистр ).
Возможно, я ошибаюсь.
Или, может быть, это может быть oneliner?
не используйте регулярные выражения для синтаксического анализа формальных языков – вы всегда будете сталкиваться с стогами сена, которые вы не ожидали. как:
<? $bla = '?> now what? <?';
безопаснее использовать процессор, который знает о структуре языка. для html это будет xml-процессор; для php – встроенное расширение токенизатора . он имеет T_OPEN_TAG
парсера T_OPEN_TAG
, который соответствует <?php
, <?
или <%
, и T_OPEN_TAG_WITH_ECHO
, который соответствует <?=
или <%=
. чтобы заменить все короткие открытые теги, вы найдете все эти маркеры и замените T_OPEN_TAG
на <?php
и T_OPEN_TAG_WITH_ECHO
с помощью <?php echo
.
реализация остается как упражнение для читателя 🙂
EDIT 1 : ringmaster был настолько добр, чтобы обеспечить его .
EDIT 2 : в системах с short_open_tag
отключен в php.ini
, <?
, <%
и <?=
не будут распознаны с помощью сценария замены. чтобы сценарий работал над такими системами, включите short_open_tag
через опцию командной строки:
php -d short_open_tag=On short_open_tag_replacement_script.php
ps man-страница для token_get_all () и googleing для творческих комбинаций токенизатора , token_get_all и имен токенов парсера.
pps см. также Regex для синтаксического анализа define () содержимого, возможно? здесь на SO
Если вы используете параметр токенизатора, это может быть полезно:
$content = file_get_contents($file); $tokens = token_get_all($content); $output = ''; foreach($tokens as $token) { if(is_array($token)) { list($index, $code, $line) = $token; switch($index) { case T_OPEN_TAG_WITH_ECHO: $output .= '<?php echo '; break; case T_OPEN_TAG: $output .= '<?php '; break; default: $output .= $code; break; } } else { $output .= $token; } } return $output;
Обратите внимание, что токенизатор не будет правильно маркировать короткие теги, если короткие теги не включены. То есть вы не можете запустить этот код в системе, где короткие теги не работают. Вы должны запустить его в другом месте, чтобы преобразовать код.
Это утилита, которую я написал, которая преобразует источник PHP, который содержит короткие открытые теги и заменяет их длинными тегами.
т.е. он преобразует код следующим образом:
<?= $var1 ?> <? printf("%u changes\n",$changes) ?>
К этому
<?php echo $var1 ?> <?php printf("%u changes\n",$changes) ?>
Параметр -skip-echo-tags заставит его пропускать теги <? = И заменять <? теги.
Он написан как скрипт PHP-CLI и требует, чтобы файл CLI php.ini устанавливался так, чтобы разрешать короткие короткие открытые теги. Это значение по умолчанию для PHP 5.3.0 и более ранних версий, но оно может не всегда оставаться таким. (Сценарий просто ничего не изменит, если параметр не включен.)
Мой предыдущий ответ, который я только что написал с помощью sed wont work, sed слишком слаб для такого рода вещей IMO.
Таким образом, я взломал perl-скрипт, который должен сделать трюк, и, надеюсь, он будет доступен для редактирования.
#!/usr/bin/perl use strict; use warnings; use File::Find::Rule; use Carp; my @files = File::Find::Rule->file()->name('*.php')->in('/tmp/foo/bar'); for my $file (@files) { rename $file, $file . '.orig'; open my $output, '>', $file or Carp::croak("Write Error with $file $! $@ "); open my $input, '<', $file . '.orig' or Carp::croak("Read error with $file.orig $! $@"); while ( my $line = <$input> ) { # Replace <?= with <?php echo $line =~ s/<\?=/<?php echo /g; # Replace <? ashded with <?php ashed $line =~ s/<\?(?!php|xml)/<?php /g; print $output $line; } close $input or Carp::carp(" Close error with $file.orig, $! $@"); close $output or Carp::carp(" Close error with $file , $! $@"); unlink $file . '.orig'; }
Но заметьте, я не тестировал это на каком-либо реальном коде, так что он мог пойти «Bang».
Я бы порекомендовал вам, что ваш код был изменен (подождите, его уже отредактировал, правильно? .. правильно?) И запустите тестовый пакет (не говорите мне, что у вас нет тестов!) На измененном коде, потому что вы можете не уверен, что он делает правильные вещи без полноценного парсера FSM.
Я собираюсь упорядочить ваше регулярное выражение для целей этого в то, что может работать лучше, но я могу ошибаться, так как я не тестировал его на каком-либо реальном коде.
Предположим, вы сидите в базовом каталоге вашего кода, вы можете начать с:
find . -iname "*.php" -print0
Это даст вам все .php-файлы, разделенные символами NULL, что необходимо, если у любого из них есть пробелы.
find . -iname "*.php" -print0 | xargs -0 -I{} sed -n 's/\(<\?\)\([^a-zA-Z]\)/\1php\2/gp' '{}'
Это должно сделать вам большую часть пути. Он найдет все файлы, затем для каждого из них запустит sed, чтобы заменить код. Однако без тега -i (используется ниже) это фактически не коснется ваших файлов, оно просто отправит ваш код на ваш терминал. Параметр -n подавляет нормальный выход, а p после того, как часть регулярного выражения сообщает ему распечатать только строки, которые изменились.
Хорошо, если ваши результаты выглядят правильно, то вы делаете большой шаг, который заменяет файлы на месте. Вы должны обязательно создать резервную копию всех своих файлов, прежде чем пытаться это сделать!
find . -iname "*.php" -print0 | xargs -0 -I{} sed -i 's/\(<\?\)\([^a-zA-Z]\)/\1php\2/g' '{}'
Это должно было сделать работу. К сожалению, у меня нет файлов PHP, лежащих вокруг, которые используют этот синтаксис, поэтому вы сами можете разобраться с этим, но, надеюсь, механика, связанная с проделанной работой, немного понятна:
Это моя версия RegExp:
<\?(?!(php|=|xml))(\s|\t|\n)
Я использовал сценарий danorton почти для 2000 файлов, и он работал как шарм
Я поместил его скрипт в файл с именем «fixtags.php» и использовал следующий linux-1 liner для решения проблемы:
find . -iname "*.php" | xargs php fixtags.php --overwrite
единственная проблема, с которой я столкнулся, – это когда он столкнулся с файлом размером 0 байт.
Проблема была решена как фиксатор в инструменте php-cs-fixer
, который может быть легко установлен и который протестирован и поддерживается.
Крепление тогда легко:
$ php-cs-fixer fix --fixers=short_tag --diff --dry-run <path>
Просто замените <path>
на путь к каталогу или файлу, который вы хотите изменить. Приведенная команда должна сначала просмотреть ( --dry-run
и --diff
параметры).
Установка так же проста, как и
$ composer global require friendsofphp/php-cs-fixer
если у вас есть композитор, установленный с глобальным каталогом каталога композитора на вашем пути (рекомендуется).
Я должен был пройти через это раньше, и я нашел, что лучше всего делать это поэтапно. Плохой скрипт, который пытается поймать все это, может испортить много файлов.
Я использовал Coda (или любой другой веб-редактор), чтобы сделать простую находку и заменить на очень конкретные строки.
Например, начиная с "
Это может показаться немного утомительным, но я был уверен, что что-то не путается где-то, о чем я не знал. Возвращение – настоящая боль.
Для страниц XML / XHTML типично включать следующий код:
<?php echo '<?xml version="1.0" encoding="UTF-8" ?>'; ?>
Конечно, это не должно меняться ни к:
<?phpphp echo '<?phpxml version="1.0" encoding="UTF-8" ?>'; ?>
ни:
<?php echo '<?phpxml version="1.0" encoding="UTF-8" ?>'; ?>
К сожалению, автоматические решения могут не работать. Моя рекомендация:
1) Используйте grep, чтобы найти все короткие теги:
grep -rn "<?[^p]" *
2) Пройдите через каждый файл и строку и исправьте вручную
Я понимаю, что это не может быть жизнеспособным решением, если у вас есть огромный проект, но для меня это сработало хорошо.
Вот однострочный perl:
perl -pi -w -e 's/\<\?/\<\?php/g;' *php
Используйте контроль версий, чтобы выполнить и принять решение сохранить изменения или нет.