0

I have the following line of code:

$message = preg_replace('/\{\{([a-zA-Z_-]+)\}\}/e', "$$1", $body);

This replaces words surrounded by two curly brackets with variables of the same name. ie {{username}} gets replaced by $username.

I am trying to convert it to use preg_replace_callback. This is my code so far based on Googling, but I'm not really sure what I am doing! The error_log output is showing the variable name including the curly brackets.

$message = preg_replace_callback(
    "/\{\{([a-zA-Z_-]+)\}\}/",
        function($match){
            error_log($match[0]);
            return $$match[0];
        },
        $body
);

Any help greatly appreciated.

4
  • 2
    Use $match[1] for 1st capture group containing your variable name. Commented Dec 20, 2018 at 17:49
  • 1
    See Using each match in preg_replace() in PHP Commented Dec 20, 2018 at 17:50
  • 1
    As a side note, this error_log($match[0]) seems to be used to debug. You could study about using xdebug (xdebug.org). It's easy and much better to debug than this. Commented Dec 20, 2018 at 17:52
  • 2
    I think the main issue here is that the variables you're referencing won't exist inside the scope of the function. I.e., if you have {{username}} then $username is undefined in the function. You could put them all in an associative array, and then make that array available via the use statement. This way, you're also essentially building a whitelist of allowed vars, so things like {{this}} won't be exploitable. Commented Dec 20, 2018 at 17:53

1 Answer 1

2

Functions have their own variable scope in PHP, so anything you're trying to replace isn't available inside the function unless you make it explicitly so. I'd recommend putting your replacements in an array instead of individual variables. This has two advantages -- first, it allows you to easily get them inside the function scope and second, it provides a built-in whitelisting mechanism so that your templates can't accidentally (or intentionally) refer to variables that shouldn't be exposed.

// Don't do this:
$foo = 'FOO';
$bar = 'BAR';

// Instead do this:
$replacements = [
    'foo' => 'FOO',
    'bar' => 'BAR',
];

// Now, only things inside the $replacements array can be replaced.

$template = 'this {{foo}} is a {{bar}} and here is {{baz}}';
$message = preg_replace_callback(
    '/\{\{([a-zA-Z_-]+)\}\}/',
    function($match) use ($replacements) {
        return $replacements[$match[1]] ?? '__ERROR__';
    },
    $template
);

echo "$message\n";

This yields:

this FOO is a BAR and here is __ERROR__
Sign up to request clarification or add additional context in comments.

1 Comment

Sane advise to use an array ++

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.