serialize() convierte cualquier valor PHP (objeto, array, escalar) en una cadena de texto que puede almacenarse en un fichero, base de datos o caché y recuperarse más tarde con unserialize(). Es el mecanismo que PHP usa internamente para las sesiones y que muchos frameworks emplean para caché de objetos complejos.
serialize() y unserialize() básicos
<?php
class Producto {
public function __construct(
public string $nombre,
public float $precio,
public int $stock
) {}
}
$p = new Producto('Teclado', 49.99, 12);
$serializado = serialize($p);
// Resultado: O:8:"Producto":3:{s:6:"nombre";s:7:"Teclado";s:6:"precio";d:49.99;s:5:"stock";i:12;}
file_put_contents('/tmp/producto.ser', $serializado);
// Recuperar
$datos = file_get_contents('/tmp/producto.ser');
$p2 = unserialize($datos);
echo $p2->nombre; // Teclado
echo $p2->precio; // 49.99
__sleep() y __wakeup(): controlar la serialización
__sleep() se ejecuta justo antes de serializar. Debe devolver un array con los nombres de las propiedades que quieres incluir en la cadena serializada. Ideal para excluir conexiones a BD, recursos o datos sensibles.
__wakeup() se ejecuta al deserializar. Permite restaurar lo que __sleep() excluyó (por ejemplo, reconectar a la BD).
<?php
class Repositorio {
private PDO $pdo;
private string $dsn;
public function __construct(string $dsn) {
$this->dsn = $dsn;
$this->pdo = new PDO($dsn);
}
public function __sleep(): array {
// No serializamos la conexión PDO; solo el DSN
return ['dsn'];
}
public function __wakeup(): void {
// Reconectamos al deserializar
$this->pdo = new PDO($this->dsn);
}
public function contar(string $tabla): int {
return (int) $this->pdo->query("SELECT COUNT(*) FROM $tabla")->fetchColumn();
}
}
__serialize() y __unserialize() (PHP 7.4+)
PHP 7.4 introduce una API más limpia que tiene prioridad sobre __sleep/__wakeup. __serialize() devuelve un array asociativo con los datos a guardar; __unserialize() recibe ese mismo array para restaurar el estado.
<?php
class Token {
private string $valor;
private DateTimeImmutable $expira;
public function __construct(string $valor, DateTimeImmutable $expira) {
$this->valor = $valor;
$this->expira = $expira;
}
public function __serialize(): array {
return [
'valor' => $this->valor,
'expira' => $this->expira->format(DateTimeInterface::ATOM),
];
}
public function __unserialize(array $data): void {
$this->valor = $data['valor'];
$this->expira = new DateTimeImmutable($data['expira']);
}
public function estaVigente(): bool {
return $this->expira > new DateTimeImmutable();
}
}
$t = new Token('abc123', new DateTimeImmutable('+1 hour'));
$s = serialize($t);
$t2 = unserialize($s);
var_dump($t2->estaVigente()); // bool(true)
PHP Object Injection: la vulnerabilidad más conocida
Nunca deserialices datos que vengan de una fuente no confiable (parámetros GET/POST, cookies del usuario, ficheros subidos). Un atacante puede construir una cadena serializada maliciosa que, al deserializarse, ejecute código arbitrario gracias a los métodos mágicos de tus propias clases.
<?php
// VULNERABLE: nunca hagas esto
$obj = unserialize($_COOKIE['carrito']);
// SEGURO: usa el parámetro $allowed_classes para limitar las clases permitidas
$obj = unserialize($datos, ['allowed_classes' => ['Producto', 'Carrito']]);
// SEGURO: si no necesitas objetos, desactívalos por completo
$arr = unserialize($datos, ['allowed_classes' => false]);
Alternativas a serialize()
Para almacenar datos en formatos interoperables (compartidos entre lenguajes o sistemas), prefiere JSON con json_encode() / json_decode(). Para objetos complejos con relaciones circulares o para caché de alta frecuencia, considera igbinary (más compacto y rápido) o MessagePack.
<?php
// JSON: más seguro para datos externos, pero no preserva el tipo de clase
$json = json_encode(['nombre' => 'Teclado', 'precio' => 49.99]);
$arr = json_decode($json, true); // array, no Producto
// igbinary: instalado como extensión, drop-in replacement
$s = igbinary_serialize($p);
$p2 = igbinary_unserialize($s);
