在全局多行模式下使用 RegEx 仅匹配 PHP 代码 (/gm)

Match Only PHP Code Using RegEx In Global MultiLine Mode (/gm)

我试图仅匹配 PHP 代码,例如此块中的 php 代码:

<?php foo(); ?>

<abc>

<? foo(); ?>

<?php

foo();
bar();

?>

foo();
bar();

<? //also short open tag

foo();
bar();

?><?php

foo();
bar();

我希望它只匹配 php 标签之间的代码,包括带结束标签的 php 开始标签和不包括结束标签的仅 php 开始标签(可以发生在 php 代码的最后。

我尝试了很多正则表达式选项,最后得到了这个,但它显然不能像我想要的那样工作,因为它处于 /g 模式,并且还选择了 <abc> 而它应该't (Demo):

<\?.*[\s\S]*?(?:$|\?\>)

有什么方法可以在 /gm 模式下使用正则表达式实现此目的吗?

请注意,我问的原因是因为我正在使用文件搜索程序,当我搜索我拥有的许多 php 文件的内容时,我希望它只在 php 代码,而不是得出不相关的结果。因此,我将使用此正则表达式作为其余内容搜索的附加条件。搜索程序使用PCRE /gm模式。

P.S。在发布问题之前,我对 SO 做了很多研究,但找不到解决这个问题的方法。 除其他问题外,我还检查了:

My regex is matching too much. How do I make it stop?

Get content between two strings PHP

结论

我最终使用了 Julio 的解决方案并对其进行了改进,以考虑到 Jan 的回答示例中提到的单引号和双引号。谢谢大家的答案。 这是在 /gm 模式下工作的最终正则表达式:

<\?[\s\S]*?(?:\z|\?\>|[\"\'].*?[\"\'][\s\S]*?\?>)

Demo

使用这个:<\?[\s\S]*?(?:\z|\?\>)

Demo

.*[\s\S]* 是多余的。你只需要 [\s\S]* 来匹配任何字符(另外,由于 .* 是贪婪的,它匹配你的结尾 ?>

也使用 \z 代替 $

这应该适合你:

(<\?)(.*?)(?:$|\?>)/isg

Online example.

你可以使用

<\?(?:php)?        # <? or <?php
(?:(?!\?>)[\s\S])* # do not overrun ?> but match anything else greedily
(?:\?>)?           # ?> in the end

参见 a demo on regex101.com(注意冗长的标志!)。


让我强调一下,这通常是一个糟糕的方法,例如。字符串如

<?php
echo "This is hilarious ?>";
?>

另见 demo for the latter on regex101.com。在这里,改用解析器或重新考虑您原来的问题。