我有多年来编写的大量php文件,我需要将所有短开标签正确替换为正确的显式开放标签。
change "<?" into "<?php"
我认为这个正则表达式会正确选择它们:
<\?(\s|\n|\t|[^a-zA-Z])
负责像
这样的案件<?//
<?/*
但我不知道如何处理整个文件夹树并检测.php文件扩展名并应用正则表达式并在文件更改后保存该文件。
如果你掌握了正确的工具,我觉得这可以非常简单。 (sed手册中有一个有趣的黑客:4.3 Example/Rename files to lower case)。
也许我错了。
或者这可能是一个oneliner?
答案 0 :(得分:17)
不要使用正则表达式来解析正式语言 - 你总是遇到你没想到的干草堆。像:
<?
$bla = '?> now what? <?';
使用知道语言结构的处理器更安全。对于html,这将是一个xml处理器;对于php,内置tokenizer extension。它有T_OPEN_TAG
解析器令牌,匹配<?php
,<?
或<%
,以及T_OPEN_TAG_WITH_ECHO
,匹配<?=
或{{1} }。要替换所有短打开的代码,您会找到所有这些代币,并将<%=
替换为T_OPEN_TAG
,将<?php
替换为T_OPEN_TAG_WITH_ECHO
。
实施留给读者练习:)
编辑1 :指挥官对provide one非常友好。
编辑2 :在<?php echo
,php.ini
,<?
和<%
赢得<?=
关闭short_open_tag
的系统上被替换脚本识别。要使脚本在此类系统上运行,请通过命令行选项启用short_open_tag
:
php -d short_open_tag=On short_open_tag_replacement_script.php
P.S。 the man page for token_get_all()和googleing for tokenizer , token_get_all 的创意组合以及解析器令牌名称可能有所帮助。
p.p.s。另见SO {/ p>上的Regex to parse define() contents, possible?
答案 1 :(得分:14)
如果您正在使用tokenizer选项,这可能会有所帮助:
$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;
请注意,如果未启用短标记,则标记生成器将无法正确标记短标记。也就是说,您无法在短标签不起作用的系统上运行此代码。您必须在别处运行它才能转换代码。
答案 2 :(得分:4)
这是我编写的一个实用程序,它转换包含短打开标记的PHP源代码并用长标记替换它们。
即。它转换代码如下:
<?= $var1 ?>
<? printf("%u changes\n",$changes) ?>
到此
<?php echo $var1 ?>
<?php printf("%u changes\n",$changes) ?>
- skip-echo-tags 选项会使其跳过&lt;?= 标记,只会替换&lt;?标记
它是作为PHP-CLI脚本编写的,需要将CLI php.ini 文件设置为允许短的短打开标记。这是PHP 5.3.0及更早版本的默认设置,但可能并非总是如此。 (如果未启用该设置,脚本将不会更改任何内容。)
答案 3 :(得分:4)
此问题已作为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
如果您的路径中已安装了具有全局编写器bin目录的作曲家(推荐)。
答案 4 :(得分:2)
我之前的回答我只是用sed覆盖不会工作,sed对于IMO这类事情太弱了。
所以我已经制作了一个perl-script应该可以做到这一点,它希望非常用户可编辑。
#!/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解析器,你不能某些做正确的事情。
答案 5 :(得分:1)
为了达到这个目的,我将简化你的正则表达式,以便更好地工作,但我可能错了,因为我没有在任何实际代码上测试它。
假设您正坐在代码的基本目录中,您可以从:
开始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文件,所以你可以自己从这里弄清楚,但希望现在完成工作的机制更清晰了:
答案 6 :(得分:1)
这是我的RegExp版本:
<\?(?!(php|=|xml))(\s|\t|\n)
答案 7 :(得分:1)
我在近2000个文件中使用了danorton脚本,它就像魅力一样
我把他的脚本放到一个名为“fixtags.php”的文件中,并使用以下linux 1的内核来解决问题:
find . -iname "*.php" | xargs php fixtags.php --overwrite
我遇到的唯一问题是它遇到的文件大小为零字节。
答案 8 :(得分:0)
我以前必须经历这个过程,我发现最好分阶段完成。一个糟糕的脚本试图抓住它可能会弄乱很多文件。
我使用Coda(或任何其他网页编辑器)对非常具体的字符串进行简单的查找和替换。
例如以“
这可能看起来有点单调乏味,但我确信某些事情并没有在我不知道的地方搞砸了。回去是一个真正的痛苦。
答案 9 :(得分:0)
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" ?>'; ?>
答案 10 :(得分:0)
不幸的是,自动化解决方案可能无效。我的建议:
1)使用grep查找所有短标签:
grep -rn "<?[^p]" *
2)浏览每个文件和行并手动修复
我知道如果你有一个庞大的项目,这可能不是一个可行的解决方案,但对我来说它运作良好。
答案 11 :(得分:0)
这是一个perl单行:
perl -pi -w -e 's/\<\?/\<\?php/g;' *php
使用版本控制进行分级并决定是否保留更改。
答案 12 :(得分:0)
PHP 7.4正式弃用短开放标记,而PHP 8则完全删除了短开放标记,因此,随着人们寻求转换旧代码库的解决方案,关于SO的这个问题将变得越来越普遍。
正如其他答案所指出的那样,sed不能涵盖所有用例。建议的full_opening_tag
PHP-CS-Fixer的行为非常类似于sed,并且并未涵盖所有用例。另外,我发现至少有一种工具(例如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()
以及编写令牌解析器方面有丰富的经验。
答案 13 :(得分:0)
更新php版本时遇到同样的问题。
使用此:
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<div class="flex-w flex-m">
<span class="m2-txt2 p-r-40" title="I am tooltip">
Navigation
</span>
<a href="../main/index.html" class="size3 flex-c-m how-social trans-04 m-r-15 m-b-5 m-t-5">
<i class="fa fa-home" title="I am tooltip"></i>
</a>
<a href="../main/contact.html" class="size3 flex-c-m how-social trans-04 m-r-15 m-b-5 m-t-5">
<i class="fa fa-phone" title="I am tooltip"></i>
</a>
<a href="#" class="size3 flex-c-m how-social trans-04 m-r-15 m-b-5 m-t-5">
<i class="fa fa-question"></i>
</a>
<a href="#" class="size3 flex-c-m how-social trans-04 m-r-15 m-b-5 m-t-5">
<i class="fa fa-comment"></i>
</a>
</div>
这将转换为“ <?”到“ <?php”,“ <?//”到“ <?php ///”,“ <?/ ”到“ <?php / ”
用于任何类型的文件.php或.phtml