214

I am trying to learn more about the PHP function sprintf() but php.net did not help me much as I am still confused, why would you want to use it?

Take a look at my example below.

Why use this:

$output = sprintf("Here is the result: %s for this date %s", $result, $date);

When this does the same and is easier to write IMO:

$output = 'Here is the result: ' .$result. ' for this date ' .$date;

Am I missing something here?

JasonDavis
  • 48,204
  • 100
  • 318
  • 537

24 Answers24

144

sprintf has all the formatting capabilities of the original printf which means you can do much more than just inserting variable values in strings.

For instance, specify number format (hex, decimal, octal), number of decimals, padding and more. Google for printf and you'll find plenty of examples. The wikipedia article on printf should get you started.

Isak Savo
  • 34,957
  • 11
  • 60
  • 92
  • 54
    Years ago I wrote a nice function for zero-padding numbers. Used for ages before realizing I could simply do `sprintf('%03d', $num)` – DisgruntledGoat Sep 09 '09 at 16:46
  • 8
    PHP *sprintf has all the formatting capabilities* ... actually is missing an important one, `%,8d` for instance to get the rightly aligned 8 digit and commas separated integer. Using `number_format` is less convenient in a `printf` style. – Déjà vu Nov 16 '10 at 06:41
  • 4
    @DisgruntledGoat Can you break down that formatting string ('%03d')? I think it would be really helpful. Thanks. – Mr. Smee Mar 09 '14 at 06:14
  • 7
    You can also repeat and reorder the arguments to display. e.g. `sprintf('%2$s %1$s, my name is also %1$s.', 'World', 'Hello');` resulting in `Hello World, my name is also World.` – Will B. May 17 '17 at 01:41
84

There are many use cases for sprintf but one way that I use them is by storing a string like this: 'Hello, My Name is %s' in a database or as a constant in a PHP class. That way when I want to use that string I can simply do this:

$name = 'Josh';
// $stringFromDB = 'Hello, My Name is %s';
$greeting = sprintf($stringFromDB, $name);
// $greetting = 'Hello, My Name is Josh'

Essentially it allows some separation in the code. If I use 'Hello, My Name is %s' in many places in my code I can change it to '%s is my name' in one place and it updates everywhere else automagically, without having to go to each instance and move around concatenations.

joshwbrick
  • 5,882
  • 9
  • 48
  • 72
  • 9
    This. Now add numbered arguments and translations :] – Konerak Apr 23 '12 at 16:57
  • But you could just as easily have used string concatenation in this case. Or am I missing the point? – Robin Bastiaan Jan 06 '21 at 13:32
  • @RobinBastiaan - in that example, yes, but say the string is "Dear %s, your account number %s has a balance of %0.2f", then it's much harder to use string concatentation when the string comes from the database. You'd have to either write a custom function, or split the string into three strings (for my specific example) – Ben Holness Jul 06 '23 at 02:05
52

Another use of sprintf is in localized applications as the arguments to sprintf don't have to be in the order they appear in the format string.

Example:

$color = 'blue';
$item = 'pen';

sprintf('I have a %s %s', $color, $item);

But a language like French orders the words differently:

$color = 'bleu';
$item = 'stylo';

sprintf('J\'ai un %2$s %1$s', $color, $item);

(Yes, my French sucks: I learned German in school!)

In reality, you'd use gettext to store the localized strings but you get the idea.


Julien Lafont
  • 7,869
  • 2
  • 32
  • 52
Ken Keenan
  • 9,818
  • 5
  • 32
  • 49
41

It's easier to translate.

echo _('Here is the result: ') . $result . _(' for this date ') . $date;

Translation (gettext) strings are now:

  • Here is the result:
  • for this date

When translated to some other language it might be impossible or it results to very weird sentences.

Now if you have

echo sprintf(_("Here is the result: %s for this date %s"), $result, $date);

Translation (gettext) strings is now:

  • Here is the result: %s for this date %s

Which makes much more sense and it's far more flexible to translate to other languages

raspi
  • 5,962
  • 3
  • 34
  • 51
  • 2
    I see where this would be very useful +1 – JasonDavis Nov 06 '12 at 10:36
  • 9
    Although for translations it would probably be better to use position specifiers, to allow some extra flexibility when translating, i.e: `echo sprintf(_("Here is the result: %1$s for this date %2$s"), $result, $date);` That would allow a translation to modify the phrase like `For the date %2$s, the result is %1$s` – PatrikAkerstrand Dec 12 '12 at 13:59
  • 2
    Though even if they are not present in the original string, you can put them in the translation and they will work. – spectras Jan 27 '16 at 12:19
