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

Aug 30 2008

Introducción:

En muchas ocasiones querremos separar el texto de nuestras páginas web de la programación propiamente dicho. En ocasiones será para poder localizar nuestra página (tenerla en diferentes idiomas), otras simplemente por comodidad.

Dichos textos, generalmente estarán mezclados con contenido dinámico que se tendrá que reemplazar.

Supongamos que tenemos una página con categorías y entradas de algún tipo y queremos mostrar en el título el camino a una subcategoría, y la cantidad de subcategorías y de elementos que tiene.

Podríamos hacer algo similar a esto usando echo:

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

No es difíl darse cuenta de que, aunque funciona, resulta complicado modificar y de mantener; especialmente si la persona que tiene que retocarlo no sabe programar. Además si quisiesemos traducirlo a otros idiomas tendríamos que traducir cada parte individualmente (con el echo).

Otra alternativa es usar la función printf, que permite crear una cadena de formato separada de los datos y nos permite una separación cómoda y eficiente; además de que facilita mucho todo lo referente a la localización de texto.

Con printf, podríamos hacer algo similar a esto:

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

El define (o una posible variable global o clave en un array), puede estar en un fichero distinto con todos los textos a localizar o a modificar de fácil acceso. También puede estar en una base de datos o en un fichero de texto que pueda editar otra persona. Además nos permitiría dar formato a los números o a las cadenas.

Por otra parte perdemos la posibilidad de cambiar de orden los elementos. El printf requiere que los elementos estén en orden.

Con strtr o con str_replace o str_ireplace podemos conseguir una “cadena de formato” que permita cambiar el orden de los elementos de una forma ligera y eficiente:

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)  
)));  

Se puede ver que he añadido una opción categories_rev, por si la persona que se encarga de traducir o de retocar los textos de la página determina que sería interesante colocar las categorías mas internas antes que las mas generales (por ejemplo para optimización para motores de búsqueda). Con preg_(i)replace, podríamos conseguir el mismo efecto que con strtr, si en vez de usar un array asociativa, usamos dos arrays, uno con las claves y otro con los valores.

Para finalizar decir que con preg_replace y preg_replace_callback, podemos conseguir un reemplazado mucho mas potente, aunque algo mas complejo y lento. Pondré un ejemplo en el que se nos permite aplicar una función a un parámetro y obtener los parámetros:

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')));  

Conclusión:

  • echo es práctico y rápido para cosas sencillas que no requieran formato y que no se tengan que modificar o traducir.
  • printf es práctico para cadenas que requieran formato y una localización sencilla.
  • echo+strtr nos permite dar un formato ligero pero más potente que las otras dos opciones.
  • echo+preg_replace_callback nos permite dar un formato mas pesado, pero tremendamente flexible, útil incluso para templates.

Generalmente echo+strtr suele dar muy buenos resultados para localización de texto, y ofrece un código compacto de bastante rendimiento y sin ninguna dependencia adicional.