PHP 正则表达式:如何在函数中使用捕获的组

PHP regex : How to use captured groups in a function

我正在 php 中构建一个手工制作的功能,用于搜索特定标签([b][/b] 用于粗体,[i][/i] 用于斜体,[img][ /img] 为图片)在一个字符串中,将它们替换为它们的 html 等价物。在替换之前,我还必须在单独的函数中处理 [img] [/img] 标签(字符串中可以有多个)之间的内容,我在这里称之为 foo :

<?php
function convert($txt){
    $patterns = array(  '/\[b\](.*?)\[\/b\]/' ,
                        '/\[i\](.*?)\[\/i\]/' ,
                        '/\[img\](.*?)\[\/img\]/');
    $replace = array("<b></b>" , "<i></i>" , foo("") );
    $res = preg_replace($patterns,$replace, $txt);
    return $res;
}

它适用于 b 和 i 标签,但不适用于 img。

这里的问题是: 当我将捕获的组(我认为由“$1”引用)放入函数中时,它将“$1”视为字符串,并且不是它引用的内容。 例如,如果 foo 声明如下:

function foo($var){
    echo $var;
}

如果我将字符串 text1 [img]path[/img] text2 放入 convert()

然后 "" 将被回显,而不是 "path"


因此这是我的问题: 我如何“评估”我在不同函数中捕获的字符串。在前面的示例中,要在 foo 中回显 [img][/img] 标签之间的内容是什么?

感谢大家花时间回复。

可以先抓取字符串再运行函数:

<?php
function convert($txt){
    preg_match('/\[img\](.*?)\[\/img\]/', $txt, $match);
    $patterns = array(  '/\[b\](.*?)\[\/b\]/' ,
                        '/\[i\](.*?)\[\/i\]/' ,
                        '/\[img\](.*?)\[\/img\]/');
    $replace = array("<b></b>" , "<i></i>" , foo($match[1]) );
    $res = preg_replace($patterns,$replace, $txt);
    return $res;
}

试试这个

<?php
function convert($txt){
    $pattern = array('/\[b\](.*?)\[\/b\]/' => function($matches) { return " 
<b>$matches[1]</b>"; },
                     '/\[i\](.*?)\[\/i\]/' => function($matches) { return " 
<i>$matches[1]</i>"; },
                     '/\[img\](.*?)\[\/img\]/' => function($matches) { echo 
$matches[1]; return "<img>$matches[1]</img>"; });
    $res = preg_replace_callback_array($pattern, $txt);
    return $res;
}
$result = convert("text1 [img]path[/img] text2");
echo "\n$result\n";

输出:

path
text1 <img>path</img> text2

首先,强烈建议使用合法的 BBCode 解析器(库)而不是正则表达式方法。 custom-developed 解析器应该比基本的正则表达式模式更好地处理边缘情况。

既然给出了免责声明,解决从 preg_replace() 的替换参数调用函数的问题的方法是调用 preg_replace_callback(),或者在您的情况下,也许更好地编码为preg_replace_callback_array() 因为您正在为不同的模式寻求不同的回调。

代码:(Demo)

function convert(string $txt): string {
    do {
        $txt = preg_replace_callback_array(
            [
                '~\[([bi])](.*?)\[/]~' => fn($m) => sprintf('<%1$s>%2$s</%1$s>', $m[1], $m[2]),
                '~\[img](.*?)\[/img]~' => 'foo',
            ],
            $txt,
            -1,
            $count
        );
    } while ($count);
    return $txt;
}

function foo(array $m): string {
    return '<img src="' . $m[1] . '">';
}

echo convert("text1 [img]path/src[/img] text2 [b]bold [i]nested string[/i][/b] [img]another/path/to/file[/img] [b]nice[/b] lingering end bbtag [/b] and [b]unclosed");

输出:

text1 <img src="path/src"> text2 <b>bold <i>nested string</i></b> <img src="another/path/to/file"> <b>nice</b> lingering end bbtag [/b] and [b]unclosed

您会注意到调用 foo() 是通过使用其字符串名称作为回调值来完成的。尽管未在 'foo' 值中明确提及,但 matches 数组已发送到自定义函数。

我在 do-while() 循环中调用 preg_replace_callback_array() 以确保替换嵌套的 bbcode 标签(否则会被忽略,因为它们的父标签完全包围了它们)。

如果您希望处理 [u] 标签,只需在第一个正则表达式模式中的 bi 之后添加 u