PHP preg_match_all search and replace

This is a very good case for using preg_replace_callback() but first let's polish your regex:

  1. Get rid of the e modifier, it's deprecated and you don't need it since we're going to use preg_replace_callback()

    /\{\{\$(\w+)\}\}/
    
  2. We don't need to escape {{}} in this case, PCRE is smart enough to tell that they are not meant as quantifiers

    /{{\$(\w+)}}/
    
  3. Since you got dots in your input, we need to change \w otherwise it will never match. [^}] is perfect since it means match anything except }

    /{{\$([^}]+)}}/
    
  4. I tend to use different delimiters, this is not required:

    #{{\$([^}]+)}}#
    

Let's get to serious business, the use identifier is going to be of great help here:

$replaces = array('testa.testb.testc' => '1', 'testc.testa' => '2', 'testf' => '3');
$text = '{{$testa.testb.testc}}<br>{{$testc.testa}}<br>{{$testf}}<br>{{$aaaaa}}<br>';

$output = preg_replace_callback('#{{\$([^}]+)}}#', function($m) use ($replaces){
    if(isset($replaces[$m[1]])){ // If it exists in our array
        return $replaces[$m[1]]; // Then replace it from our array
    }else{
        return $m[0]; // Otherwise return the whole match (basically we won't change it)
    }
}, $text);

echo $output;

Online regex demo Online php demo


Well you are just using $matches and not $matches[0], that's why the code you posted does not work.

And your regex doesn't count the . with the \w, so let'z try with [\w.]+

(I used $matches[1] that contains directly the key we need, and then we don't need to use str_replace 2 times more)

$replaces = array('testa.testb.testc' => '1', 'testc.testa' => '2', 'testf' => '3');
$text = '{{$testa.testb.testc}}<br>{{$testc.testa}}<br>{{$testf}}<br>{{$aaaaa}}<br>';

preg_match_all('/\{\{\$([\w.]+)\}\}/', $text, $matches);

var_dump($matches[1]);

foreach($matches[1] as $match)
{
    if(isset($replaces[$match]))
    {
        $text = str_replace('{{$'.$match.'}}', $replaces[$match], $text);
    }
}

echo $text;

This works and returns :

1
2
3
{{$aaaaa}}

You don't need to use a regex for that since you are only dealing with fixed strings:

$replaces = array('testa.testb.testc' => 1, 'testc.testa' => 2, 'testf' => 3);
$keys = array_map(function ($item) {
    return '{{$' . $item . '}}'; }, array_keys($replaces));
$trans = array_combine($keys, $replaces);

$result = strtr($text, $trans);

Note that array_map is not needed if you write the replaces array like this:

$trans = array('{{$testa.testb.testc}}' => 1, '{{$testc.testa}}' => 2, '{{$testf}}' => 3);