devdev / in the loop

PHP serialize e unserialize di una classe in modo semplice

Uno dei pochi modi per salvare una istanza di una Classe per utilizzarla in seguito è quello di serializzarla. Se vi state chiedendo cosa diamine significhi, potrebbero essere due i casi: uno, siete dei rookie non ancora a questo livello (e non c’è niente di male), per voi ho scritto la guida PHP startup; due, non vi trovate spesso davanti a questo tipo di operazione, anche se potrebbe essere davvero utile capire come funziona.

Per farla breve, serializzare una istanza di Classe significa mettere un’intera istanza in una stringa, per poi poterla riconvertire in istanza facendo l’unserialize.

PHP mette a disposizione i metodi serialize($istanza) e unserialize($stringa) che rispettivamente convertono in stringa una istanza qualsiasi e riportano ad istanza una stringa. Vediamo un po’ di codice:

$auto = new Auto(new OlioMotore());
file_put_contents('classe.txt', serialize($car));
$stessaAuto = unserialize(file_get_contents('classe.txt'));
if ($auto === $stessaAuto) {
    echo "Le istanze sono identiche";
}

Che abbiamo fatto? Abbiamo preso un istanza di Auto in $auto e l’abbiamo serializzata in una stringa, che abbiamo poi salvato nel file classe.txt. Al terzo rigo, abbiamo letto il contenuto del file e abbiamo passato il suo contenuto, sempre come stringa, a unserialize(), ed ecco che $stessaAuto è identica all’istanza originaria $auto.

Come vediamo, è molto utile per conservare lo stato di una data istanza in un file o in un database (o, perché no, in un database in-memory come Redis o memcached) o qualsiasi cosa accetti una stringa.

Ad esempio, salvare l’istanza dell’Utente loggato con tutte le sue proprietà in una sessione, potrebbe esserci utile per poi ripristinarla alla prossima pagina richiesta direttamente dalla sessione, senza istanziare di nuovo l’utente loggato (e tirarlo quindi nuovamente fuori dal database).

Prima di PHP 5.1

Prima di PHP 5.1, l’unico modo per gestire una serializzazione di una classe era quello di usare i metodi __sleep() e __wakeup(). Il primo doveva restituire un array di proprietà, quelle che volevamo appunto serializzare. Il secondo metodo invece doveva essere chiamato dopo che la classe fosse stata unserializzata, in modo da poter modificare l’istanza corrente. Aggiungiamo pure che la stringa generata è complicata da capire, visto che veniva fatto un encoding “strano” proprio per mantenere intatta la Classe. Facile? No.

Serializable è qui

Per fortuna, tutto questo è stato rimpiazzato da un’interfaccia chiamata, indovinate, Serializable. È provvista di due metodi, serialize() e unserialize(), che hanno sostituito la vecchia logica delle funzioni procedurali omonime, ma non hanno problemi di retrocompatibilità.

Anche se si spiegano praticamente da soli, i metodi sono:

  • serialize(), che viene chiamato quando la classe viene serializzata. Il suo scopo è restituire la rappresentazione dell’instanza corrente sotto forma di stringa e non prevede parametri;
  • unserialize(), che viene chiamato quando appunto tentiamo di fare l’unserialize di quella istanza. PHP controllerà l’integrità e la corrispondenza della stringa passatogli e creerà un’istanza vera e propria. Può sembrare complicato all’inizio, ma questo metodo funziona come costruttore, ciò significa che il metodo __construct() verrà bypassato. Se per noi fosse fondamentale la sua esecuzione, potremmo chiamarlo noi all’interno del metodo.

Capito? Vediamo un po’ di codice:

<?php

use Serializable;

class Auto implements Serializable
{
    /**
     * Motore
     *
     * @var \Motore
     */
    protected Motore $motore;
    
    /**
     * Colore dell'auto
     *
     * @var string
     */
    public string $colore = 'rosso';
    
    /**
     * Costruttore
     *
     * @return null
     */
    public function __construct(Motore $motore)
    {
        $this->motore = $motore;
    }
    
    /**
     * Il motore è acceso
     *
     * @var bool
     */
    public function motoreAcceso() : bool
    {
        return $this->motore->avviato();
    }
    
    /**
     * Settiamo il Motore
     *
     * @param \Motore $motore
     * @return void
     */
    public function setMotore(Motore $motore)
    {
        $this->motore = $motore;
    }
    
    /**
     * Resituiamo una stringa JSON encoded
     *
     * @return string
     */
    public function serialize()
    {
        return json_encode([
            'motore' => get_class($this->motore),
            'motore_on' => $this->motoreAcceso(),
            'colore' => $this->colore
        ]);
    }

    /**
     * Ripristiniamo questa classe a partire da una stringa JSON
     *
     * @param string $serialized
     * @return void
     */
    public function unserialize($serialized)
    {
        //La riportiamo ad array
        $json = json_decode($serialized);

        //Popoliamo gli attributi
        $this->setMotore($json->motore);
        $this->colore = $json->colore;
        
        // ...
    }
}

Nel nostro esempio, utilizziamo JSON come formato restituito da serialize(). Abbiamo scelto di inflilarci lo stato del motore e il colore dell’auto.

Quando eseguiamo l’unserialize(), invece, prendiamo la stringa  JSON in input, la convertiamo in array, e settiamo le proprietà della classe con i valori ottenuti. In due parole, decodifichiamo la stringa JSON e impostiamo la proprietà $colore con il valore trovato nella stringa e continuiamo con gli altri dati.

Vediamola in azione:

<?php

$auto = new Auto(new OlioMotore());
$auto->colore = 'rosso';

// 1
// Serializziamo la classe Auto e la salviamo nel file auto.txt
file_put_contents('auto.txt', serialize($auto));

// 2
// Adesso che è stata salvata, prendiamo il contenuto del file, in qualsiasi momento.
// Ricordiamoci che la stringa sarà in JSON
$contents = file_get_contents('auto.txt');

echo $contents;
// Otterremo C:3:"Auto":63:{{"motore":"Motore\\OlioMotore","motore_on":false,"colore":"rosso"}}

// 3
// Creiamo una nuova istanza basata sui dati appena presi
$stessaAuto = unserialize($contents);

// 4
// A questo punto controlliamo, ad esempio che il colore sia lo stesso che gli abbiamo passato in JSON
echo $stessaAuto->colore;
// "rosso"
Questo articolo ti è stato utile?
PHP – LETTURA 6 MINUTI PHP 8: l’operatore null-safe
L’introduzione dell’operatore null coalescing con PHP 7 è stato un buon passo avanti per la pulizia del codice, ma non…
Laravel – LETTURA 4 MINUTI Aggiungere phpMyAdmin a Laravel Sail
Anche se a questo punto lo sapete già, è giusto dire che phpMyAdmin è sicuramente il tool dbms open  source…
PHP – LETTURA 4 MINUTI Usare glob() in PHP per elencare i file di una directory
Spesso abbiamo utilizzato (lo so, l’avete fatto anche voi) la combinazione di opendir(), readdir() e closedir() per elencare i file…
PHP – LETTURA 5 MINUTI Installare PHP su macOS Monterey con homebrew (anche su M1)
L’ultima versione di macOS 12 Monterey non porta più con sé preinstallato PHP. Difatti, una nota nel conf http.conf di…
Roba figa da
if (weekend) {
    relax();
}
la nostra newsletter, ogni tanto.