¿Por qué no se debe usar la API mysql_* en PHP/MySQL?

Como complemento a la respuesta de @AlvaroMontoro, quisiera mostrar las dos posibilidades de conexión que ofrece PHP basada en la API mysqli y en la clase PDO.

Cualquiera de ellas es válida al momento de tener que migrar de mysql_ (obligatorio a partir de PHP 7).

Tiene dos estilos de código. El estilo orientado a objetos, que es más claro y más moderno y el estilo procedural, que es más largo, más verboso, produciendo código más difícil de leer en consecuencia1.

Cualquiera de los dos estilos es válido, pero no se recomienda mezclar estilos.

Veamos ahora la forma de conectar usando los dos estilos.

a. Conexión a mysqli con estilo orientado a objetos

mysqli es una clase, por eso se puede usar con el estilo orientado a objetos. En ese caso se usa new, como en todas las clases. El constructor admite cuatro parámetros:

  1. El host
  2. El nombre de usuario
  3. La constraseña
  4. El nombre de la base de datos 2

Haciendo simplemente esto, ya tendremos un objeto de conexión disponible para interactuar con la base de datos:

$mysqli = new mysqli("localhost", "usuario", "password", "basedatos");

Recomendaciones:

  1. Muchas veces, cuando nuestros datos tienen acentos, ñ o caracteres especiales, los datos vienen deformados, con
    codificación extraña o con el caracter inspector. Para evitar esto a
    nivel de la conexión conviene, una vez creado el objeto, establecer un
    charset adecuado, esto se hace mediante el uso de set_charset(), una
    vez creada la conexión:

     $mysqli->set_charset("utf8");
    
  2. Podría ser interesante, para evitar conectar donde quiera que necesitemos acceder a la base de datos, crear una clase que gestione nuestra conexión.

Ya sólo nos queda usar nuestro objeto $mysqli donde lo necesitemos.


b. Conexión a mysqli con estilo procedural


IMPORTANTE: Aunque el estilo procedural está muy extendido, y el Manual de PHP muestra siempre ejemplos también en este estilo (aunque en último término), debería evitarse programar en este estilo. El mismo es más verboso y menos claro que el estilo orientado a objetos y aparte de eso, algunas de sus funciones, incluyendo a mysqli_connect, están en una lista de funciones obsoletas, desde PHP 5.

No obstante, muestro la forma de conectar.


La conexión es casi idéntica al estilo orientado a objetos, con los mismos cuatro parámetros:

$mysqli = mysqli_connect('localhost', 'usuario', 'password', 'basedatos');

En la conexión se nota poco la verbosidad referida anteriormente. El asunto se nota cuando empezamos a usar las funciones, sobre todo cuando hay que trabajar con consultas preparadas y usar los métodos de binding…, etc.

Veamos ahora el asunto del charset:

mysqli_set_charset($mysqli, "utf8");

Listo, ahora sólo nos queda usar $mysqli.


PDO sólo tiene estilo orientado a objetos. Una de las grandes ventajas de PDO es que casi con el mismo código, cambiando solamente el DSN y las credenciales de conexión, podemos pasar de un manejador de base de datos (Oracle, SQL Server, MySQL, Postgresql…) a otro sin cambiar todo el código interno que consulta los datos en los diferentes puntos de una aplicación.

Igual que lo indicado en (A), la conexión a PDO se crea con new. Pero el constructor de PDO es más completo y admite varios parámetros interesantes:

  1. El DSN. Son las iniciales de Data Source Name (Nombre de Origen de Datos). En general, el DSN consiste en el nombre del controlador de PDO, seguido por dos puntos, seguido por la sintaxis específica del controlador de PDO para la conexión. En esa información suele ir el nombre de la base de datos, el host y también podemos poner el charset. Eso lo veremos más adelante.
  2. El nombre e usuario.
  3. La contraseña
  4. Un array de opciones. Esto es interesante y novedoso. La configuración de PDO es algo delicada en algunos aspectos por lo que conviene saber usar este cuarto parámetro.

