2

I need to write a simple PHP function to replace text between {{ }} characters with their respective data.

Example:

String: "and with strange aeons even {{noun}} may {{verb}}"

$data = ['noun' => 'bird', 'verb' => 'fly'];

Result:

"and with strange aeons even bird may fly"

I have it almost working with the following code based on preg_replace_callback

function compile($str,$data){

    foreach ($data as $k => $v) {
        $pattern = '/\b(?<!\-)(' . $k . ')\b(?!-)/i';
        $str = preg_replace_callback($pattern, function($m) use($v){
            return $v;
        }, $str);
    }

    return $str;
}

But I cant seem to account for the {{ }}.

The result looks like this:

"and with strange aeons even {{bird}} may {{fly}}"

How can I adjust the regex and/or code to account for the double curly brackets?

Also, before anyone asks why I'm trying to do this manually rather than use PHP itself or the Smarty plugin -- its too narrow a use case to install a plugin and I cannot use PHP itself because the input string is coming in as raw text from a database. I need to compile that raw text with data stored in a PHP array.

yevg
  • 1,846
  • 9
  • 34
  • 70

3 Answers3

2

You can use

$str = "and with strange aeons even {{noun}} may {{verb}}";
$data = ['noun' => 'bird', 'verb' => 'fly'];

$pattern = '/{{(' . implode('|', array_keys($data)) . ')}}/i';
echo preg_replace_callback($pattern, function($m) use($data){
    return $data[strtolower($m[1])];
}, $str);
// => and with strange aeons even bird may fly

See the PHP demo.

The $pattern will look like /{{(noun|verb)}}/i, and will match noun or verb inside double braces while capturing the word itself. The replacement will be the corresponding key value of the $data array. Turning the Group 1 value to lower case with strtolower($m[1]) is required since the keys in the $data array are all lowercase, and the $pattern can match uppercase variants, too.

Wiktor Stribiżew
  • 607,720
  • 39
  • 448
  • 563
  • clever solution. and best of all, it worked! Thank you – yevg Dec 01 '20 at 19:46
  • I would suggest applying `preg_quote()` to `array_keys($data)` using `array_map()` in the event that a key contains a special character. – MonkeyZeus Dec 01 '20 at 20:43
  • Something similar might need to be done to the values given to `preg_replace_callback()` in the event that a value contains `$1` or something. – MonkeyZeus Dec 01 '20 at 20:44
  • 1
    @MonkeyZeus If that is necessary, I will add `implode("|", array_map(function($i) { return preg_quote(trim($i), "/"); }, array_keys($data)))` to the answer instead of `implode('|', array_keys($data))`. I doubt `noun`, `verb`, any POS value has got more than a letter. – Wiktor Stribiżew Dec 01 '20 at 20:48
  • I really can't predict OP's future plans but it's better to harden the code ahead of time rather than trying to debug a future issue especially if OP is not the one maintaining the code in the future. – MonkeyZeus Dec 01 '20 at 20:58
  • @MonkeyZeus There is nothing to harden in this case. POS values are composed solely of letters. – Wiktor Stribiżew Dec 01 '20 at 21:17
2

Since you're looping anyway, keep it simple:

foreach ($data as $k => $v) {
    $str = str_ireplace('{{'.$k.'}}', $v, $str);
}

You can add a space before {{ and after }} if needed.

AbraCadaver
  • 78,200
  • 7
  • 66
  • 87
1

Make use of strtr() and call it a day:

$string = 'and with strange aeons even {{noun}} may {{verb}}';

$data = ['{{noun}}' => 'bird', '{{verb}}' => 'fly'];

echo strtr( $string, $data );

produces:

and with strange aeons even bird may fly

strtr() is nice because it won't mess up the string in the event of:

$data = ['{{noun}}' => 'bi{{verb}}rd', '{{verb}}' => 'fly'];
MonkeyZeus
  • 20,375
  • 4
  • 36
  • 77