用PHP替换PHP短打开标签的批处理脚本

问题描述 投票:17回答:13

我有很多年来编写的大量php文件,我需要将所有短开标签正确替换为正确的显式开放标签。

change "<?" into "<?php"

我认为这个正则表达式将正确选择它们:

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

照顾像这样的案件

<?//
<?/*

但我不知道如何处理整个文件夹树并检测.php文件扩展名并应用正则表达式并在文件更改后保存。

如果你掌握了正确的工具,我觉得这可以非常简单。 (sed手册中有一个有趣的黑客:4.3 Example/Rename files to lower case)。

也许我错了。 或许这可能是一个oneliner?

php sed find awk
13个回答
17
投票

不要使用正则表达式来解析正式语言 - 你总会遇到你没想到的干草堆。喜欢:

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

使用知道语言结构的处理器更安全。对于html,这将是一个xml处理器;对于PHP,内置的tokenizer extension。它有T_OPEN_TAG解析器标记,它匹配<?php<?<%,以及T_OPEN_TAG_WITH_ECHO,它匹配<?=<%=。要替换所有短开标签,你会找到所有这些标记,用T_OPEN_TAG替换<?phpT_OPEN_TAG_WITH_ECHO

实施留给读者的练习:)

编辑1:指挥官对<?php echo非常友好。

编辑2:关于在provide oneshort_open_tagphp.ini<?关闭<%的系统将不会被替换脚本识别。要使脚本在此类系统上运行,请通过命令行选项启用<?=

short_open_tag

附: php -d short_open_tag=On short_open_tag_replacement_script.php 和googleing for tokenizer,token_get_all和解析器令牌名称的创意组合可能会有所帮助。

p.p.s.另见the man page for token_get_all()在这里


0
投票

XML / XHTML页面通常包含以下代码:

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

当然,不应该改为:

<?php echo '<?xml version="1.0" encoding="UTF-8" ?>'; ?>

也不:

<?phpphp echo '<?phpxml version="1.0" encoding="UTF-8" ?>'; ?>

0
投票

遗憾的是,自动化解决方案可能无效我的建议:

1)使用grep查找所有短标签:

<?php echo '<?phpxml version="1.0" encoding="UTF-8" ?>'; ?>

2)浏览每个文件和行并手动修复

我知道如果你有一个庞大的项目,这可能不是一个可行的解决方案,但对我而言,它运作良好。


0
投票

这是一个perl单行:

grep -rn "<?[^p]" *

使用版本控制进行分级并决定是否保留更改。


0
投票

PHP 7.4正式弃用了短开放标签,而PHP 8完全删除了它们,因此当人们寻找转换遗留代码库的解决方案时,SO上的这个问题会变得有些受欢迎。

正如其他答案所述,sed并未涵盖所有用例。建议的perl -pi -w -e 's/\<\?/\<\?php/g;' *php PHP-CS-Fixer的行为非常像sed,并没有涵盖所有用例。此外,至少有一个我发现的工具,例如danorton的一个答案,目前仅在启用短开标签时才有效,如果你通过操作系统升级升级到PHP 8,你就不能轻易回滚到7.x来运行这样的工具。工具。 Caveat Emptor非常适用于所有这些方法。

我编写了一个不依赖于短开标签的工具(即它适用于PHP 8),不使用正则表达式(即使用full_opening_tag),还避免使用非短开标签(例如token_get_all())和其他非标记场景(例如包含“标记”的PHP字符串)。

<?xml

该工具运行的默认模式只是查找引用并显示它们。没有文件被修改。

https://github.com/cubiclesoft/php-short-open-tag-finder/模式(当前是唯一修改文件的模式)中,该工具会询问是否可以基于每个文件替换每组引用。也就是说,如果有500个文件,总共有2,000个短开标签引用,那么它只会询问500次。

即使使用文件级别分组,该工具在进行更改时也可能过于谨慎。但我们正在谈论可能在一天内修改系统上的数千个文件。我不认为完全自动化是正确的答案。我只用了几个小时就仔细考虑了我使用该工具管理的所有系统上的几千个文件中的每个更改。

我在使用-ask以及编写令牌解析器方面有相当多的经验。


14
投票

如果您使用的是tokenizer选项,这可能会有所帮助:

Regex to parse define() contents, possible?

请注意,如果未启用短标记,则标记生成器将无法正确标记短标记。也就是说,您无法在短标签不起作用的系统上运行此代码。您必须在其他地方运行它来转换代码。


5
投票

这是我编写的一个实用程序,它转换包含短打开标记的PHP源代码并用长标记替换它们。

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

即它转换代码如下:

https://github.com/danorton/php_replace_short_tags

对此

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

--skip-echo-tags选项会导致它跳过<?=标签而只替换<?标签。

它被编写为PHP-CLI脚本,需要将CLI php.ini文件设置为允许短的短打开标记。这是PHP 5.3.0及更早版本的默认设置,但可能并非总是如此。 (如果未启用该设置,脚本将不会更改任何内容。)


4
投票

该问题已作为 <?php echo $var1 ?> <?php printf("%u changes\n",$changes) ?> 工具中的修复工具得到解决,该工具可以轻松安装并经过测试和维护。

然后修复很容易:

php-cs-fixer

只需将$ php-cs-fixer fix --fixers=short_tag --diff --dry-run <path> 替换为您要更改的目录或文件的路径即可。给出的命令是首先检查(<path>--dry-run参数)。

安装就像一样简单

--diff

如果您已在路径中安装了具有全局编写器bin目录的composer(推荐)。


2
投票

我之前的回答我只是用sed覆盖不会工作,sed太弱了这种事情IMO。

所以我已经制作了一个perl脚本应该可以做到这一点,它希望非常用户可编辑。

$ composer global require friendsofphp/php-cs-fixer

但是请注意,我没有在任何真实代码上测试过这个,所以它可能会“爆炸”。

我建议你修改你的代码(等等,它已经修改过了,对吗?对吗?)然后运行你的测试套件(不要告诉我你没有测试!)修改后的代码,因为你可以没有完全成熟的FSM解析器,不能确定它做正确的事情。


1
投票

为了达到这个目的,我将简化你的正则表达式,使其更好用,但我可能错了,因为我还没有在任何真正的代码上测试它。

假设您正坐在代码的基本目录中,您可以从以下开始:

#!/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';
}

这将获得所有.php文件,由NULL字符分隔,如果它们中的任何一个有空格,这是必要的。

find . -iname "*.php" -print0

这应该可以帮助你完成大部分工作。它将找到所有文件,然后为每个文件运行sed来替换代码。但是,如果没有-i标签(在下面使用),这实际上不会触及您的文件,它只会将您的代码发送到您的终端。 -n抑制正常输出,正则表达式部分后面的p告诉它只打印更改的行。

好的,如果您的结果看起来正确,那么您将采取重大步骤,即就地替换文件。在尝试此操作之前,您一定要备份所有文件!

find . -iname "*.php" -print0 | xargs -0 -I{} sed -n 's/\(<\?\)\([^a-zA-Z]\)/\1php\2/gp' '{}'

这应该是关于完成工作。不幸的是,我没有使用该语法的PHP文件,所以你可以自己从这里弄清楚,但希望现在完成工作的机制更清晰一些:

  1. 用“查找”抓取所有文件
  2. 将该文件列表发送到“xargs”(它一次对文件执行一些命令
  3. 使用“sed”和语法's / to-change / changed /'来使你的正则表达式魔法发挥作用!

1
投票

这是我的RegExp版本:

find . -iname "*.php" -print0 | xargs -0 -I{} sed -i 's/\(<\?\)\([^a-zA-Z]\)/\1php\2/g' '{}'

1
投票

我在近2000个文件中使用了danorton脚本,它就像一个魅力

我将他的脚本放入一个名为“fixtags.php”的文件中,并使用以下linux 1衬里来解决问题:

<\?(?!(php|=|xml))(\s|\t|\n)

我遇到的唯一问题是它遇到的文件大小为零字节。


0
投票

我以前必须经历这个,我发现最好分阶段完成。一个糟糕的脚本试图抓住它可能会弄乱很多文件。

我使用Coda(或任何其他Web编辑器)对非常特定的字符串进行简单的查找和替换。

例如以“

这可能看起来有点单调乏味,但我确信某些事情并没有在某些我不知道的地方搞砸了。回去是一个真正的痛苦。

© www.soinside.com 2019 - 2024. All rights reserved.