Programador PHP freelance

PEAR DB_DataObject + MySQL + UTF8

El paquete PEAR::DB_DataObject es uno de mis preferidos. Siempre que puedo lo utilizo en mis proyectos y por ahora con muy buenos resultados. Básicamente me ayuda a clarificar el código ya que paso a tener objetos en vez de registros y propiedades en vez de campos. Por otro lado aprovecho su distribución en sistema de ficheros para codificar la capa de datos, cada método en la clase que hace referencia a la tabla o concepto. Por ejemplo, si tengo un método que marca al usuario como eliminado después de hacer unas comprobaciones previas sé que lo voy a poner en la clase que hace referencia a la tabla ‘usuario’ con lo que resulta muy cómodo a la hora de mantener el código legible y bien organizado.
Además el paquete abstrae el acceso al servidor de forma que puedo cambiar mañana mi aplicación de MySQL a PostgreSQL sin tener que cambiar todas las llamadas a bases de datos. Genial, vaya!!!

Pues este paquete tan genial tiene una pequeña carencia con la que me he encontrado hoy.

Resulta que en una codificación he utilizado un MySQL con el charset en UTF8. Todo funcionaba bien si la carga de los datos se realizaba mediante la interfaz web pero en el momento se insertaba algún registro mediante phpMyAdmin con algún caracter especial, como un acento, este aparecía representado con caracteres extraños en la interfaz web de mi aplicación.

El problema es bastante sencillo, MySQL permite definir el charset a varios niveles de comunicación de forma que podemos enviar los datos en UTF8 pero recibirlos en latin1, por ejemplo. Podéis ver más información aquí

En concreto mi problema se debía a que el charset por defecto para los datos devueltos desde el servidor era latin1 lo que producía el efecto explicado anteriormente. Por otro lado el charset para los datos enviados desde el cliente al servidor también se encontraba en latin1 lo que introducía datos en la base de datos en un charset no deseado. El problema se soluciona fijando todas las comunicaciones en UTF8 para lo cual, la forma más sencilla es lanzando la siguiente query:
‘SET NAMES UTF8’.

Esto debe de lanzarse en cada conexión, lo que resulta bastante sencillo de implementar cuando estás trabajando con DB_DataObject ya que lo único que hay que hacer es sobreescribir el método que realiza la conexión.

Pegó aquí un ejemplo donde he optado por añadir una option más que permite lanzar cualquier comando inmediatamente despues de la conexión.

He puesto un feature request en la página del paquete para que los desarrolladores se planteen añadir una option charset que permita definir el mismo sin tener que sobreescribir la clase.

require_once ‘DB/DataObject.php’;

class DB_DataObject_Custom extends DB_DataObject {

  // Overwrite _connect because we add after_connection_command to set charset in mysql
  public function _connect() {
  
    parent::_connect();

    if(isset($GLOBALS['_DB_DATAOBJECT']['CONFIG']['after_connection_command'])) {
      $DB = $GLOBALS['_DB_DATAOBJECT']['CONNECTIONS'][$this->_database_dsn_md5];
      if ($DB->phptype == ‘mysql’) {
        $DB->query($GLOBALS['_DB_DATAOBJECT']['CONFIG']['after_connection_command']);
      }
    }

    return true;
  
  }

}

Gran noticia!!!
En mi caso, mi aplicación utilizaba el paquete DB como driver para el DB_DataObject. Como sabréis este paquete se ha abandonado en pro de MDB2 que sí que permite la definición del charset en el DSN. He cambiado el driver a MDB2, he fijado el charset a UTF8 y a funcionar. Puedo descartar el uso de la clase DB_DataObject_Custom aunque ahí dejo el ejemplo para posibles ampliaciones de los option

Pongo un ejemplo en el que se puede observar como se setea el charset a UTF8 y el driver de la base de datos a MDB2

$DB_DSN = array(
  'phptype' => 'mysql',
  'username' => 'user',
  'password' => 'pass',
  'hostspec' => 'localhost',
  'database' => 'dbname',
  'charset' => 'utf8',
);
 
// DataObject configuration
$options = &PEAR::getStaticProperty('DB_DataObject','options');
$options = array(
  'database'         => $DB_DSN,
  'schema_location'  => './classes/dal',
  'class_location'   => './classes/dal',
  'require_prefix'   => './classes/dal',
  'class_prefix'     => 'DAO_',
  'db_driver' => 'MDB2'
);