4

Does anyone know a better implementation of sprintf in PHP? I was looking for something like the string formatting we have in python:

print "Hello %(name)s. Your %(name)s has just been created!" % { 'name' : 'world' }
# prints::: Hello world. Your world has just been created!

This is pretty handy to avoid repeating the same variables without need, such as:

sprintf("Hello %s. Your %s has just been created!", 'world', 'world');
# prints::: Hello world. Your world has just been created!

I guess is fairly easy to build this on my own, but don't wanna reinvent the wheel, if you know what I mean... but I could not find (maybe wrong search keywords) any trace of this anywhere.

If anyone can help, I appreciate.

Cheers,

bruno.braga
  • 1,001
  • 12
  • 21
  • 1
    This has a particular interesting usage if you want to build extensive queries with MySQL's _INSERT ... ON DUPLICATE KEY UPDATE Syntax..._ [http://dev.mysql.com/doc/refman/5.0/en/insert-on-duplicate.html](http://dev.mysql.com/doc/refman/5.0/en/insert-on-duplicate.html) – bruno.braga Jan 19 '12 at 02:01
  • Please see : http://stackoverflow.com/questions/5701985/vsprintf-or-sprintf-with-named-arguments-or-simple-tempalte-parsing-in-php and http://stackoverflow.com/questions/7435233/name-php-specifiers-in-printf-strings – lqez Jan 19 '12 at 02:07

5 Answers5

8

You can use positional (but not named) arguments to do this, for example

printf('Hello %1$s. Your %1$s has just been created!', 'world'); 

A word of caution here: you must use single quotes, otherwise the dollar signs will cause PHP to try to substitute $s with the value of this variable (which does not exist).

If you want named arguments then you will have to do this with a regular expression; for example, see How to replace placeholders with actual values?.

Community
  • 1
  • 1
Jon
  • 428,835
  • 81
  • 738
  • 806
5

You can repeat the same placeholder with PHP's sprintf (though it might not look as nice):

$str = sprintf('%1$s %1$s', 'yay');
// str: 'yay yay'

You can use n$ right after the % in a placeholder, where n is the argument position (so %1$s refers to the first argument (as a string), %2$s refers to the second, etc.). As you can see above, when you use placeholders that are positionally-bound, you can repeat them within the string without duplicating arguments when you call sprintf.

John Flatness
  • 32,469
  • 5
  • 79
  • 81
3

The following code was stolen from a post by Salathe on TalkPHP.

$szAdjective = 'fluffy';
$szNoun = 'cat';

printf('Yesterday, I saw a %s. '.
       'It was a %s %s! I have '.
       'never seen a %s quite so %s.',
       $szNoun,
       $szAdjective,
       $szNoun,
       $szNoun,
       $szAdjective);

printf('Yesterday, I saw a %1$s. '.
       'It was a %2$s %1$s! I have '.
       'never seen a %1$s quite so %2$s.',
       $szNoun,
       $szAdjective);

The above two expressions are equivalent and will both output

"Yesterday, I saw a cat. It was a fluffy cat! I have never seen a cat quite so fluffy."

kba
  • 19,333
  • 5
  • 62
  • 89
2

I answered this very question in another post: vsprintf or sprintf with named arguments, or simple template parsing in PHP

But this has the same format youre looking for!

This is really the best way to go imho. No cryptic characters, just use the key names!

As taken from the php site: http://www.php.net/manual/en/function.vsprintf.php

function dsprintf() {
  $data = func_get_args(); // get all the arguments
  $string = array_shift($data); // the string is the first one
  if (is_array(func_get_arg(1))) { // if the second one is an array, use that
    $data = func_get_arg(1);
  }
  $used_keys = array();
  // get the matches, and feed them to our function
  $string = preg_replace('/\%\((.*?)\)(.)/e',
    'dsprintfMatch(\'$1\',\'$2\',\$data,$used_keys)',$string);
  $data = array_diff_key($data,$used_keys); // diff the data with the used_keys
  return vsprintf($string,$data); // yeah!
}

function dsprintfMatch($m1,$m2,&$data,&$used_keys) {
  if (isset($data[$m1])) { // if the key is there
    $str = $data[$m1];
    $used_keys[$m1] = $m1; // dont unset it, it can be used multiple times
    return sprintf("%".$m2,$str); // sprintf the string, so %s, or %d works like it should
  } else {
    return "%".$m2; // else, return a regular %s, or %d or whatever is used
  }
}

$str = <<<HITHERE
Hello, %(firstName)s, I know your favorite PDA is the %(pda)s. You must have bought %(amount)s
HITHERE;

$dataArray = array(
  'pda'         => 'Newton 2100',
  'firstName'   => 'Steve',
  'amount'      => '200'
);
echo dsprintf($str, $dataArray);
// Hello, Steve, I know your favorite PDA is the Newton 2100. You must have bought 200
Community
  • 1
  • 1
roberthuttinger
  • 1,172
  • 1
  • 17
  • 31
0

I've written a small component that allow you to make name substitutions in php strings. It's called StringTemplate. With it you can get what you want with a code like this:

$engine = new StringTemplate\Engine;

$engine->render(
   '"Hello {name}. Your {name} has just been created!"',
   [
      'name' => 'world',
   ]
);
//Prints "Hello world. Your world has just been created!"

Multidimensional array value are allowed too. Hope that can help.

Nicolò Martini
  • 5,182
  • 4
  • 32
  • 38