Hi, my name is Tom - Concat vs. Sprintf vs. In-String Variable

My recent post about lovely exceptions opened very interesting question. In comments bellow the post, in Reddit thread and on Twitter.

A questions about connecting string with variables. You have 3 options. Each has its strong and weak points. How do you pick the right one?

In how many ways you can register a service in Symfony or call a service method in Laravel? PHP and PHP frameworks are so free, that you often have 3+ ways to do one 1 thing. But that comes with a price.

The more approaches you use, the more has to the reader learn about rules of coding and the less space he or she has for real algorithms. You can be really cool by using marginal PHP features, but if nobody except you understands it, you only hurt your code.

How to Pick The Best Solution?

Instead of using what you already use (the most common non-sense argument for everything), try to imagine that your colleague is about to create a pull-request with PHP features you've never seen before.

What should it be like?


Let's try the simplest example possible:

"Hi, my name is Tom."

1. Easy To Learn

<?php

$name = 'Tom';

# 1. concat
$message = 'Hi, my name is ' . $name;

# 2. sprintf
$message = sprintf('Hi, my name is %s', $name);

# 3. in-string variable
$message = 'Hi, my name is $name';

Concat

Just like a + for numbers, but for strings.

Sprintf

What the hell is %s? I'd have to read the manual... %s for strings, %d for numbers.

In-String Variable

I guess I copy the variable inside the string.

2. Easy To Maintain

Let's say, I'd like to tell more about myself.

 <?php

 $name = 'Tom';
+$love = 'PHP';

 # 1. concat
-$message = 'Hi, my name is ' . $name;
+$message = 'Hi, my name is ' . $name . ' and I love ' . $love;

 # 2. sprintf
-$message = sprintf('Hi, my name is %s', $name);
+$message = sprintf('Hi, my name is %s and I love %s', $name, $love);

 # 3. in-string variable
-$message = 'Hi, my name is $name';
+$message = 'Hi, my name is $name and I love $love';

Or quote the name to express it's a variable string:

 <?php

 $name = 'Tom';
 $love = 'PHP';

 # 1. concat
-$message = 'Hi, my name is ' . $name . ' and I love ' . $love;
+$message = 'Hi, my name is "' . $name . '" and I love ' . $love;

 # 2. sprintf
-$message = sprintf('Hi, my name is %s and I love %s', $name, $love);
+$message = sprintf('Hi, my name is "%s" and I love %s', $name, $love);

 # 3. in-string variable
-$message = 'Hi, my name is $name and I love $php';
+$message = 'Hi, my name is "$name" and I love $php';

Concat

1 new element = 2 new dots .. I have to think where to put it. Imagine there will be 4 elements one day. I also type like a dyslexic, so seeing '"' hurts.

Sprintf

Nice to read.

In-String Variable

Still the same.

3. Hard to Fuck Up

Concat

<?php

$name = 'Tom';
$love = 'PHP';
$also =  'to travel';

$message = 'Hi, my name is ' . $name . ' and I love ' . $love . 'and also' . $also;

Can you spot it? This already happened me million times. I never make extra spaces anywhere else in the code.

Sprintf

This also happens...

<?php

$name = 'Tom';
$love = 'PHP';

$message = sprintf('Hi my name is %s and I love %s', $name);
// PHP Warning: sprintf(): Too few arguments

...or this...

<?php

$name = 'Tom';
$love = 'PHP';

$message = sprintf('Hi my name is %s and I love s', $name, $love);
// PHPStan: Call to sprintf contains 1 placeholder, 2 values given.

...but PHP and PHPStan got us covered.

In-String Variable

I must confess, I've tricked you (and myself too until I tried running the code):

And PHP tells you... nothing, it happily prints the wrong strings.

From removing magic quotes, I'm very happy that I don't have to think about what is really working.

This problem is mentioned in PHP Doc though, but who of you even read what %f in sprintf stands for:

And what about this, will it be escaped?

Why stopping there. What about method calls?

<?php

$nameProvider = new NameProvider();

# 1. concat
$message = 'Hi, my name is ' . $nameProvider->provide();

# 2. sprintf
$message = sprintf('Hi, my name is %s', $nameProvider->provide());

# 3. in-string variable
$message = 'Hi, my name is $nameProvider->provide()';
$message = 'Hi, my name is ${nameProvider}->provide()';
$message = 'Hi, my name is ${nameProvider->provide()}';
$message = 'Hi, my name is {$nameProvider->provide()}';
# ?

This is so complex, that there is even a coding standard fixer for such cases:

Next image is not exclusively related to strings, but it shines the same instant coolness that will hunt you down later:

And it's a hell to upgrade such a code (even with Rector). Read more about it in PHP Doc


Whatever You Pick, Stick With It

We all start with one approach, then jump to another whenever we need. Damn you, brain!

That's how such code is born:

And your code readers will have to think:

Don't make them think and stick with one approach in your code. Your readers will have 50 % more brain energy to improve your code then they'd have otherwise.


But still, which one of...

...is your favorite and why? Tell me in comments, I've definitely missed many fuckups.


Happy connecting!


Typo? Fix it, please  and join 49 people who build this website