WordPress code surprise: wp_sprintf

_DSC3433

by Ulf Wendel, on Flickr

Everybody loves PHP’s sprintf(). We use it everywhere. There are just some things it doesn’t do, like format lists in sentences. Three years ago, while working on the Media Library, I needed a way to list categories or tags in a sentence. And, by golly, it would need Oxford commas and localization to be worthy of WordPress.

 

Problem solved

I wrote wp_sprintf() as a wrapper for sprintf(). It uses WordPress filters to customize the formatting directives. WordPress only uses this in one place, the function where I needed to list categories and tags: get_the_taxonomies(). This is used in standard template tags so I estimate wp_sprintf() has run at least a trillion times without fanfare. The time has come to give wp_sprintf() the attention it deserves.

wp_sprintf

The calling interface is identical to sprintf():

string wp_sprintf ( string $format [, mixed $args [, mixed $... ]] )

Internally, wp_sprintf() splits the format string into fragments that begin with a single ‘%’. It sends each fragment through the 'wp_sprintf' filter with the appropriate $args, respective of numbered directives (‘%1$s’). If the filter did not modify the fragment, it is passed through sprintf(). The processed fragments are concatenated and then returned.

Functionally, wp_sprintf() should be identical to sprintf() until a filter is added which implements a new formatting directive, or supersedes any of the standard ones. It is certainly less optimized so it should only be used when a customized directive is needed.

wp_sprintf_l

The only 'wp_sprintf' filter now in core is wp_sprintf_l(), which adds a new directive to format arrays into lists, %l:

wp_sprintf('%s: %l.', 'Tags', array('Cats', 'Dogs', 'Birds'));
=> 'Tags: Cats, Dogs, and Birds.'

Our filter, wp_sprintf_l(), receives a format fragment and the $args that corresponds with its position or number. It returns the first fragment, ‘%s: ‘, unchanged. When it sees ‘%l.’ it replaces the %l directive with a formatted list.

WordPress always tries to make text beautiful. To that end, wp_sprintf_l formats lists with Oxford commas. Two items are “cats and dogs”. Three or more items are “cats, dogs, and birds”. However, if you don’t like Oxford commas you can remove them:

function remove_oxford_commas( $separators ) {
    $separators[ 'between_last_two' ] = ' and ';
    return $separators;
}
add_filter( 'wp_sprintf_l', 'remove_oxford_commas' );

Our list formatter also respects language differences. WordPress translations include wp_sprintf_l()‘s separators so that, for example, the Spanish translation always separates the last two items with ‘ y ‘ with no comma.

You can use <code>wp_sprintf</code> with the ‘%l’ directive anywhere in WordPress since 2.5.0. It’s a lot easier than writing a <code>foreach</code> loop every time.

Something new

Recently a prominent bug crept into some code when the integer argument for %d was wrapped in a call to number_format(). Everything after the first comma was lost. I wished there were a formatting directive to mimic number_format.

While I’m thinking about it, here’s a possibility: ‘%n’. It should automatically localize the thousands separator and decimal point; this alone makes it a compelling upgrade. It should also accept at least one specifier for precision. The other specifiers (sign, padding, alignment, width) would be nice but not necessary.

Lisp alien

Image via Wikipedia

p.s. Lisp rocks!

Yesterday I finished reading Conrad Barski’s entertaining Lisp primer, Land of Lisp. He gives only a whiff of Lisp’s format function but I was blown away. It provides tabulation, justification, iteration, recursion, conversion, conditions, and never mind making a cup of coffee, it could run a chain to compete with Starbucks. Here’s a taste:

(format nil
        "~{~a~#[~;, and ~:;, ~]~}"
        (list "Cats" "Dogs" "Birds"))
"Cats, Dogs, and Birds"