Home > OS >  PHP regex : How to use captured groups in a function
PHP regex : How to use captured groups in a function

Time:05-04

I'm building a hand-made function in php that searches for specific tags ( [b][/b] for bold, [i][/i] for italic and [img][/img] for pictures ) in a string, to replace them into their html equivalent. I also have to treat what's between [img] [/img] tags (can be more than one in a string) in a seperate function before the replacement, i've called it foo here :

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

It works correctly for the b and i tags, but not img.

The issue here is that : When i put the captured group (referenced by "$1" i think) in a function, it treats "$1" as a string, and not what is referenced by it. For example, if foo is declared like so :

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

If i put the string text1 [img]path[/img] text2 in convert()

Then "$1" will be echoed, instead of "path"


Therefore here is my question : How can i "evaluate" the string i've captured in a different function. In the previous example, to echo in foo what is between [img][/img] tags?

Thank you for anyone taking time to reply.

CodePudding user response:

First and foremost, it is strongly recommended to use a legitimate BBCode parser (library) instead of a regex approach. A custom-developed parser should be expected to better handle fringe cases much better than a basic regex pattern.

Now that that disclaimer is given, the way to resolve your issue of calling a function from the replacement parameter of preg_replace() is to call preg_replace_callback(), or in your case, perhaps better coded via preg_replace_callback_array() since you are seeking different callbacks for different patterns.

Code: (Demo)

function convert(string $txt): string {
    do {
        $txt = preg_replace_callback_array(
            [
                '~\[([bi])](.*?)\[/\1]~' => 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");

Output:

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

You will notice that calling foo() is done by using its string name as the callback value. The matches array is sent to the custom function despite not being explicitly mentioned in the 'foo' value.

I am calling preg_replace_callback_array() in a do-while() loop to ensure that nested bbcode tags are replaced (which otherwise would have been overlooked because their parent tag completely enclosed them).

If you wish to handle [u] tags, simply add u after bi in the first regex pattern.

CodePudding user response:

You can grab the string first and then run the function:

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

CodePudding user response:

Try this

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

Output:

path
text1 <img>path</img> text2
  • Related