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);
}
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.