strtouppper, strtolower, ucwords y la codificación

En un proyecto reciente del estudio, hemos tenido que lidiar con la conversión de cadenas a minúsculas, mayúsculas y a frases capitalizadas. El proyecto partía de un Excel en el que todos los datos venían en mayúsculas, y por razones tanto de diseño como de legibilidad decidimos capitalizar los nombres de empresas, sectores, etc.

En principio con PHP lo teníamos fácil, ya que trae unas cuantas funciones conocidas para convertir cadenas a minúsculas, mayúsculas e incluso a formato titular (Cada Letra De Cada Palabra En Mayúscula). Los problemas llegan al hablar de ASCII extendido. Para empezar, nuestra base de datos estaba en UTF8_general_ci, con lo que algo como ESPAÑA viene a ser ESPAÄ’A o algo similar. Aquí os dejo una página en php clarificadora de lo que ocurre al tratar de lograr el objetivo de manera directa:

Nota: la página está guardada como UTF8, y tiene se charset en la correspondiente etiqueta HTML.

$cadenaOriginal = "En EsPaÑA hAy MuchOs HIpÓcritas y PolíTicOS CoRruPtos";

echo ("CADENA ORIGINAL: ".$cadenaOriginal."
"); // Arroja >> CADENA ORIGINAL: En EsPaÑA hAy MuchOs HIpÓcritas y PolíTicOS CoRruPtos echo ("CADENA MAYÚSCULAS: ".strtoupper($cadenaOriginal)."
"); // Arroja >> EN ESPAÑA HAY MUCHOS HIPÓCRITAS Y POLíTICOS CORRUPTOS echo ("CADENA MINÚSCULAS: ".strtolower($cadenaOriginal)."
"); // Arroja >> CADENA ORIGINAL: en espa�a hay muchos hip�critas y pol�ticos corruptos echo ("CADENA CAPITALIZADA: ".ucwords(strtolower($cadenaOriginal))."
"); // Arroja >> CADENA ORIGINAL: En Espa�a Hay Muchos Hip�critas Y Pol�ticos Corruptos

Lo que muestra esto por pantalla bajo Firefox:

Error en la conversión a minúsculas con php y strtolower y ucwords

Ahora nos ponemos manos a la obra, porque parece claro que nos encontramos con varios problemas:

  • Los acentos en minúsculas no los convierte a mayúsculas
  • Los caracteres de ascii extendido los rompe al convertir a minúsculas con strtolower
  • Con ucwords, y dado que tira de strtolower, ocurre 3/4 de lo mismo

La solución, aqui está:

1. El breikendan: decodificar la cadena (utf8_decode)
2. El cruzaito: pasarla a minúsculas (strtolower)
3. El Maikel Yason: pasarla a capitalizado (ucwords)
4. El rabocop: codificarla de nuevo (utf8_encode).

Resumiendo

utf8_encode(ucwords(strtolower($cadena)));

Y aqui os dejo un meneaito de ejemplo. Test Conversión en PHP

Actualización (08/05/2013)
Retomando este hilo, se puede simplificar este proceso mediante las funciones mb_strtoupper y mb_strtolower y mb_convert_case, a las que se le puede pasar la codificación de manera que es más sencillo resolver estas casuísticas.