serialize() y unserialize() en PHP: persistir objetos y los métodos mágicos __sleep y __wakeup

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);

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP