What's the difference in using sprintf in relation to using variables within the string?

6

Looking at some libraries and examples, it is common to find sprintf in some situations that I find strange.

Reading your documentation I noticed that there are several types of formatting , however I see some cases that I do not see usefulness to use this, since it just inserts a variable into a string.

A real example, taken from StatusPage.io:

$ch = curl_init(sprintf("%s/pages/%s/metrics/%s/data.json", $BASE_URI, $PAGE_ID, $METRIC_ID));

An additional example, taken from the Yubico / Yubikey : / p>

$query = sprintf("SELECT username FROM demoserver WHERE id='%s'",
           pg_escape_string($identity));

What is the difference of strintf against this:

$ch = curl_init("$BASE_URI/pages/$PAGE_ID/metrics/$METRIC_ID/data.json");

$query = 'SELECT username FROM demoserver WHERE id="'.pg_escape_string($identity).'"';

What would be the advantage of using sprintf in situations like this?

    
asked by anonymous 18.01.2017 / 01:21

4 answers

6

The function sprintf() formats a string based on the order of the parameters defined in the first argument, which makes everything very dynamic, allowing you to format strings with a lot of flexibility.

An example of great utility is when you need to write data in different languages.

Formatting for templates, output of data, etc.

Common cases are names of people, personal pronouns, addresses, etc.

The example below elucidates better:

// Define idioma. Modifique para "pt" e veja como é a saída dos dados.
$l = 'ja';

// tratamento pessoal
$t['ja'] = '様';
// nome da pessoa
$n['ja'] = array('name' => '太郎', 'surname' => '山田');
// endereço
$a['ja'] = array(
    'postal_code' => '104-0045',
    'country_name' => '日本',
    'province_name' => '東京都',
    'city_name' => '東京',
    'city_subregion' => '中央区',
    'county_name' => '明石町',
    'building' => 'マンション名123'
);

// tratamento pessoal
$t['pt'] = 'Sr(a)';
// nome da pessoa
$n['pt'] = array('name' => 'Fulano', 'surname' => 'Silva');
// endereço
$a['pt'] = array(
    'postal_code' => '06453-040',
    'country_name' => 'Brasil',
    'province_name' => 'SP',
    'city_name' => 'Barueri',
    'city_subregion' => '',
    'county_name' => 'Alphaville',
    'building' => 'Calçadas das Papoulas 123'
);

// regra de formatação para idioma japonês
$nf['ja'] = '%3$s %2$s %1$s'; // nome
$af['ja'] = '〒%1$s %2$s %3$s %4$s %5$s %6$s %7$s'; // endereço

// regra de formatação para idioma português
$nf['pt'] = '%1$s %2$s %3$s'; // nome
$af['pt'] = '%7$s %6$s'.(empty($a[$l]['city_subregion'])? '': '- %5$s').', %4$s - %3$s, %1$s, %2$s'; // endereço

// escreve o nome
echo sprintf($nf[$l], $t[$l], $n[$l]['name'], $n[$l]['surname']).'<br>';
// escreve o endereço
echo sprintf(
    $af[$l],
    $a[$l]['postal_code'],
    $a[$l]['country_name'],
    $a[$l]['province_name'],
    $a[$l]['city_name'],
    $a[$l]['city_subregion'],
    $a[$l]['county_name'],
    $a[$l]['building']
);

Note that at the time of writing, nothing changes. The structure of the data in the arrays also does not change. Both are the same. What makes the difference is the formatting rule, which informs the sprintf() function how and where to concatenate the other parameters.

Try to try to get to the same result without using the function. It becomes complicated with many conditionals in a tangle of codes. A nightmare.

This is an example only for 2 languages and is a very simple and "easy" case. There are more complex things, however, the above example is enough for the one asking the question.

Number formatting

There are several other utilities for using the function, such as converting a number in scientific notation format to decimal / float.

$n = '5.3882598876953E-5';

// Esse trecho é para obter o exponente. Não é relevante para a questão
if ($p = strpos($n, '-')) {
    $decimals = strlen(substr($n, 0, $p - 1));
    $exponent = substr($n, $p + 1);
    $decimals += !empty($exponent)?$exponent - 1: 0;
} else {
    $decimals = strlen(substr($n, strpos($n, '.') + 1));
}

echo $n.'<br>'.sprintf('%.'.$decimals.'f', 1 * $n);

Imagine doing this without the function sprintf() .

Opinion

Finally, personal opinion, but without wanting to disparage anyone, I find it unnecessary to use at all just to make the code beautiful. It does not make sense to use cases where the format is static, for example. It would only be consuming unnecessary processes.