17

The best reason that I have found is that it allows you to place all the language strings in your language file were people can translate and order them as needed - yet you still know that no matter what format the string is in - you wish to show the users name.

For example, your site will say "Welcome back [[User]]" on the top of the page. As the programmer you don't know or care how the UI guys are going to write that - you just know that a users name is going to be shown somewhere in a message.

So you do can embed the message into your code without worring about what that message actually is.

Lang file (EN_US):

...
$lang['welcome_message'] = 'Welcome back %s';
...

Then you can support any type of message in any language by using this in your actual php code.

sprintf($lang['welcome_message'], $user->name())
Xeoncross
  • 55,620
  • 80
  • 262
  • 364
11

why would you want to use it?

It proves very useful when using an (external) source for language strings. If you need a fixed number of variables in a given multilingual string, you only need to know the correct ordering:

en.txt

not_found    = "%s could not be found."
bad_argument = "Bad arguments for function %s."
bad_arg_no   = "Bad argument %d for function %s."

hu.txt

not_found    = "A keresett eljárás (%s) nem található."
bad_argument = "Érvénytelen paraméterek a(z) %s eljárás hívásakor."
bad_arg_no   = "Érvénytelen %d. paraméter a(z) %s eljárás hívásakor."

The inserted variables don't even have to be at the beginning or the end across multiple languages, only their ordering matters.

Of course, you could write your own function to perform this replace, undoubtedly even with some minor performance increases, but it is much faster to just (given you have a class Language to read language strings):

/**
 * throws exception with message($name = "ExampleMethod"):
 *  - using en.txt: ExampleMethod could not be found.
 *  - using hu.txt: A keresett eljárás (ExampleMethod) nem található.
 */
throw new Exception(sprintf(Language::Get('not_found'), $name));

/**
 * throws exception with message ($param_index = 3, $name = "ExampleMethod"):
 *  - using en.txt: Bad argument 3 for function ExampleMethod.
 *  - using hu.txt: Érvénytelen 3. paraméter a(z) ExampleMethod eljárás hívásakor.
 */
throw new Exception(sprintf(Language::Get('bad_arg_no'), $param_index, $name));

It also comes with the full capabilities of printf, thus is also a one-liner for formatting numerous types of variables, for example:

Community
  • 1
  • 1
John Weisz
  • 30,137
  • 13
  • 89
  • 132
9

As mentioned, it allows formatting of the input data. For example, forcing 2dp, 4-digit numbers, etc. It's quite useful for building MySQL query strings.

Another advantage is that it allows you to separate the layout of the string from the data being fed into it, almost like feeding in paramaters. For example, in the case of a MySQL query:

// For security, you MUST sanitise ALL user input first, eg:
$username = mysql_real_escape_string($_POST['username']); // etc.
// Now creating the query:
$query = sprintf("INSERT INTO `Users` SET `user`='%s',`password`='%s',`realname`='%s';", $username, $passwd_hash, $realname);

This method does of course have other uses, such as when printing output as HTML, etc.

Edit: For security reasons, when using a technique as above you must sanitise all input variables with mysql_real_escape_string() before using this method, to prevent MySQL insertion attacks. If you parse unsanitised input, your site and server will get hacked. (With exception to, of course, variables which have been completely constructed by your code and are guaranteed to be safe.)

Samuel Jaeschke
  • 1,126
  • 11
  • 23
8

Using sprintf() is much cleaner and safer to format your string.

For example when you're dealing with input variables, it prevents unexpected surprises by specifying the expected format in advance (for instance, that you're expecting string [%s] or the number [%d]). This could potentially helps with possible risk of SQL injection, but it won't prevent if string consist quotes.

It also helps dealing with floats, you can explicitly specify the digit precision (e.g. %.2f) which saves you from using converting functions.

The other advantages is that most of the major programming languages have their own implementation of sprintf(), so once you get familiar with it, it's even more easier to use, rather than learning a new language (like how to concatenate strings or converting the floats).

In summary, it's a good practise to use in order to have a cleaner and more readable code.

For instance, see the below real example:

$insert .= "('".$tr[0]."','".$tr[0]."','".$tr[0]."','".$tr[0]."'),";

Or some simple example which prints e.g. '1','2','3','4':

