用 PHP preg_match_all() 匹配子字符串

Matching substrings with PHP preg_match_all()

我正在尝试创建一个轻量级的 BBCode 解析器,而无需为每个元素硬编码正则表达式匹配。我的方法是利用preg_replace_callback()来处理函数中的匹配。

我的简单但令人沮丧的方法是使用正则表达式对元素名称进行分组,并使用每个函数的开关进行不同的解析。

这是我的正则表达式模式:

'~\[([a-z]+)(?:=(.*))?(?: (.*))?\](.*)(?:\[/\])~siU'

这是我要测试的 preg_replace_callback()

return preg_replace_callback(
  '~\[([a-z]+)(?:=(.*))?(?: (.*))?\](.*)(?:\[/\])~siU', 
  function($matches) {
    var_dump($matches);
    return "<".$matches[1].">".$matches[4]."</".$matches[1].">";
  },
  $this->raw
);

这一问题难倒了我。正则表达式模式似乎不会递归匹配,这意味着如果它匹配一个元素,它不会匹配其中的元素。

以这个BBCode为例:

[i]This is all italics along with a [b]bold[/b].[/i]

这只会匹配 [u],不会匹配其中的任何元素,因此看起来像

This is all italics along with a [b]bold[/b].

preg_match_all() 继续表明情况确实如此,我已经尝试搞乱贪婪的语法和模式。

我该如何解决这个问题?

感谢@Casimir et Hippolyte 的评论,我能够像他们说的那样使用 while 循环和计数参数解决这个问题。

基本的正则表达式字符串不起作用,因为我想在 [color=red][img width=""].

等标签中使用值

这是最终的代码。它并不完美,但它确实有效。

$str = $this->raw;
do {
  $str = preg_replace_callback(
    '~\[([a-z]+)(?:=([^]\s]*))?(?: ([^[]*))?\](.*?)(?:\[/\])~si', 
    function($matches) {
      return "<".$matches[1].">".$matches[4]."</".$matches[1].">";
    },
    $str,
    -1,
    $count
  );
} while ($count);
return $str;