string.Format() in PHP @ r1

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 https://tmont.com/blargh/2010/1/string-format-in-php. Hopefully that link redirects back to this page.

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

C♯
var s = string.Format("My name is {1}, {0}", "Tommy", "Montgomery");
//s = "My name is Montgomery, Tommy"

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

I needed that. Here it is, PHPified:

php
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.