PHP: echo vs printf vs strtr/str_(i)replace vs preg_replace_callback

Aug 30 2008

Introduction:

It is pretty frequent that we want to separate the text content of our websites from our website’s logic code. Sometimes that’s because we want to translate its text, other times just because of a matter of convenience.

Those texts would be, generally, mixed with dynamic context that we will have to replace.

Let’s suppose that we have a website with categories and posts of any kind and we want to show in the page’s title the path to a subcategory and the count of categories that it contains.

We could do something similar to this, using echo:

echo htmlspecialchars('Categoría: ' . implode(' > ', $path) . ' Subcategorías: ' . (int)$categories_count . ' Entradas: ' . (int)$entries_count);

It is easy to find out that even if it works, it is a bit hard to maintain; specially if the person that has to update it doesn’t know how to code. Furthermore if we would want to translate it to other languages, we would have to translate each part individually (with that echo).

Another option is to use printf function, that allows you to create a string of the format separating the actual data and allows us to separate it efficiently; also it makes much easier to localize text.

With printf we could do something like this:

define('TITLE_CATEGORY','Categoría: %s Subcategorías: %d Entradas: %d');  
printf(TITLE_CATEGORY, implode(' > ', $path), (int)$categories_count, (int)$entries_count));

That define, or a global variable, or a key inside an array, could be in a different file with all the localized texts together. That could also be in a database or in a text file that other person could edit. That would also allow us to format numbers or strings using printf capabilities.

But we lose the capability of change the order of the elements. Since PHP’s printf requires all the elements to be in the original order.

Using strtr, str_replace or str_ireplace, we ca achieve to have a formatting string that could allow to change the order of the elements easily and efficiently.

define('TITLE_CATEGORY', 'Categoría: {categories} Subcategorías: {categories_count} Entradas: {entries_count}');  
echo htmlspecialchars(strtr(TITLE_CATEGORY, array(  
'{categories}' => implode(' > ', $path),  
'{categories_rev}' => implode(' < ', array_reverse)($path)),  
'{categories_count}' => (int)$categories_count,  
'{entries_count}' => (int)$entries_count)  
)));  

You can see that I have added an additional key categories_rev, in the case the translator or a person updating the texts would decide to put the categories in the reverse order (for example for the SEO sake). Using preg_(i)replace, we could achieve the same result that using strtr if we use two arrays (one with keys and other with values) instead of an associative array.

To conclude, let’s add that using preg_replace and preg_replace_callback, we can achieve a much more powerful replacement, though a bit more complex and slow. Let’s illustrate it with an example, that allows us to apply a function to a parameter and to obtain those parameters:

function my_function($v) {  
    return strtr($v, 'aeios', '43105');  
}  
function my_replace_callback($k) {  
    $rl    = &$GLOBALS['my_replace_list'];  
    $funcs = &$GLOBALS['my_replace_list_funcs'];  
    $k = explode(':', $k[1]);  
    $key = array_shift($k);  
    $r = isset($rl[$key]) ? $rl[$key] : $key;  
    while (sizeof($k)) {  
        $func = array_shift($k);  
        if (in_array($func, $funcs)) $r = $func($r);  
    }  
    return $r;  
}  
function my_replace($t, $l, $f) {  
    $GLOBALS['my_replace_list'] = $l;  
    $GLOBALS['my_replace_list_funcs'] = $f;  
    return preg_replace_callback('/\\{([^\\}]+)\\}/', 'my_replace_callback', $t);  
}  
$path = array('objetos', 'inanimados', 'escolar', 'escritura');  
define('TITLE_CATEGORY', 'Categoría: {categories:my_function:strtoupper} Subcategorías: {categories_count} Entradas: {entries_count}');  
echo htmlspecialchars(my_replace(TITLE_CATEGORY, array(  
    'categories'       => implode(' > ', $path),  
    'categories_rev'   => implode(' < ', array_reverse($path)),  
    'categories_count' => 10,  
    'entries_count'    => 100,  
), array('strtoupper', 'trim', 'my_function')));

Conclusion:

  • echo is practical and the fastest option out there for simple things that doesn’t require formatting and that do not require translating or modification at all.
  • printf is practical for strings that require a format and simple translations
  • echo+strtr allows us to add a light formatting a bit more powerful than previous options
  • echo+preg_replace_callback allows us to add a much more costly formatting; it is the most flexible option out there and useful even for templates

Generally echo+strtr can do good enough results for text localization, offers a compact code with a good balance between computational cost and convenience and do not have any additional dependency on it.