preg_split混合HTML和PHP标记,引号和注释除外

use*_*374 5 php regex preg-match preg-split

我有一个混有HTML的php页面.一些示例代码:

<?php echo "<p>some text</p>"; ?>/* <? some php in comments ?> */
<p>some HTML text</p> <!-- <h1>some HTML in comments</h1> -->
<? $header_info = <<<END 
\$some="<?php @ob_start(); @session_set_save_handler(); ?>";
END; ?>
<h2>Some more HTML</h2>
Run Code Online (Sandbox Code Playgroud)

我想在每个PHP和HTML标记处拆分,但保留/忽略引号或注释中的任何PHP标记或HTML标记.这是我到目前为止:

$array = preg_split("/((^<\?php)|([^'|\"]<\?php)|([^'|\"]<\?)|([^'|\"]\?>)|(<\%)|(\%>))/i", $string, -1);
Run Code Online (Sandbox Code Playgroud)

我遇到的问题是最终的$数组中缺少一些HTML结束括号'>'.我想保持HTML打开和关闭标签完好无损.有时我最终会

<p></p instead of <p></p> 
Run Code Online (Sandbox Code Playgroud)

它应该如下所示:

[0] echo "<p>some text</p>";  
[1] <p>some HTML text</p> 
[2] $header_info = <<<END 
\$some="<?php @ob_start(); @session_set_save_handler(); ?>";
END; 
[3] <h2>Some more HTML</h2>
Run Code Online (Sandbox Code Playgroud)

只要preg_split没有将它们视为任何分隔符并忽略它们中的任何一个,任何注释都不需要成为数组的一部分.

我也刚刚意识到一些php标签,特别是在使用eval()时最终会像这样结束:

"?> <p>some HTML text</p> <?";
Run Code Online (Sandbox Code Playgroud)

这意味着我的正则表达式中的引用与任何这些情况都不匹配.

Preg_match()可能是一个更好的选择,但不确定.

任何帮助都会非常感激,因为我在正则表达方面并不是非常聪明,而是在这一点上陷入困​​境.

非常感谢 :)

Ste*_*han 2

前言
由于询问了正则表达式解决方案,因此以下解决方案将依赖于正则表达式。然而,在这种特殊情况下,PHP 解析器会更适合

正则表达式

#(?<!"|\')<\\?(?:php)?\\s+(.+?)\\?>(?!"|\')|/\*.+\*/|<!--.+-->#is
Run Code Online (Sandbox Code Playgroud)

小脚本

$subject = '<?php echo "<p>some text</p>"; ?>/* <? some php in comments ?> */
<p>some HTML text</p> <!-- <h1>some HTML in comments</h1> -->
<? $header_info = <<<END
\\$some="<?php @ob_start(); @session_set_save_handler(); ?>";
END; ?>
<h2>Some more HTML</h2>';

$returnValue = preg_replace('#(?<!"|\')<\\?(?:php)?\\s+(.+?)\\?>(?!"|\')|/\*.+\*/|<!--.+-->#is', '$1', $subject, -1);

var_dump(preg_split('#\\r?\\n#s', $returnValue));
Run Code Online (Sandbox Code Playgroud)

结果

array(6) {
  [0]=>
  string(25) "echo "<p>some text</p>"; "
  [1]=>
  string(22) "<p>some HTML text</p> "
  [2]=>
  string(21) "$header_info = <<<END"
  [3]=>
  string(60) "\$some="<?php @ob_start(); @session_set_save_handler(); ?>";"
  [4]=>
  string(5) "END; "
  [5]=>
  string(23) "<h2>Some more HTML</h2>"
}
Run Code Online (Sandbox Code Playgroud)

演示
http://sandbox.onlinephpfunctions.com/code/017a51877b50f272f151feade7b59e142757481e

讨论

1. # 
2. (?<!"|\')
3. <\\?(?:php)?\\s+
4. (.+?)
5. \\?>
6. (?!"|\')
7. |/\*.+\*/
8. |<!--.+-->
9. #is
Run Code Online (Sandbox Code Playgroud)

第 1 行我使用这个正则表达式分隔符,因为它可以避免 /
第 2 行的转义这是正则表达式的关键。向后查找用于确保下一个打开的 php 标记前面没有任何单引号或双引号。
第 3 行这里定义了 php 起始标签。为了也支持 ASP 标签,可以像这样更改此行:<\\?(?:php|%)?\\s+
第 4 行由于我们已经检测到 php 代码序列的开始,因此我们匹配此 php 代码序列中出现的任何字符。请注意,在第 9 行,我们使用s标志来表明我们也需要在 php 代码序列中添加新行。
第 5 行我们标记 php 代码序列的结尾。
第 6 行我们确保前面匹配的 php 标签后面没有任何带有否定先行断言的单/双引号。
第 7,8 行如果我们发现一些 php/HTML 注释,它们将被简单地忽略。
第 9 行结束 f 正则表达式。

已知的问题

  • 在 上执行正则表达式后$subject,行只是用换行符(前面有可选的回车符)分隔符分隔。
  • 没有努力处理 php heredoc 或 newdoc 语法。
  • 这个正则表达式应该被视为针对任何 php 代码的防弹正则表达式。PHP 解析器更适合。