Codificación de Datos: Una Guía UTF-8 para PHP y MySQL

Como desarrollador PHP o MySQL, una vez que pasas m√°s all√° de los confines de los c√≥modos conjuntos de caracteres s√≥lo en ingl√©s, te encuentras r√°pidamente enredado en el maravillosamente y extra√Īo mundo de UTF-8.

Una Mirada R√°pida UTF-8 Primer

Unicode es un est√°ndar de la industria de computaci√≥n ampliamente utilizado, que define un mapeo completo de valores √ļnicos de c√≥digos num√©ricos a los caracteres de la mayor√≠a de los conjuntos de caracteres escritos hoy en d√≠a, para ayudar con la interoperabilidad de los sistemas y el intercambio de datos.

UTF-8 es una codificaci√≥n de amplitud variable (variable-width encoding) que puede representar todos los caracteres en el conjunto de caracteres Unicode. Fue dise√Īado para mantener la retrocompatibilidad con ASCII y para evitar las complicaciones con Endianness y marcas de orden de bytes en UTF-16 y UTF-32. UTF-8 se ha convertido en la codificaci√≥n de caracteres dominante para la World Wide Web, lo que representa m√°s de la mitad de todas las p√°ginas Web.

UTF-8 codifica cada carácter utilizando de uno a cuatro bytes. Los primeros 128 caracteres de Unicode corresponden uno a uno con ASCII, haciendo válido el texto ASCII, al igual que el texto con codificación UTF-8. Es por esta razón que los sistemas que están limitados al uso del conjunto de caracteres en inglés, están aislados de las complejidades que de lo contrario pueden surgir con UTF-8.

Por ejemplo, el c√≥digo hexadecimal Unicode para la letra A es U + 0041, que en UTF -8 simplemente est√° codificado con el byte √ļnico 41. En comparaci√≥n, el c√≥digo hexadecimal Unicode para el car√°cter es U+233B4, que en UTF-8 se codifica con los cuatro bytes F0, A3, B4, 8E.

En un trabajo previo a éste, comenzamos a encontrar problemas de codificación de datos al mostrar biografías de artistas de todo el mundo. Pronto se hizo evidente que había problemas con los datos almacenados ya que a veces los datos se codifican correctamente y otras veces no.

Esto llevó a los programadores a implementar una mezcla de parches, a veces con JavaScript, a veces con etiquetas meta charset HTML, a veces con PHP, y así sucesivamente. Pronto, terminamos con una lista de 600.000 biografías de los artistas, con la información codificada al doble o triple, con datos almacenados en diferentes formas, dependiendo de quién había programado la característica o aplicado el parche. Un clásico nido de ratas técnico.

De hecho, navegar por problemas UTF-8 relacionados con codificaci√≥n de datos, puede ser una experiencia frustrante. Este post proporciona un ‚Äúlibro de cocina‚ÄĚ conciso para abordar estos problemas cuando se trabaja con PHP y MySQL particularmente, basado en la experiencia pr√°ctica y las lecciones aprendidas (y con agradecimientos, en parte, a la informaci√≥n descubierta aqu√≠ y aqu√≠ en el camino).

En concreto, vamos a cubrir lo siguiente en este post:

  • Mods que tendr√°s que hacer a tu archivo php.ini y c√≥digo PHP.
  • Mods que tendr√°s que hacer a tu archivo my.ini y otros problemas relacionados con MySQL que se deben tener en cuenta (incluyendo mods de configuraci√≥n, necesarias si est√°s utilizando Sphinx )
  • C√≥mo migrar datos de una base de datos MySQL previamente codificada en latin1 en lugar de utilizar una codificaci√≥n UTF-8

PHP y la Codificación UTF-8 РModificaciones en el Archivo php.ini:

Lo primero que debes hacer es modificar tu archivo ‚Äėphp.ini‚Äô para utilizar UTF-8 como el conjunto de caracteres por defecto:

default_charset = "utf-8";

(Nota: Puedes utilizar posteriormente phpinfo()para verificar que éste se haya ajustado correctamente).

Bien, ahora PHP y UTF-8 deber√≠an funcionar bien juntos. ¬ŅVerdad?