print "foo: '" . $a . "','" . $b . "'; bar: '" . $c . "','" . $d . "'" . "\n";

and printing with formatted string:

printf("foo: '%d','%d'; bar: '%d','%d'\n", $a, $b, $c, $d);

where printf() is equivalent to sprintf(), but it outputs a formatted string instead of returning it (to the variable).

Which is more readable?

kenorb
  • 155,785
  • 88
  • 678
  • 743
  • 2
    I don't disagree but also worth mentioning I asked this 6-7 years ago and thinking back I can''t recall once that I have used this since then! I do hope to find a place for it someday and do believe it has a place but funny to think I haven't used it since asking this question that many years ago! – JasonDavis Jul 23 '15 at 22:06
  • I can say that it is somewhat similar to setting variables for PDO in PHP. Also if you want to give it the readability argument, then in that case I prefer to use `sprintf()` broken into multiple lines where the string with placeholders is on 1 line and the values are on a line or even multiple lines below it, all indented correctly of course. Using your demo in the manner I described... http://i.imgur.com/DFi3ur8.png – JasonDavis Jul 23 '15 at 22:12
  • Prepared statements in conjunction with stored procedures is the best way to prevent sql injection, not sprintf. You can still easily sql inject using sprintf, if the string happens to contain a single quote – HumbleWebDev Jun 23 '17 at 11:31
4

This:

"<p>Some big paragraph ".$a["name"]." again have to take care of space and stuff .". $a["age"]. "also would be hard to keep track of punctuations and stuff in a really ".$a["token"]. paragraph.";

Could also be written:

"<p>Some big paragraph {$a['name']} again have to take care of space and stuff .{$a['age']} also would be hard to keep track of punctuations and stuff in a really {$a['token']} paragraph.";

In my opinion this is clearer to read, but I can see the use for localizing, or formatting.

4

Well, sprintf has many capabilities as we know an example as below:

Few months ago I was in need of converting seconds to hour:minute:seconds format Like $t = 494050 //seconds i wanted to print like 137 h 14 m 10 s so i came up with the php function springf() i just hold the seconds in $t and echo sprintf("%02d h %s%02d m %s%02d s", floor($t/3600), $f, ($t/60)%60, $f, $t%60); gives me 137 h 14 m 10 s

sprintf() function is very useful one if we know how to use it.

Sahed
  • 65
  • 1
  • 7
4

Even i though the same thing unless i used it recently. When you generate documents based on user inputs this will be in handy.

"<p>Some big paragraph ".$a["name"]." again have tot ake care of space and stuff .". $a["age"]. "also would be hard to keep track of punctuations and stuff in a really ".$a["token"]. paragarapoh.";

WHich can be easily written as

