string.Format() in PHP

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

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:

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

Mmmmm… smells like win.

January 9, 2010   Posted in: c#, php

Leave a Reply