Search code examples
phpreplace

Avoid backreference replacement in php's preg_replace


Consider the below use of preg_replace

$str='{{description}}';
$repValue='$0.0 $00.00 $000.000 $1.1 $11.11 $111.111';

$field = 'description';
$pattern = '/{{'.$field.'}}/';

$str =preg_replace($pattern, $repValue, $str );
echo $str;


// Expected output: $0.0 $00.00 $000.000 $1.1 $11.11 $111.11
// Actual output:   {{description}}.0 {{description}}.00 {{description}}0.000 .1 .11 1.111 

Here is a phpFiddle showing the issue

It's clear to me that the actual output is not as expected because preg_replace is viewing $0, $0, $0, $1, $11, and $11 as back references for matched groups replacing $0 with the full match and $1 and $11 with an empty string since there are no capture groups 1 or 11.

How can I prevent preg_replace from treating prices in my replacement value as back references and attempting to fill them?

Note that $repValue is dynamic and it's content will not be know before the operation.


Solution

  • Escape the dollar character before using a character translation (strtr):

    $repValue = strtr('$0.0 $00.00 $000.000 $1.1 $11.11 $111.111', ['$'=>'\$']);
    

    For more complicated cases (with dollars and escaped dollars) you can do this kind of substitution (totally waterproof this time):

    $str = strtr($str, ['%'=>'%%', '$'=>'$%', '\\'=>'\\%']);
    $repValue = strtr($repValue, ['%'=>'%%', '$'=>'$%', '\\'=>'\\%']);
    $pattern = '/{{' . strtr($field, ['%'=>'%%', '$'=>'$%', '\\'=>'\\%']) . '}}/';
    $str = preg_replace($pattern, $repValue, $str );
    echo strtr($str, ['%%'=>'%', '$%'=>'$', '\\%'=>'\\']);
    

    Note: if $field contains only a literal string (not a subpattern), you don't need to use preg_replace. You can use str_replace instead and in this case you don't have to substitute anything.