sprintf("Some big paragraph %s. Again have to take care of space and stuff.%s also wouldnt be hard to keep track of punctuations and stuff in a really %s paragraph",$a,$b,$c);
swordfish
  • 4,899
  • 5
  • 33
  • 61
  • Or you can do this: `"

    Some big paragraph {$a['name']} more words {$a['age']}

    "`. HOWEVER, DO NOT FORGET to sanitize user input or you open your code up to XSS vulnerabilities.
    – thirdender Dec 29 '15 at 19:26
  • You can also use [HEREDOC syntax](http://php.net/manual/en/language.types.string.php#language.types.string.syntax.heredoc). In my opinion, both are easier to read than the sprintf syntax. – thirdender Dec 29 '15 at 19:28
3

Some of the typical cases are where you need more precise control over the output format. It can be tricky to for example make sure a specific value has specific amount of spaces padded to front, depending on its length, or that a number is outputted in a specific precise format.

There are plenty of examples in the PHP manual

Also what comes to your example of "easier to write".. While the echo may be easier to write, the sprintf is easier to read, especially if you have a lot of variables.

Another reason to use sprintf or printf might be that you want to let a user define the output format of some value - you can safely allow them to define a sprintf compatible output format.

Oh, and your example is actually wrong for one part. sprintf returns the string, but echo does not - echo immediately outputs it and returns nothing, while sprintf just returns it.

Jani Hartikainen
  • 42,745
  • 10
  • 68
  • 86
3
  1. If you've used C/C++, then you would be used to the sprintf function.

  2. There is a good chance that the second line is less efficient. Echo is designed as an output command, whereas sprintf is designed to do string token substitution. I'm not a PHP person, but I suspect that there are more objects involved with the echo. If it acts like Java would, it creates a new string each time something is added to the list, so you'd end up with 4 strings created.

Paul
  • 316
  • 2
  • 12
  • Recent editions of Java will convert String concatenation to a StringBuilder behind the scenes. – RodeoClown Sep 06 '09 at 23:15
  • 1
    Strings in PHP are not objects. The second example uses a language construct instead of a function call so will be imperceptibly faster. – Tamlyn Apr 26 '13 at 11:08
3

I usually use sprintf to ensure that a id that come from the user input is an integer for example:

// is better use prepared statements, but this is practical sometimes
$query = sprintf("SELECT * from articles where id = %d;",$_GET['article_id']);

Also is used to do rudimentary templates (for html mails or other things), so you can reuse a the the template in many places:

$mail_body = "Hello %s, ...";
$oneMail = sprintf($mail_body, "Victor");
$anotherMail = sprintf($mail_body, "Juan");

It's very useful also to format numbers in different representations (octal, control the decimal place, etc).

Castro
  • 1,436
  • 2
  • 15
  • 19
  • Isn't it easier just to use intval($_GET["article_id"])? Well, actually it took more typing...*notes down your method" Dag nabbit. – user97410 Sep 06 '09 at 23:34
  • For this type of code, better use the built-in mysql_real_escape_string(): $query = sprintf("SELECT * from articles where id = %d;", mysql_real_escape_string($_GET['article_id'])); – bruno.braga Jan 19 '12 at 01:46
3

sprintf is particularly useful when formatting strings that use numbers. For example,

$oranges = -2.34;
echo sprintf("There are %d oranges in the basket", $oranges);

Output: There are -2 oranges in the basket

Oranges is formatted as a whole number (-2), but will wrap around to positive numbers if one uses %u for unsigned values. To avoid this behaviour, I use the absolute function, abs(), to round the number towards zero as follows:

$oranges = -5.67;
echo sprintf("There are %d oranges in the basket", abs($oranges));

Output: There are 5 oranges in the basket

The end result is a statement with high readability, logical construction, clear formatting, and flexibility for addition of additional variables as required. As the number of variables increases in combination with functions that manipulate these variables, the benefits become more apparent. As a final example:

$oranges = -3.14;
$apples = 1.5;
echo sprintf("There are %d oranges and %d apples", abs($oranges), abs($apples));

Output: There are 3 oranges and 4 apples

The left side of the sprintf statement clearly expresses the string and the types of expected values, while the right side clearly expressed the variables used and how they are being manipulated.

Ro Mc
  • 441
  • 1
  • 4
  • 9
  • I was about to upvote your answer for its extellent first sentence (*"sprintf is particularly useful when formatting strings that use numbers"*), which is indeed the most important point of `sprintf` in PHP, but then seeing those convoluted examples creating fruits from "missing" fruits :), each repeatedly only showing `%d` (and `abs()`, which is irrelevant for the topic), instead of showing some of the plethora of number formatting options (which *would* be relevant), I changed my mind. But if you update the answer, I'll still have my upvote in the fridge for you. :) Thx, cheers! – Sz. Apr 18 '14 at 10:32
  • `echo sprintf()` is an "antipattern". There is absolutely no reason that anyone should ever write `echo sprintf()` -- it should be `printf()` without `echo` every time. – mickmackusa Apr 14 '22 at 06:04
2

The argument is the same one for using templates. You'll want to separate your Textsleev from the actual variable values. Besides the additional powers of sprintf that we're mentioned about it's just a style thing.

worenga
  • 5,776
  • 2
  • 28
  • 50
2

A really good use case for using sprintf is outputting padded formats of numbers and also, when mixing different types in a string. It can be easier to read in many cases and makes it super simple to print different representations of the same variable, especially numerical ones.

Lloyd Moore
  • 3,117
  • 1
  • 32
  • 32
2

Sometimes I got something like this, which I consider a little more easier to read:

$fileName = sprintf('thumb_%s.%s', 
                    $fileId,
                    $fileInfo['extension']);
vise
  • 12,713
  • 11
  • 52
  • 64
2
define('TEXT_MESSAGE', 'The variable "%s" is in the middle!');

sprintf(TEXT_MESSAGE, "Var1");
sprintf(TEXT_MESSAGE, "Var2");
sprintf(TEXT_MESSAGE, "Var3");
Mee
  • 29
  • 1
  • This unexplained answer doesn't "do anything". The return value from `sprintf()` isn't being received by anything. This answer is more misleading than helpful. – mickmackusa Apr 14 '22 at 06:06