Veamos entonces cómo se crea la conexión. Usaremos variables para mayor claridad.

La conexión más simple que existe sería esta, y es la que muestra el Manual de PHP:

$dsn = 'mysql:dbname=testdb;host=127.0.0.1';
$usr = 'usuario_bd';
$pwd = 'password_bd';

try {
    $pdo = new PDO($dsn, $usr, $pwd);
} catch (PDOException $e) {
    echo 'Falló la conexión: ' . $e->getMessage();
}

Como se puede ver, la conexión se maneja dentro de un bloque try ... catch, porque cualquier fallo al invocar new PDO() levantaría una excepción que rompería el código. Por eso hay que capturar las excepciones.

PERO este código básico tiene varios problemas, así que conviene mejorarlo. PDO es genial, pero tiene algunos inconvenientes que hay que resolver en la configuración:

  1. Si dejamos el código como está más arriba. Esta línea es problemática: echo 'Falló la conexión: ' . $e->getMessage(); Pues al invocar a $e->getMessage(), ante una excepción PDO estaría revelando las credenciales de conexión, contraseña incluida. Y eso podría quedar escrito en el log de errores, que es uno de los archivos favoritos de los hackers… No hay que alarmarse, vamos a solventar eso más adelante.
  2. PDO trae por defecto las preparaciones emuladas encendidas. En algunos escenarios un hacker podría recurrir a ellas para colarnos una Inyección SQL, por lo que conviene apagar dicha opción.

Visto eso, podemos pasar a mejorar nuestra conexión. Los cambios son los siguientes:

  1. En $dsn vamos a indicar el charset. A diferencia de mysqli, no tenemos que usar una función aparte para esto. Lo podemos pasar como un dato más del DSN.
  2. Vamos a crear un array $options con todas las configuraciones que querramos para la conexión. Dado que PDO tiene métodos setAttribute para modificar su configuración, muchos usuarios crean primero la conexión y luego la modifican con esos métodos. Desde el punto de vista del rendimiento es mucho mejor crear la conexión ya configurada con todo lo que querramos en vez de modificarla una vez creada con setAttributes. Precisamente para eso PDO admite ese cuarto parámetro de opciones.
  3. El en catch evitaremos hacer público mensajes de error internos. De todos modos, con respecto a la contraseña, conviene usar siempre una contraseña larga de entre 30 y 50 caracteres. De modo que si ésta se revela por descuido en algún log de errores o en la pantalla, al ser larga, ésta quedaría truncada.

Conexión mejorada:

$dsn = 'mysql:dbname=testdb;host=127.0.0.1;charset=utf8';
$usr = 'usuario_bd';
$pwd = 'password_bd';

$options = array(
        PDO::ATTR_EMULATE_PREPARES => FALSE, 
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
);

try {
    $pdo = new PDO($dsn, $usr, $pwd, $options);
} catch (PDOException $e) {
    $errMsg='Falló la conexión. Revise las credenciales (u otro mensaje)';
    #Puedes hacer echo de un error personalizado
    echo $errMsg;
    #y/o escribirlo en el log de errores
    error_log($errMsg);
    #Aquí puedes optar también por escribir $e->getMessage() en un archivo protegido        
}

Ya sólo nos queda usar nuestro objeto $pdo.

Como recomendamos en (1). Conviene gestionar la conexión a PDO en una clase, la cual invocaremos cada vez que necesitemos conectar.


Enlaces


Notas

  1. A título personal, recomiendo usar el estilo orientado a objetos
  2. Esta es una novedad importante con respecto a la vieja mysql_ en la cual este parámetro no existe. En los viejos manuales se repandió el uso de mysql_select_db() después de conectar y muchos usuarios continúan esta práctica innecesaria al migrar a la nueva mysqli