PDO Vs MySQLi

Al momento de querer conctarte a una base de datos, tenemos dos opciones: MySQLi y PDO. Entonces, ¿que deberías tomar en cuenta antes de utilizar uno del otro? En este artículo veremos las principales diferencias, el soporte de base de datos que brinda cada uno, su estabilidad, y rendimiento de ambos controladores de conexión.

Conexión

En ambos casos, la conexión a una base de datos es algo muy sencillo y fácil de hacer.

// PDO
$pdo
= new PDO("mysql:host=localhost;dbname=database", 'usuario', 'contraseña');
// mysqli, de manera procesal
$mysqli
= mysqli_connect('localhost','usuario','contraseña','base_de_datos');
// mysqli, de manera orientado a objetos
$mysqli
= new mysqli('localhost','usuario','contraseña','base_de_datos');

Soporte De API's

Ambos, PDO y MySQLi, ofrecen una API orientado a objetos, pero MySQLi también ofrece una API de forma procesal — lo que lo hace fácil para personas que recien empiezan con este lenguaje. Si estas familiarizado con mysql_*, el API nativo de PHP, encontrarás que la migración al API procesal es mucho más fácil. Por otro lado, si entiendes PDO podrás conectarte a una mayor cantidad de bases de datos.

*Nota: mysql_* es oficialmente obsoleta a partir de la versión 5.5 de PHP (Junio 2013).  

Soporte de Bases de Datos

PDO vs MySQLi

La principal ventaja que tiene PDO, sobre MySQLi, es la habilidad de poderte conectar a 12 diferentes bases de datos, a diferencia de MySQLi que sólo tiene soporte para una, MySQL. 

Para imprimir una lista de todos las bases de datos soportadas en PDO, utiliza el siguiente snippet de código:

var_dump(PDO::getAvailableDrivers());

¿Qué significa todo esto? Imaginemos que trabajas en un proyecto utilizando MySQL como tu base de datos. Ahora imagina que de un momento a otro necesitas cambiar a otro tipo de base de datos, Oracle por ejemplo. Si utilizas MySQLi, esto será imposible ya que sólo tiene soporte para MySQL. Por otro lado, si utilizas PDO, puedes simplemente cambiar el tipo de la base de datos en la conexión, el formato de unas consultas y listo.

Bases de Datos Soportadas por PDO

PDO_CUBRID Cubrid
PDO_DBLIB FreeTDS / Microsoft SQL Server / Sybase
PDO_FIREBIRD Firebird
PDO_IBM IBM DB2
PDO_INFORMIX IBM Informix Dynamic Server
PDO_MYSQL MySQL 3.x/4.x/5.x
PDO_OCI Oracle Call Interface
PDO_ODBC ODBC v3 (IBM DB2, unixODBC and win32 ODBC)
PDO_PGSQL PostgreSQL
PDO_SQLITE SQLite 3 and SQLite 2
PDO_SQLSRV Microsoft SQL Server / SQL Azure
PDO_4D 4D

Mapeo de Objetos

Ambos, PDO y MySQLi, pueden mapear resultados a objetos. Esto es muy útil cuando no deseas utilizar una capa de abstracción en una base de datos personalizada, pero quires seguir teniendo un comportamiento de ORM. 

Imaginemos que tenemos una clase llamada Usuario con algunas propiedades, estas propiedades tienen el mismo nombre que las columnas en la base de datos.

class Usuario {
  public
$id;
  
public $nombre;
  public
$apellido;

  public
function info(){
    return
'#' $this->id . ': ' $this->nombre . ' ' $this->apellido;
  }
}

Sin el mapeo, necesitariamos rellenar el valor de cada propiedad (ya sea a tráves de un constructor ó manualmente) antes de poder usar el método info().

El mapeo de resultados a objetos nos permite predefinir estas propedades antes de crear el constructor. Por ejemplo:

$query = "SELECT id, nombre, apellido FROM usuario";

// PDO
$result
= $pdo->query($query);
$result->setFetchMode(PDO::FETCH_CLASS,
'Usuario');

while ($user = $result->fetch()) {
  echo
$user->info() . "\n";
}

// MySQLI, de forma procesal
if
($result = mysqli_query($mysqli, $query)) {
  while
($user = mysqli_fetch_object($result, 'Usuario')) {
    echo
$user->info() . "\n";
  }
}

// MySQLi, de forma orientado a objetos
if
($result = $mysqli->query($query)) {
  while
($user = $result->fetch_object('Usuario')) {
    echo
$user->info() . "\n";
  }
}

Seguridad

Seguridad PDO vs MySQLi

Digamos que una persona quiere inyectar un poco de código SQL malicioso en el parámetro de la consulta llamada "username" a tráves de la variable GET.

$_GET['username'] = "'; DELETE FROM users; /*";

Si no escapamos los carácteres especiales de esta consulta, terminará eliminando todas las filas de nuestra tabla llamada users.

Para estos casos podemos utilizar consultas preparadas, las cuales escaparan estos carácteres especiales.

// PDO, consultas preparadas
$pdo->prepare(
'SELECT * FROM users WHERE username = :username');
$pdo->execute(array
(':username' => $_GET['username']));

// mysqli, consultas preparadas
$query
= $mysqli->prepare('SELECT * FROM users WHERE username = ?');
$query->bind_param('s'
, $_GET['username']);
$query->execute();

Rendimiento

Pero si PDO es tan potente y versátil… ¿no hay lugar para MySQLi en la actualidad?

La verdad es que sí lo hay (a diferencia de lo que muchos creen).

La versatilidad de PDO es un arma de doble filo.

Como sucede con cualquier solución genérica, quien mucho abarca poco aprieta 🙂

En términos técnicos, lo que ocurre es que PDO no puede aprovechar las pequeñas diferencias existentes entre los diferentes motores, lo cual lo hace menos eficiente que MySQLi en ciertas situaciones.

Por otro lado, una capa más de abstracción agrega más overhead.

Ambos drivers, PDO y MySQLi, son bastante rápidos. Sin embargo, MySQLi es un poco más rápido, aproximadamente 2.5% en consultas no preparadas y 6.5% en consultas preparadas. 

En Conclusión

  PDO MySQLi
Bases de Datos Soportadas 12 diferentes MySQL
Tipo de API OOP OOP + procesal
Conexión Fácil Fácil
Parámetros Nombrados Si No
Mapeo de Objetos Si Si
Consultas preparadas
(lado del cliente)
Si No
Rendimiento Rápido Rápido
Procedimientos Almacenados
(Stored procedures)
Si No