Take the 2-minute tour ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

I have a function which uses preg_replace() where the pattern and replacements are arrays. I need a counter to track the replacements, so I am converting the function to use preg_replace_callback with a closure, but I can't seem to find a way to distinguish which pattern the match being passed to the callback matches. Is there a way to do array => array replacement using preg_replace_callback?

Ideally this is what I'd like to work, but obviously it won't since $pattern and $replace are evaluated going into the call and not inbetween each replacement

function replaceTags($text)
{
    $i = 1;

    $pattern = array(
        '/\[d\](.*?)\[\/d\]/',
        '/\[s\](.*?)\[\/s\]/',
    );

    $replace = array(
        '<div id="'.($i++).'">$1</div>',
        '<span id="'.($i++).'">$1</span>',
    );

    return preg_replace($pattern, $replace, $text);
}
share|improve this question
 
preg_replace already supports array -> array replacement. Does it not fit your needs? –  Waleed Khan Jul 18 '12 at 1:45
 
I need a replacement counter which is used in the replacements itself so preg_replace won't work –  Slipflurry Jul 18 '12 at 1:47
 
You could take the easy way out and count and replace separately. –  Waleed Khan Jul 18 '12 at 1:49
 
Perhaps you should count occurances first, then replace, instead of trying to hack preg_replace into doing both. –  Hamish Jul 18 '12 at 2:11
add comment

1 Answer

If I understand it correctly, you just need to maintain state between calls to your callback function. The ideal way to do this is using a member function. The state is stored in the object instance. Each time you call, you can modify the object, changing your state.

I also added an extra capture to your patterns in order to differentiate between the patterns in the callback.

<?php
class TagReplacer {
    private $counter = 0;

    public function replacer($matches) {
        // modify the state
        $this->counter++;

        // return the replacement text using the state
        if ($matches[1] === "d")
            $tag = 'div';
        else
            $tag = 'span';
        return "<{$tag} id=\"{$this->counter}\">{$matches[2]}</{$tag}>";
    }
}

function replaceTags($text) {
    $stateObject = new TagReplacer();
    $patterns = array(
        '/\[(d)\](.*?)\[\/d\]/',
        '/\[(s)\](.*?)\[\/s\]/',
    );

    return preg_replace_callback(
            $patterns,
            array(&$stateObject, "replacer"),
            $text);
}

echo replaceTags("zzz[d]123[/d]zzz[s]456[/s]zzz[d]78[/d]zzz[s]90[/s]zzz");
?>

The output is

zzz<div id="1">123</div>zzz<span id="3">456</span>zzz<div id="2">78</div>zzz<span id="4">90</span>zzz

I was surprised that the ids are not in numerical order. My guess is that preg_replace_callback iterates through the pattern array doing all the replacements at once for each pattern.

share|improve this answer
 
Yes this is what I'd like to do, but my pattern and replacement arguments are arrays. I added an example to the question of what I'd like to do if that clarifies it. –  Slipflurry Jul 18 '12 at 4:59
 
@Slipflurry Is this more what you are looking for? –  walrii Jul 18 '12 at 6:25
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.