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

Posted

技术标签:

【中文标题】preg_split 混合 HTML 和 PHP 标记,引号和注释除外【英文标题】:preg_split mixed HTML and PHP tags except in quotes and comments 【发布时间】:2012-11-17 06:04:07 【问题描述】:

我有一个混合了 htmlphp 页面。一些示例代码:

<?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>

我想在每个 PHP 和 HTML 标记处拆分,但保留引号或 cmets 中的任何 PHP 标记或 HTML 标记不变/忽略。这是我目前所拥有的:

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

我遇到的问题是最终 $array 中缺少一些 HTML 右括号“>”。我想保持 HTML 打开和关闭标签完好无损。有时我会得到 ​​p>

<p></p instead of <p></p> 

应该是这样的:

[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>

只要 preg_split 不将它们视为任何分隔符并忽略其中任何一个,任何 cmets 都不需要是数组的一部分。

我也刚刚意识到一些 php 标签,尤其是在使用 eval() 时可能会像这样结束:

"?> <p>some HTML text</p> <?";

这意味着我的正则表达式中的引号将不匹配任何这些情况。

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

任何帮助都将不胜感激,因为我在正则表达式方面不是很聪明,而且在这一点上卡住了。

非常感谢:)

【问题讨论】:

THE PONY HE COMES! - 只有在这里情况更糟,因为您还试图解析 PHP 块... 【参考方案1】:

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

正则表达式

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

脚本

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

结果

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>"

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

讨论

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

line 1 我使用这个正则表达式分隔符,因为它允许避免转义 /line 2 这是 key正则表达式。 negative lookbehind 用于确保下一个开始 php 标记前面没有任何单引号或双引号。第 3 行 这里定义了一个开始 php 标记是。为了也支持 ASP 标签,这一行可以这样修改:&lt;\\?(?:php|%)?\\s+第 4 行 因为我们检测到一个 php 代码序列的开始,我们匹配任何出现在这个 php 代码中的字符顺序。请注意,在 第 9 行,我们使用 s 标志来指示我们希望在 php 代码序列中也有新行。第 5 行我们标记 php 的结尾代码序列。第 6 行我们确保前面匹配的 php 标记后面没有任何带有 negative lookahead 断言的单引号/双引号。line 7,8 如果我们发现一些 php/HTML 注释,它们将被简单地忽略。line 9 结束 f 正则表达式。

已知问题

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

【讨论】:

非常感谢 Stephan 非常详细的回答。我忘了提到处理任何类型的 php 代码输入是至关重要的。我想知道是否有人可以概述使用标记器的示例。提前致谢。我对 Tokenizer 不是很熟悉。我做了一些研究,发现了一些关于如何使用 PHP Tokenizer 从 PHP 中剥离 cmets 的代码,但很难理解代码。 您的代码应改为输出以下内容:
 [0]=&gt;$subject = '<?php echo "<p>some text"; ?&gt;'; /*  cmets 中的一些 php ?&gt; */ [1]=&gt;<p>一些 HTML 文本</p> <!-- <h1>cmets 中的一些 HTML</h1> --> [2]=&gt;$header_info = ";结尾; ?&gt; [3]="<h2>还有一些 HTML</h2> "

所以基本上我想在每个不在引号或 cmets 内的 php 标签和任何中间的 HTML 以它自己的数组键结束。丢失或保持 cmets 完好无损。

对不起,我试图格式化上面的代码,但它变得一团糟。这是我的第一篇文章。通读评论格式,但... @user1862374 我无法理解代码应该如何输出。您可以将其再次粘贴到您的问题中作为更新吗? Tks

以上是关于preg_split 混合 HTML 和 PHP 标记,引号和注释除外的主要内容,如果未能解决你的问题,请参考以下文章

PHP 拆分为 preg_split()

正则表达式(特别是 preg_split() PHP)

php面试题汇集2

php preg_split 查找字符串中的所有单词不起作用

如何正确缩进 PHP/HTML 混合代码? [关闭]

PHP正则