2

Using sprintf() function over ordinary concatenation has the advantage that you can apply different types of formatting on the variables to be concatenated.

In your case, you have

$output = sprintf("Here is the result: %s for this date %s", $result, $date);

and

$output = 'Here is the result: ' .$result. ' for this date ' .$date;

Let us take $result = 'passed'; date = '23rd';

Using ordinary concatenation you can only get the output:

Here is the result: passed for this date 23rd

However if you use sprintf() you can get a modified output such as:

$output = sprintf('Here is the result: %.4s for this date %.2s',$result,$date);
echo $output;

Output:

Here is the result: pass for this date 23
2

sprintf() is quite similar to printf(). If you know printf() in details then sprintf() and even vsprintf() is not really difficult to understand.

one of the things that differs sprintf() from printf() is that you will need declare a variable to catch the output from the function as it does not directly print/echo anything. Let see the following code snippets:

printf("Hello %s", "world"); // "Hello world"

sprintf("Hello %s", "world"); // does not display anything

echo sprintf("Hello %s", "world"); // "Hello world"

$a = sprintf("Hello %s", "world"); // does not display anything

echo $a;// "Hello world"

Hope that helps.

Vuong
  • 31
  • 1
  • Of course, `echo sprintf()` is an "antipattern". There is absolutely no reason that anyone should ever write `echo sprintf()` -- it should be `printf()` without `echo` every time. – mickmackusa Apr 14 '22 at 06:03
0

One "outputs", the other "returns", that's one of the main differences.

printf() Outputs

sprintf() Returns

Mohd Abdul Mujib
  • 13,071
  • 8
  • 64
  • 88
0

You have to be careful when using sprintf in loops:

$a = 'Anton';
$b = 'Bert';
$c = 'Corni';
$d = 'Dora';
$e = 'Emiel';
$f = 'Falk';
$loops = 10000000;

$time = microtime(true);

for ($i = 0; $i < $loops; $i++)
{
    $test = $a . $b . $c . $d . $e . $f;
}

$concatTime = microtime(true) - $time;

$time = microtime(true);

for ($i = 0; $i < $loops; $i++)
{
    $test = "$a $b $c $d $e $f";
}

$concat2Time = microtime(true) - $time;

$time = microtime(true);

for ($i = 0; $i < $loops; $i++)
{
    $test = sprintf('%s %s %s %s %s %s', $a, $b, $c, $d, $e, $f);
}

$sprintfTime = microtime(true) - $time;

echo 'Loops: ' . $loops . '<br>';
echo '\'$a . $b . $c . $d . $e . $f\'' . ' needs ' . $concatTime  . 's<br>';
echo '"$a $b $c $d $e $f"' . ' needs ' . $concat2Time  . 's<br>';
echo 'sprintf(\'%s %s %s %s %s %s\', $a, $b, $c, $d, $e, $f)' . ' needs ' . $sprintfTime  . 's<br>';

Which leads to the following times (on my local machine with PHP 7.2):

Loops: 10000000

'$a . $b . $c . $d . $e . $f' needs 1.4507689476013s

"$a $b $c $d $e $f" needs 1.9958319664001s

sprintf('%s %s %s %s %s %s', $a, $b, $c, $d, $e, $f) needs 9.1771278381348s

g3rdn
  • 1
  • 1
0

I use it for messages to users or other "pretty" types of functionality. For instance if I know I'm going to use the user's name.

$name = 'Some dynamic name';

And have multiple messages to use in that situation. (I.e., blocking or following another user)

$messageBlock = 'You have blocked %s from accessing you.';
$messageFollow = 'Following %s is a great idea!';

You can create a general function that does something to the user and add this string, and no matter what the structure of the sentence it should look pretty nice. I always dislike just appending strings together and constantly using dot notation and closing and reopening strings just to make a sentence look good. I was a fan at first like most but this seems pretty useful when multiple strings need to be manipulated and you do not want to hard code the placement of the variable in every time.

Think of it, what looks better?

return $messageOne === true ? $name.'. Please use the next example' : 'Hi '.$name.', how are you?'

Or

$message = $messageOne === true ? 'Option one %s' 
: ($messageTwo === true ? 'Option Two %s maybe?' : '%s you can choose from tons of grammatical instances and not have to edit variable placement and strings');

return sprintf($message, $name);

Sure its an extra step but what if your conditional checks do a bunch of other functional things, then the quotations and appending starts to get in the way of coding functionally.