Bueno, no exactamente. De hecho, ni est√°n cerca de hacerlo.

Si bien este cambio se asegurar√° de que PHP siempre de salida a UTF-8 como codificaci√≥n de caracteres (en los encabezados tipo‚Äďcontenido de respuesta de navegador), todav√≠a tienes que hacer una serie de modificaciones en tu c√≥digo PHP, para asegurarte de que procesa y genera caracteres UTF-8 correctamente.

PHP y la Codificación UTF-8 РModificaciones a tu Código:

Para asegurarte de que tu código PHP se maneje bien en el sandbox de codificación de datos UTF-8, aquí están las cosas que debes hacer:

  • Ajusta UTF-8 como el conjunto de caracteres para todas las salidas de los encabezados por tu c√≥digo PHP.

    En cada encabezado de salida PHP, especifica UTF-8 como la codificación:

    header(‚ÄėContent-Type: text/html; charset=utf-8‚Äô);

  • Especifica UTF-8 como el tipo de codificaci√≥n para XML
      <?xml version="1.0" encoding="UTF-8"?>
    
  • Elimina caracteres no compatibles de XML

Dado que no todos los caracteres UTF-8 se aceptan en un documento XML, necesitas eliminar cualquier tipo de caracteres de cualquier XML que generes. Una funci√≥n √ļtil para hacer esto (la cual encontr√© aqu√≠) es la siguiente:

    function utf8_for_xml($string)
    {
      return preg_replace('/[^\x{0009}\x{000a}\x{000d}\x{0020}-\x{D7FF}\x{E000}-\x{FFFD}]+/u',
                          ' ', $string);
    }

He aquí cómo puedes utilizar esta función en tu código:

    $safeString = utf8_for_xml($yourUnsafeString); 
  • Especifica UTF-8 como el conjunto de caracteres para todo el contenido HTML

    Para el contenido HTML, especifica UTF-8 como la codificación:

     <meta http-equiv="Content-Type" content="text/html; charset=utf-8">  
    

    En formularios HTML, especifica UTF-8 como la codificación:

     <form accept-charset="utf-8">
    
  • Especifica UTF-8 como la codificaci√≥n de todas las llamadas a htmlspecialchars

    Por ejemplo:

     htmlspecialchars($str, ENT_NOQUOTES, "UTF-8")
    

Nota: A partir de PHP 5.6.0, el valor default_charset se utiliza por defecto. A partir de PHP 5.4.0, UTF-8 venía por defecto, pero antes de PHP 5.4.0, se usó la norma ISO-8859-1 como predeterminado. Por lo tanto, es una buena idea especificar siempre explícitamente a UTF-8, para estar seguros, a pesar de que éste argumento es técnicamente opcional.

También ten en cuenta que, para UTF-8, htmlspecialchars y htmlentities se pueden utilizar indistintamente.

  • Ajusta UTF-8 como el conjunto de caracteres por defecto para todas las conexiones de MySQL

Especifica UTF-8 como el conjunto de caracteres por defecto para usar al intercambiar datos con la base de datos MySQL, utilizando mysql_set_charset:

$link = mysql_connect('localhost', 'user', 'password');
mysql_set_charset('utf8', $link);

Ten en cuenta que, a partir de PHP 5.5.0, mysql_set_charset est√° en desuso, y mysqli::set_charset se debe utilizar en su lugar:

  $mysqli = new mysqli("localhost", "my_user", "my_password", "test");

  /* check connection */
    if (mysqli_connect_errno()) {
        printf("Connect failed: %s\n", mysqli_connect_error());
        exit();
    }
    
    /* change character set to utf8 */
    if (!$mysqli->set_charset("utf8")) {
        printf("Error loading character set utf8: %s\n", $mysqli->error);
    } else {
        printf("Current character set: %s\n", $mysqli->character_set_name());
    }
    
    $mysqli->close();
  • Usa siempre versiones compatibles de las funciones de manipulaci√≥n de cadenas UTF-8

Hay varias funciones de PHP que pueden fallar, o al menos no comportarse como se esperaba si la representaci√≥n del car√°cter necesita m√°s de 1 byte (como lo hace UTF-8). Un ejemplo es la funci√≥n strlen, que devolver√° el n√ļmero de bytes en lugar de la cantidad de caracteres.

