Пакетный скрипт для замены коротких открытых тегов PHP на <? Php - PullRequest
17 голосов
/ 26 марта 2009

У меня есть большая коллекция php-файлов, написанных за эти годы, и мне нужно правильно заменить все короткие открытые теги на соответствующие явные открытые теги.

change "<?" into "<?php"

Я думаю, что это регулярное выражение правильно выберет их:

<\?(\s|\n|\t|[^a-zA-Z])

, который заботится о таких случаях, как

<?//
<?/*

но я не уверен, как обработать все дерево папок и определить расширение .php, применить регулярное выражение и сохранить файл после его изменения.

У меня такое чувство, что это может быть довольно просто, если вы освоите правильные инструменты. (В руководстве по sed есть интересный взлом: 4.3 Пример / Переименование файлов в нижний регистр ).

Может быть, я ошибаюсь.
Или, может быть, это может быть oneliner?

Ответы [ 13 ]

17 голосов
/ 26 марта 2009

не используйте регулярные выражения для анализа формальных языков - вы всегда будете сталкиваться с стогами сена, которые вы не ожидали. как:

<?
$bla = '?> now what? <?';

безопаснее использовать процессор, который знает о структуре языка. для html это будет процессор xml; для php встроенное расширение tokenizer . у него есть токен синтаксического анализатора T_OPEN_TAG, который соответствует <?php, <? или <% и T_OPEN_TAG_WITH_ECHO, что соответствует <?= или <%=. чтобы заменить все короткие открытые теги, вы найдете все эти токены и замените T_OPEN_TAG на <?php и T_OPEN_TAG_WITH_ECHO на <?php echo.

реализация оставлена ​​в качестве упражнения для читателя:)

РЕДАКТИРОВАТЬ 1 : Ringmaster был так любезен предоставить один .

EDIT 2 : в системах с short_open_tag, отключенными в php.ini, <?, <% и <?=, не распознаются Сценарий замены. чтобы скрипт работал в таких системах, включите short_open_tag с помощью параметра командной строки:

php -d short_open_tag=On short_open_tag_replacement_script.php

p.s. справочная страница для token_get_all () и поиск творческих комбинаций tokenizer , token_get_all , и имена токенов анализатора могут помочь.

p.p.s. см. также Regex для анализа содержимого define (), возможно? здесь, на SO

14 голосов
/ 30 октября 2009

Если вы используете опцию токенизатора, это может быть полезно:

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

Обратите внимание, что токенизатор не будет правильно маркировать короткие теги, если короткие теги не включены. То есть вы не можете запустить этот код в системе, где короткие теги не работают. Вы должны запустить его в другом месте, чтобы преобразовать код.

5 голосов
/ 25 октября 2010

Это утилита, которую я написал, которая преобразует исходный код PHP, который содержит короткие открытые теги, и заменяет их длинными тегами.

т.е. он преобразует код следующим образом:

  <?= $var1 ?>
  <? printf("%u changes\n",$changes) ?>

К этому

  <?php echo $var1 ?>
  <?php printf("%u changes\n",$changes) ?>

Опция - skip-echo-tags заставит его пропускать теги <? = </strong> и заменять только теги <? </strong>.

Он написан в виде сценария PHP-CLI и требует, чтобы файл CLI php.ini был установлен для разрешения коротких коротких открытых тегов. Это настройка по умолчанию для PHP 5.3.0 и более ранних версий, но она не всегда может оставаться такой. (Сценарий просто ничего не изменит, если настройка не включена.)

4 голосов
/ 06 ноября 2016

Проблема была решена с помощью fixer в инструменте 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

если у вас установлен composer с глобальным каталогом bin для composer в вашем пути (рекомендуется).

2 голосов
/ 26 марта 2009

Мой предыдущий ответ, который я только что переписал с sed, не работает, 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';
}

Но обратите внимание, я не проверял это ни на одном реальном коде, так что он может пойти "Взрыв".

Я бы порекомендовал вам пересмотреть ваш код (подождите, он уже пересмотрен, верно? .. верно?) И запустить ваш набор тестов (не говорите, что у вас нет тестов!) На измененном коде потому что вы не можете быть уверенным , он делает правильные вещи без полноценного парсера FSM.

1 голос
/ 11 декабря 2011

Я использовал сценарий Данортона почти для 2000 файлов, и он работал как шарм

Я поместил его скрипт в файл с именем "fixtags.php" и использовал следующий linux 1 liner для решения проблемы:

find . -iname "*.php" | xargs php fixtags.php --overwrite

Единственная проблема, с которой я столкнулся, это когда он столкнулся с файлом нулевого размера.

1 голос
/ 27 января 2010

Это моя версия RegExp:

<\?(?!(php|=|xml))(\s|\t|\n)
1 голос
/ 26 марта 2009

Я собираюсь упростить ваше регулярное выражение для целей этого в то, что может работать лучше, но я могу ошибаться, так как я не проверял его ни на одном реальном коде.

Допустим, вы сидите в базовой директории вашего кода, вы можете начать с:

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-файлов, использующих этот синтаксис, так что вы сами можете выяснить это отсюда, но, надеюсь, механизм достижения цели немного яснее:

  1. Захватите все файлы с помощью команды "найти"
  2. Отправьте этот список файлов в «xargs» (который выполняет какую-то команду для файлов по одному
  3. Используйте «sed» и синтаксис «s / to-change / change /», чтобы применить магию регулярных выражений!
0 голосов
/ 26 апреля 2019

PHP 7.4 официально отвергает короткие открытые теги, а PHP 8 полностью их удаляет, поэтому этот вопрос о SO станет несколько популярным, когда люди будут искать решения для преобразования устаревших кодовых баз.

Как уже отмечалось в других ответах, sed не охватывает все варианты использования. Предлагаемый full_opening_tag PHP-CS-Fixer очень похож на свое поведение и также не охватывает все варианты использования. Кроме того, по крайней мере один инструмент, который я нашел, такой как один ответ от danorton, в настоящее время работает только при включенных коротких открытых тегах, которые, если вы обновили до PHP 8 через обновление ОС, вы не сможете легко откатиться до 7.x для запуска такого инструменты. Caveat Emptor очень применим ко всем этим подходам.

Я написал инструмент, который не зависит от существования коротких открытых тегов (т.е. он работает с PHP 8), не использует регулярные выражения (т.е. он использует token_get_all()), а также избегает использования коротких открытых тегов (например, <?xml) и другие сценарии без тегов (например, строки PHP, содержащие "теги").

https://github.com/cubiclesoft/php-short-open-tag-finder/

Режим по умолчанию, в котором работает инструмент, просто находит ссылки и отображает их. Файлы не изменены.

В режиме -ask, который в настоящее время является единственным режимом, который изменяет файлы, инструмент спрашивает, можно ли заменить каждый набор ссылок для каждого файла отдельно. То есть, если существует 500 файлов с 2 000 ссылок на короткие открытые теги, он будет запрашивать только 500 раз.

Даже при группировке на уровне файлов инструмент, возможно, слишком осторожен в своем подходе к внесению изменений. Но мы говорим о возможном изменении тысяч файлов в системе за один день. Я не думаю, что полная автоматизация является правильным ответом здесь. Мне потребовалось всего несколько часов, чтобы внимательно изучить каждое изменение, охватывающее несколько тысяч файлов во всех системах, которыми я управляю с помощью этого инструмента.

У меня довольно большой опыт использования token_get_all(), а также написания парсеров токенов.

0 голосов
/ 23 апреля 2016

Вот Perl однострочный:

perl -pi -w -e 's/\<\?/\<\?php/g;' *php

Используйте контроль версий для постановки и решите сохранить изменения или нет.

...