string.Format() in PHP

This article was originally published in my blog (affectionately referred to as blargh) on . The original blog no longer exists as I've migrated everything to this wiki.

The original URL of this post was at Hopefully that link redirects back to this page.

.NET's sprintf() equivalent has ordered parameters.

var s = string.Format("My name is {1}, {0}", "John", "Doe");
//s = "My name is Doe, John"

var s = string.Format("My name is {{{0}}}", "John");
//s = "My name is {John}"

I needed that. Here it is, PHPified:

function format($format) {
	$args = func_get_args();
	$format = array_shift($args);
	preg_match_all('/(?=\{)\{(\d+)\}(?!\})/', $format, $matches, PREG_OFFSET_CAPTURE);
	$offset = 0;
	foreach ($matches[1] as $data) {
		$i = $data[0];
		$format = substr_replace($format, @$args[$i], $offset + $data[1] - 1, 2 + strlen($i));
		$offset += strlen(@$args[$i]) - 2 - strlen($i);
	return $format;

There's a lot going on in here that isn't used very often in PHP. Here is a terse explanation of the not-so-obvious features:

  • The PREG_OFFSET_CAPTURE constant tells the regex engine to capture the string index offset of each match.
  • (?=\{) and (?!\}) in the regular expression are positive and negative lookarounds, respectively. The nice thing about that is they're also zero-width matching, which means they won't be returned as part of the match result. These are needed because to output a literal { in a C# string you precede it with another { (a little known fact about C#).
  • substr_replace is an infrequently used, extremely useful function, that replaces a substring within a string, expanding to the given width.
  • The $offset variable tracks the string length delta. Since we're modifying the string, the offsets captured in the regex match becomes out of date.