Search code examples
phpregexpreg-replace-callback

PHP - preg_replace_callback fails with sub-tags


First of all I want to apolagize if there is existing thread, I searched a lot, but could not find solution.

So I have preg_replace_callback function which replace specific tags in string with function.

Example:

$object = preg_replace_callback('~{FUNC\s+(.+?)}(.+?){/FUNC}~is', function($matches) use ($replace)
{
        list($condition, $function, $content) = $matches;
        return $function($content);
}, $object);

But when I use sub-tag inside the other tag it fails

Example:

{FUNC name_of_func}
    text
    text
    text
    {FUNC name_of_func2}text 2{/FUNC}
    text
    text
{/FUNC}

I know it finds the first closing tag and thats the problem, but im bad with regex, how to fix that so I can use multiple sub-tags or sub-sub-tags if thats possible?


Solution

  • To perform custom replacements of eventually nested structures using preg_replace_callback an easy way consists to replace innermost parts first in a while loop until there's nothing to replace. To do that, your pattern must forbid nested parts.

    Other thing, instead of copying match arrays unnecessarily using list(), it's better to use named captures:

    $replace = [
        'func1' => function ($var) { /*...*/ },
        'func2' => function ($var) { /*...*/ },
        //...
    ];
    
    $pattern = '~{FUNC \s+ (?<funcName> \w+ )}
                 (?<content> [^{]*+ (?: { (?!/?FUNC\b) [^{]* )*+ )
                 {/FUNC}~xi';
    
    do {
        $object = preg_replace_callback($pattern, function($m) use ($replace) {
            return $replace[$m['funcName']]($m['content']);
        }, $object, -1, $count);
    } while ($count);