views:

51

answers:

3

I'm looking for function like sprintf(), except whereas with sprintf() you bind the values by order of arguments, I want something where I can bind variables by name. So, if I had the string "Hello $name! We're please to have you visit, $name!", you could pass an array or something and get the resultant string from it.

Something like the PDO statements, but just for plain strings, not database queries. What can I use?

A: 

Perhaps you could use anonymous functions, such that the free variables are bound as parameters in the anonymous function, which returns a string with the values populating the free variables. This would work well in conjunction with the extract function inside a closure, wherein the keys from your array become real variables within a local scope necessary to evaluate the variables referenced in the format string.

Or there is probably a relatively simple version using eval (this is just an illustration, not tested code):

function named_printf ($format_string, $values) {
    extract($values);
    $result = $format_string;
    eval('$result = "'.$format_string.'";');
    return $result;
}

echo named_printf ('Hello $msg', array('msg'=>'World'));

PHP's scope control mechanisms are a bit scary, so you may want to verify that this isn't going to leak variables into scope all over the place. It'd also be worth santising input.

Gian
`eval` isn't a solution, it's a problem.
Kendall Hopkins
Agreed. That's why I suggested using a closure in the first instance, and then said that there was a "relatively simple version" using eval, if OP wanted a generalised solution in the manner of printf. It _is_ acceptable to use eval if you are actually implementing system-level generic functionality.
Gian
@Gian "It is acceptable to use eval if you are actually implementing system-level generic functionality.", I disagree there's almost *always* a better way. `eval` is hard to maintain and bad for performance and security.
Kendall Hopkins
+1  A: 

PHP has built in support for evaluating variables inside of double-quoted strings. While you can't "pass-in" an array to it, you could think of the current variable scope as the input for the string builder "function".

$name = "Kendall Hopkins";
print "Hello {$name}!"; //Hello Kendall Hopkins!

http://www.php.net/manual/en/language.types.string.php#language.types.string.parsing

EDIT:

A more flexible solution might be to abstract out the code into a closure. This doesn't depend on eval and will probably run faster.

$hello_two = function ( array $params ) {
    extract( $params );
    return "Hello $name1 and $name2!";
}

//Hello User and Kendall
$hello_two( array( "var1" => "User", "var2" => "Kendall" ) );
Kendall Hopkins
Yes, I know, but I want a function that does this so I can re-use my template string in several places. In other words I don't want the variables bound at the time of writing. Using this php functionality makes the string a one-off and I can't re-use that.
@user151841 Take a look at my edit.
Kendall Hopkins
This seems to demand a separate closure per format string. Is that really what was being asked?
Gian
@Gian Yes, each string needs to be compiled into opcode before the PHP is run in order to gain decent performance. Closures provide a portable way of "moving" around PHP string building construct. Anything that's eval'ed requires the PHP compiler to read it EACH time the code is run, regardless of how many times it's run.
Kendall Hopkins
@Kendall, That wasn't what I was asking. I'm suggesting that you have to write a new function for every different format string you may wish to use. That seems a little brittle. I agree eval is not a great solution, but I'm trying to think if there is some better way that is general (i.e. parameterised in terms of the format string _and_ the data value array).
Gian
A: 

preg_replace/e or preg_replace_callback is your best bet

  $vars = array('name' => 'Joe', 'age' => 25);
  $str = "@name is @age years old";
  echo preg_replace('/@(\w+)/e', '$vars["$1"]', $str);
stereofrog
Stereofrog -- the awesomest username with the awesomest answers!