Example of situation where it becomes unnecessary

$ch = curl_init(sprintf("%s/pages/%s/metrics/%s/data.json", $BASE_URI, $PAGE_ID, $METRIC_ID));

It makes no difference if you make an organized and standardized concatenation.

$ch = curl_init($BASE_URI.'/pages/'.$PAGE_ID.'/metrics/'.$METRIC_ID'./data.json');

As mentioned above, the format in this case is static. There is no need to use a dynamic formatting feature for something static.

$var1 = 'A';
$var2 = 'B';
$var3 = 'C';
echo $var1.''.$var2.''.$var3;
//Tempo: 596μs e algumas vezes a 610μs
// Não faz menor diferença usar vírgula ou ponto para concatenar.

$var1 = 'A';
$var2 = 'B';
$var3 = 'C';
echo sprintf('%1$s %2$s %3$s', $var1, $var2, $var3);
//Tempo: 786μs, porém, fica "flutuando" entre 880μs e 900μs

μs: microseconds

The difference for a single single line is 2 to 3 microseconds. Of course, for a single run it is irrelevant, but if the application takes the function to do all concatenations, somatize them. If you have 100 concatenations in the codes, it is no longer irrelevant. A loss of performance for an unnecessary "whim".




The examples have didactic purpose. They are merely illustrative.

    
18.01.2017 / 07:48
5
The sprintf in PHP has basically the same function as in C, which is to allow the formatting capabilities of available values in printf to be applied to string , instead of playing in terminal.

I agree, the example you gave does not illustrate a case where sprintf is really useful. However, in my opinion, the code has gotten cleaner.

But here's a case where he can help.

$pi = 3.14159265359;
$piResumido = sprintf("PI com duas casas decimais: %.2f",$pi);
echo $piResumido;
    
18.01.2017 / 01:35
3

Keep in mind that the sprintf function can do much more than simply reserve a space to be populated with an argument.

It is much more comprehensive than that. Through this function you can format numbers with fills with zero to the left, you can process integers and floats .

Example:

sprintf('%010.2f', 11.66); // '0000011.66'
sprintf('%010d', 11.66); // '0000000011'

That is, the argument can be rendered in a number of ways.

It is worth remembering that between the call of a function and a declaration of a string, the second case would be more performative.

I'm not here to preach micro-optimization (even because I hate it), but I believe that in the example described in the question there is no real benefit in using sprintf , though I have to confess who in some cases (such as crafting a path which is a process that must be careful) I use sprintf for organization purposes, it does not matter to me the performance, since the performance does not depend on a function only, but on the whole work. >

I do not say that you prioritize legibility or performance, but rather be moderate between the two, because everything in this life without balance creates problems.

    
18.01.2017 / 10:45
2

Visibility

Sprintf can make it easier to understand and improve the visibility of a piece of code, consider the following examples with small variables:

// exemplo 1
$ch = curl_init(sprintf("%s/pages/%s/metrics/%s/data.json", $BASE_URI, $PAGE_ID, $METRIC_ID));

// exemplo 2
$ch = curl_init("$BASE_URI/pages/$PAGE_ID/metrics/$METRIC_ID/data.json");

// exemplo 3
$ch = curl_init("{$BASE_URI}/pages/{$PAGE_ID}/metrics/{$METRIC_ID}/data.json");

// exemplo 4
$ch = curl_init($BASE_URI.'/pages/'.$PAGE_ID.'/metrics/'.$METRIC_ID.'/data.json');

All were easy to understand, there was not much difference, although I prefer example 1 and 3.

But now see this other scenario with "big" variants, it becomes even clearer that using sprintf improves visibility.
Compare the ugly and beautiful example and realize that it is much easier to understand the set of information the string wants to pass on.

// feio
// Quebrei em linhas para ficar "menos pior"
$description  = "Ação: {$acao->getDescricao()} (ID: {$acao->getDescricao()}), ";
$description .= "Pessoa: {$pessoa->getDescricao()} (ID: {$pessoa->getId()}), ";
$description .= "Local: {$loja->getDescricao()} (ID: {$loja->getId()}), ";
$description .= "Data: {$data->format('d/m/Y')}";

// bonito
$description .= sprintf('Ação: %s (ID: %d), Pessoa: %s (ID: %d), Local: %s (ID: %d), Data: %s',
    $acao->getDescricao(),
    $acao->getId(),
    $pessoa->getDescricao(),
    $pessoa->getId(),
    $loja->getDescricao(),
    $loja->getId(),
    $data->format('d/m/Y')
);
    
18.01.2017 / 20:21