Why printf Isn’t Working: PHP String Handling

At one point in time, I spent hours troubleshooting why my argument swapping/repeated placeholders in sprintf/printf statements weren’t working properly. I referred to the PHP docs, the internet, cursed the fact that I was doing back-end coding again, etc., and finally gave up and just repeated my variables using normal, un-numbered conversion specifications. Not once did I see anything that answered my question: “Why am I getting a ‘Too few arguments’ warning when using numbered placeholders?”

So, why am I getting a ‘Too few arguments’ warning when using numbered placeholders?

I (and probably you) was/were/are using a double-quoted string.

The issue here is with how PHP handles the String type. PHP is pretty forgiving about a lot of things, and in many cases single-quoted strings and double-quoted strings work just as well as the other. But there are some very important differences, especially when it comes to special characters, conversion specifications and variables.

If you’re having this same problem, change to using single quotes. Voilà, your problem is probably fixed, as long as you didn’t forget to escape any single quotes within your string.

A little more information on strings

The number one thing to remember when working with single-quoted strings in PHP: the only parsed character is the escaped single quote [\'].

Double-quoted strings will parse many escape sequences, and most importantly will expand variables.

If you’re a visual learner, refer to this example:

print '\nHello $username';
print "\nHello $username";

The above code will produce the following (assuming $username has been set to “World”):

\nHello $username
Hello World

So why is this an issue with printf?

I haven’t actually found a source to explain this or verify my postulate. But, I’d bet money on it, and I’m not a betting woman.

Double-quoted strings expand the conversion specification as a variable before printf even gets a chance to evaluate the code. Visually speaking:

$s = "shoes";
$numchucks = "a";
$noun = "wood";
$verb = "chuck";
printf("How much %1$s would %2$s %1$s-%3$s %3$s...",$noun,$numchucks,$verb);

The above code results in a “Too few variables” warning, and nothing prints because printf is actually getting a string that looks like: “How much %1shoes would %2shoes %1shoes-%3shoes %3shoes... In other words, you’re passing it five valid conversion specifications but only four arguments. If you added in the proper number of arguments, like this:

printf("How much %1$s would %2$s %1$s-%3$s %3$s...",$noun,$numchucks,$verb,$noun,$verb);

The resulting output would be:

How much woodhoes would    ahoes chuckhoes-woodhoes chuckoes…

For those readers who are familiar with only the most basic of conversion specs, %2s is perfectly valid. It reads like: “format as a string [s] at least two characters long [2], using the default padding character [a space] and default alignment [left].” So, as you see with ‘a’, it pads the left of the value with a space (exaggerated in above example with three spaces).

In the above examples, we end up with a (somewhat) sensibly formatted string because our interpreted variable $s started with the letter ‘s’, a valid conversion specification character. You will end up with even weirder results if $s begins with an invalid conversion spec character. Take $s="random"; for example. printf tries to use ‘r’ but as it’s not a valid spec character it just leaves out the conversion specifications

How much andom would andom andom-andom andom…

Even more descriptive is if $s doesn’t exist:

How much would…

So, for future reference, if you want to use numbered conversion specifications, here’s the proper way:

printf('How much %1$s would %2$s %1$s-%3$s %3$s...',$noun,$numchucks,$verb);

And you know, I’ve really always wondered—

How much wood would a wood-chuck chuck…

…if a wood-chuck could chuck wood.

2 thoughts on “Why printf Isn’t Working: PHP String Handling”

  1. Thanks for your post. I was receiving this same error and couldn't figure out why. I Googled, "php trouble with printf %1$s" and found your post near the top. Hurray for the Internet, Google, and people like you helping other people they don't even know exist.

    I believe you are correct about the $s first getting interpreted as variable references instead of as a conversion spec for a printf argument.

    The interesting thing is that if you have /n (newline) in your printf's and use single quotes the newline comes out as /n instead of a newline. So that's a different problem.

    I did some more digging and found another solution. You *can* use double quotes, but you must escape the $ in the conversion spec items.

    For example: %1$s becomes %1\$s

    Doing this solves the /n newline issue and gives you the ability to reuse arguments.

    In the end it's one of those "choose your poison" type decisions.

    Thanks again for putting me on the right path!

    1. Yep, with single quotes, newline isn't escaped; the only escaped character is the single quote.

      But thanks for posting about escaping the $ when using a double quote: that does indeed make life so much easier when you need to use all those escaped characters! I've never come across that bit of info.

Comments are closed.