Hay dos opciones disponibles para hacer frente a esto:

  • Las funciones iconv que est√°n disponibles por defecto con PHP, proporcionan versiones compatibles de varios bytes de muchas de estas funciones (por ejemplo, iconv_strlen, etc.). Sin embargo, recuerda que las cadenas que suministres a estas funciones deben a su vez ser codificadas correctamente.
  • Tambi√©n existe la extensi√≥n mbstring a PHP (informaci√≥n sobre la activaci√≥n y configuraci√≥n est√° disponible aqu√≠). Esta extensi√≥n proporciona un conjunto completo de funciones que responden adecuadamente por la codificaci√≥n multibyte.

MySQL y la Codificación UTF-8 РModificaciones en el Archivo my.ini:

En el lado de MySQL / UTF-8 de las cosas, modificaciones al archivo my.ini son requeridas de la siguiente manera:

  • Establece los siguientes par√°metros de configuraci√≥n despu√©s de cada etiqueta correspondiente: [client] default-character-set=UTF-8
      [mysql]
      default-character-set=UTF-8
        
      [mysqld]
      character-set-client-handshake = false #force encoding to uft8
      character-set-server=UTF-8
      collation-server=UTF-8_general_ci
        
      [mysqld_safe]
      default-character-set=UTF-8
    
  • Despu√©s de hacer los cambios anteriores en tu archivo my.ini, reinicia el MySQL daemon.
  • Para comprobar que todo ha sido configurado correctamente para utilizar la codificaci√≥n UTF-8, ejecuta la siguiente consulta:
      mysql> show variables like 'char%';
    

El resultado debe ser algo asi:

        | character_set_client        | UTF-8                       
        | character_set_connection    | UTF-8                       
        | character_set_database      | UTF-8                       
        | character_set_filesystem    | binary                    
        | character_set_results       | UTF-8                       
        | character_set_server        | UTF-8                       
        | character_set_system        | UTF-8                       
        | character_sets_dir          | /usr/share/mysql/charsets/

Si por el contrario ves latin1 enumerado para cualquiera de estos, comprueba tu configuraci√≥n y aseg√ļrate de haber reiniciado correctamente el MySQL Daemon.

MySQL y la Codificación UTF-8 РOtras Cosas a Considerar:

  • MySQL UTF-8 es en realidad una aplicaci√≥n parcial del conjunto de caracteres UTF-8. En concreto, la codificaci√≥n de datos MySQL UTF-8, utiliza un m√°ximo de 3 bytes, mientras que se requieren 4 bytes para codificar el conjunto completo de caracteres UTF-8. Esto est√° bien para todos los caracteres del idioma, pero si necesitas sostener s√≠mbolos astrales (cuyos puntos de c√≥digo oscilan entre U + 010000 a U + 10FFFF), estos requieren una codificaci√≥n de cuatro bytes que no se puede sostener en MySQL UTF-8. En MySQL 5.5 0.3, esto se discuti√≥ con la adici√≥n de apoyo al conjunto de caracteres utf8mb4, que utiliza un m√°ximo de cuatro bytes por car√°cter y por lo tanto sostiene el conjunto completo de caracteres UTF-8. As√≠ que, si est√°s utilizando MySQL 5.5.3 o posterior, utiliza utf8mb4 en lugar de UTF-8 como conjunto de caracteres de base de datos / tabla / fila. M√°s informaci√≥n disponible aqu√≠.
  • Si el cliente que se conecta no tiene ninguna forma de especificar la codificaci√≥n para su comunicaci√≥n con MySQL, una vez establecida la conexi√≥n, puede que tengas que ejecutar el siguiente comando / consulta:
      set names UTF-8;
    
  • Al determinar el tama√Īo de los campos varchar al modelar la base de datos, no te olvides que los caracteres UTF-8 pueden requerir hasta 4 bytes por car√°cter.

 

Contenido extraído de:  https://www.toptal.com/python/por-que-hay-tantos-pythons/es
Autor: Francisco Sanchez Clariá РSoftware Engineer @ Toptal (translated by Marisela Ordaz)