Typecasting Integers to Strings

At work, we are gradually trying to enforce some code style rules to a big legacy codebase. So whenever we fix a bug in long untouched files, we always add another commit that runs a linter tool on our codebase. In code review we often take a look at those commits to check if something could break by those mostly harmless changes. Sometimes these commits highlight some strange bits where we don’t know exactly what the reason behind the code is.

Today a colleague stumbled upon some variable assignments that always called a function and added an empty string to the result. Which looked similar to this snippet:

$var1 = someFunction() . '';
$var2 = otherFunction() . '';

He then asked if we would know about a reason that might explain the concatenation of an empty string to a method result. As a joke I explained that it is obviously a way to cast an integer return value to a string.

$int = 1;
$string1 = (string) $int;
$string2 = $int . '';

As part of the joke I then explained, that the second version is clearly better, as it takes fewer characters to type. After some time I remembered that conversation from earlier and got curious to find other methods of casting an integer into a string. I therefore created another snippet to ask around, which version should be preferred.

$int = 1;

$string1 = '' . $int;
$string2 = "{$int}";
$string3 = (string) $int;

Soon I got some other suggestions that should be added to the list:

$string4 = "$int";
$string5 = strval($int);
$string6 = sprintf("%d", $int);

The person who suggested the strval() and sprintf() functions was interested in some benchmarks, to see if any of the versions would be faster or slower than the others. So I created a small script that creates an array of 1000 * 1000 random numbers between 0 and 9, and then checks how long each of the mentioned methods took to transform those numbers into strings.

<?php

$ints = [];
foreach (range(0, 1000 * 1000) as $c) {
    $ints[] = random_int(0, 9);
}

$methods = [
    'singlequotes' => fn (int $int) : string => '' . $int,
    'brackets' => fn (int $int) : string => "{$int}",
    'doublequotes' => fn (int $int) : string => "$int",
    'stringcast' => fn (int $int) : string => (string) $int,
    'strval-func' => fn (int $int) : string => strval($int),
    'sprintf' => fn (int $int) : string => sprintf("%d", $int),
];

foreach ($methods as $key => $method) {
    $startTime = hrtime(true);
    array_map($method, $ints);
    $endTime = hrtime(true);
    $time = ($endTime - $startTime);
    printf("%s:\t%d%s", $key, $time, PHP_EOL);
}

The result is, that all versions perform mostly the same, except for sprintf which takes considerably longer that the others. As I ran the script a few more times I noticed that the other versions result slightly varied everytime and I could not determine a clear winner.

singlequotes:   712254100
brackets:       754219300
doublequotes:   746071200
stringcast:     749518900
strval-func:    752735500
sprintf:        1321027600

Maybe some day I will tweak my tests a bit more to reduce the random variations to determine which is fastest. But now I will just stick with using the explicit (string) typecast.