Introduzione
Serializzare una variabile significa convertirla in una sequenza lineare di byte, ovvero una stringa di caratteri ASCII, che è in grado di rappresentarne la struttura e le proprietà.
Tale rappresentazione potrà essere memorizzata su un file di testo (o altro supporto) quindi recuperata e convertita nuovamente nei valori originari, che diverranno immediatamente disponibili.
Qualsiasi variabile, per quanto complessa, è serializzabile e de-serializzabile con la sola esclusione delle resource cioè delle variabili che rappresentano un riferimento a risorse esterne, come ad esempio il valore restituito da mysql_connect() (nel manuale è reperibile una lista delle funzioni di questo tipo).
Quando è utile serializzare?
Poichè il protocollo HTTP è privo di stato (vedi anche Tutto sui cookie), ricorrere ad una qualche forma di serializzazione è pressochè indispensabile quando abbiamo a che fare con dati che devono rimanere persistenti durante la navigazione: non a caso "impacchettare" le variabili, salvandone la rappresentazione su un file o database, è proprio il metodo utilizzato dal sistema delle sessioni per tenere traccia dell'utente e di quanto avvenuto durante le richieste precedenti.
Anche nelle applicazioni che fanno parte del variegato mondo dei "Web Services", client e server comunicano tra loro attraverso dati serializzati (in uno dei dialetti XML).
In generale è un sistema efficace per memorizzare dati complessi poichè consente il loro recupero con estrema facilità.
serialize() e unserialize()
Utilizzare le funzione predefinita serialize() è certamente il metodo più semplice per ottenere una rappresentazione in formato stringa della variabile da immagazzinare.
Ecco alcuni esempi:
Variabili molto semplici
<?php
$varStr="Visita freephp.html.it" ;
$varInt=5 ;
$arr=array("Hola!","Hello!","Ciao!") ;
echo(
serialize($varStr).'
'.
serialize($varInt).'
'.
serialize($arr).'
'
) ;
/*
output:
s:22:"Visita freephp.html.it";
i:5;
a:3:{i:0;s:5:"Hola!";i:1;s:6:"Hello!";i:2;s:5:"Ciao!";}
*/
?>
Una struttura complessa
<?php
$arrayClienti=array() ;
$arrayClienti[0]['nome']="Mario" ;
$arrayClienti[0]['cognome']="Grendene" ;
$arrayClienti[0]['recapito']=array('city'=>array('name'=>'Trento', 'via'=>'Via Ungaretti', 'civico'=>49), 'telefono'=>0422712305,'e-mail'=>'gre@yabba.com') ;
$daSalvare=serialize($arrayClienti) ;
echo($daSalvare) ;
/*
Come output la stringa (qui spezzata per motivi di spazio)
a:3:{i:0;a:1:{s:4:"nome";s:5:"Mario";}i:1;
a:1:{s:7:"cognome";s:8:"Grendene";}i:11;a:1:{s:8:"recapito";
a:3:{s:4:"city";a:3:{s:4:"name";s:6:"Trento";s:3:"via";
s:13:"Via Ungaretti";s:6:"civico";i:49;}
s:8:"telefono";i:72062149;s:6:"e-mail";s:13:"gre@yabba.com";}}}
che avrei potuto salvare su file o database
*/
$arrayRecuperato=unserialize($daSalvare) ;
echo("Cliente: ".$arrayRecuperato[0]['nome']." ".
$arrayRecuperato[0]['cognome']."
") ;
echo("e-mail: ".$arrayRecuperato[0]['recapito']['e-mail']) ;
?>
Lo script precedente dimostra come sia possibile "congelare" struttura e valori, anche di un array abbastanza complesso, in una stringa e successivamente ricavare da essa attraverso unserialize() un nuovo array identico al precedente.
Tutto ciò senza che si verifichi alcuna perdita di informazioni.
Il formato appena visto è tipico di php ed è lo stesso con cui, in base alle impostazioni di default, il sistema delle sessioni memorizza i dati su file o database.
Anche gli oggetti sono serializzabili tuttavia vi sono alcune particolarità, la questione richiederebbe l'introduzione di alcuni concetti specifici della programmazione OOP (object oriented programmation), e quindi ce ne occuperemo in un articolo apposito.
Qui mi limito a segnalare che affinchè la de-serializzazione avvenga correttamente è necessario che la definizione della classe da cui l'oggetto deriva sia presente nello script (magari attraverso un inclusione).
Il manuale è piuttosto chiaro sull'argomento.
var_export(), l'alternativa a serialize()
Nelle versioni più recenti di Php (sicuramente a partire dalla 4.2), grazie alla funzione var_export(), ci viene fornita un'ulteriore possibilità che si dimostra particolarmente efficace nella memorizzazione e recupero degli array.
Se forniremo come argomento di var_export() un array, otterremo di vederne stampata la struttura sotto forma di codice php valido: quest'ultima caratteristica è un'importante differenza rispetto a fuzioni analoghe, già presenti da tempo, come var_dump() e print_r.
Se forniremo come argomento opzionale anche il valore boleano "TRUE" il codice non verrà stampato ma restituito ad una variabile.
Poichè si tratta di codice valido, il dato restituito potrà anche essere reinterpretato grazie ad eval() operando di fatto una de-serializzazione.
Un esempio completo
<php
/***
Funzione che de-serializza le stringhe create da var_export()
***/
function varImport($str){
/***
Facciamo restituire la variabile ad eval inserendo nella stringa da interpretare return()
***/
return(eval("return($str);")) ;
}
$arrayClienti=array() ;
$arrayClienti[0]['nome']="Mario" ;
$arrayClienti[0]['cognome']="Grendene" ;
$arrayClienti[0]['recapito']=array('city'=>array('name'=>'Trento', 'via'=>'Via Ungaretti', 'civico'=>49), 'telefono'=>0422712305,'e-mail'=>'gre@yabba.com') ;
$daSalvare=var_export($arrayClienti,TRUE) ;
echo($daSalvare) ;
/***
Output restituito in codice php valido
array ( 0 => array ( 'nome' => 'Mario', 'cognome' => 'Grendene', 'recapito' => array ( 'city' => array ( 'name' => 'Trento', 'via' => 'Via Ungaretti', 'civico' => 49, ), 'telefono' => 72062149, 'e-mail' => 'gre@yabba.com', ), ), )
***/
/***
Deseralizzazione attraverso eval()
***/
$arrayRecuperato=varImport($daSalvare) ;
/***
Aggiorno l'array
$arrayRecuperato
***/
echo("Cliente: ".$arrayRecuperato[0]['nome']." ".
$arrayRecuperato[0]['cognome']."<br>") ;
echo("e-mail: ".$arrayRecuperato[0]['recapito']['e-mail']) ;
/***
Aggiorno i dati relativi all'indrizzo e-mail
***/
$arrayRecuperato[0]['recapito']['e-mail']="grendene@yahoo.uk" ;
echo("e-mail: ".$arrayRecuperato[0]['recapito']['e-mail']) ;
$daSalvare=var_export($arrayRecuperato) ;
/***
Ora posso salvare i dati aggiornati su file
***/
//...
?>
Questo metodo di conversione e riconversione risulta fino a 10 volte più rapido rispetto all'utilizzo di serialize()/unserialize(), pur essendo altrettanto semplice recuperare ed aggiornare i dati: per queste ragioni è quello che prediligo nel 90% dei casi.
Purtroppo non è adatto alla serializzazione degli oggetti, in quanto var_export() trasforma qualsiasi oggetto in un array associativo: tradurre completamente la stringa che rappresenta l'oggetto serializzato e ricreare l'oggetto originario non è semplicissimo, pertanto le operazioni necessarie ad ottenere questo risultato farebbero perdere tutti i vantaggi di eval() in termini di prestazioni.
Avvertenze sulla sicurezza
Mai applicare eval() su stringhe di codice proveniente da variabili GET o POST, e in qualsiasi altro caso è bene essere certi che non si tratti di istruzioni potenzialmente pericolose.
Serializzazione semplice
Cos'è il CSV (comma separated values) se non una forma semplice di serializzazione?
Eppure si tratta di un metodo universalmente riconosciuto dai database per il "dump" dei dati: ad esempio possiamo esportare in formato CSV un database nato in Access e, dopo qualche aggiustamento, importarlo in Mysql con tutta tranquillità.
La caratteristica del CSV è quella di memorizzare i valori delle variabili in una struttura descritta da delimitatori in cui i diversi record sono segnalati dal carattere invisibile "n" (a capo), mentre i campi all'interno dei record sono tradizionalmente racchiusi da virgole, tuttavia qualsiasi delimitatore che non sia un carattere presente nel contenuto delle variabili è adatto allo scopo.
Esempio di record CSV:
dato1,dato2,dato3,dato4
altrodato1,altrodato2,altrodato3,altrodato4
CSV è utile quando si debbono descrivere valori con una struttura predefinita e costante, ha anche il pregio di fornire come risultato stringhe più concise rispetto ai formati visti nelle pagine precedenti (e perciò di consentire un risparmio di spazio e memoria).
Serializziamo e deserializziamo un array
<php
$arrayDisney=array("pluto"=>"cane", "gambadilegno" =>"gatto", "cip e ciop"=>"scoiattoli") ;
$daSalvare=join(",",$arrayDisney)."n" ;
echo($daSalvare) ;
/***
L'output sarà
cane,gatto,scoiattoli
***/
/***
Recupero le variabili
***/
$arrayRecuperato=explode(",",$daSalvare) ;
?>
Avete notato che abbiamo perso le chiavi dell'array? È possibile conservarle inserendole nella stringa da salvare utilizzando un terzo separatore (e rinunciando quindi al formato CSV), ma al massimo sarà possibile ottenere agevolmente soltanto l'interpreatazione di un array associativo unidimensionale.
In ogni caso ricorrere a questa tipo di serializzazione per strutture complesse come quelle presentate nelle pagine precedenti significherebbe soltanto complicarsi la vita senza motivo, abbiamo già visto che le alternative non mancano.
Serializzare con XML e WDDX
XML è un meta-linguaggio in grado di descrivere praticamente qualsiasi tipo di dato, e ha la prerogativa di consentire la condivisione delle informazioni tra varie applicazioni. Ad esempio un'applicazione scritta in Java o Perl sarà in grado di ricevere via HTTP dati serializzati con Php e interepretarli correttamente...e viceversa nella direzione opposta.
Quello appena descritto è in due parole il sistema dei Web Services, ed è chiaro il motivo per cui XML ne rappresenta il cuore.
WDDX è un dialetto XML di cui ci occuperemo più dettagliatamente in futuro, per ora tra gli standard specifici per i Web Services è quello meglio supportato da Php, tanto che persino nel sistema delle sessioni è possibile sostituire il formato php di serializzazzione (quello restituito da serialize()) con wddx: sarà sufficiente sostituire "wddx" a "php" nella seguente direttiva del php.ini:
session.serialize_handler = php
In mezzo a tanti "pro" XML presenta un unico inconveniente rispetto agli altri metodi: rappresenta i dati in stringhe piuttosto lunghe, con un conseguente spreco di spazio e memoria.
In un certo senso si tratta di un CSV molto evoluto e, in quanto tale, paga la sua straordinaria capacità descrittiva con la prolissità.
Spedire dati serializzati attraverso HTTP
Tratteremo anche questo argomento più a fondo quando parleremo di Web Services, per ora diciamo soltanto che qualora si invii la stringa serializzata attraverso la query string o tramite POST (nel caso di POST soltanto se si tratta della simulazione di un invio da form tramite Php) sarà necessario proteggerla, affinchè il contenuto non si "corrompa" e rischi di essere male interpretato in fase di de-serializzazione.
Dalle prove fatte urlencode() e urldecode() non sempre preservano le stringhe passate attraverso l'URL, invece la codifica in base 64 si è dimostrata affidabile.
Esempio di invio attraverso la query string
<php
//pag. "serialize.php"
$myarray("valore1","valore2","valore3","valore4") ;
$arrayDaInviare=base64_encode(serialize($myarray)) ;
$url="http://www.mydomain.com/unserialize.php?myarray=$arrayDaInviare" ;
echo("<a href=$url>Trasmetti</a>") ;
?>
Pagina che riceve e decodifica i dati:
<php
//pag. "unserialize.php"
$arrayDecodificato=unserialize(base64_decode($_GET["myarray"])) ;
?>
Ricordo soltanto per completezza che la query string non può eccedere una certa lunghezza, se supera i 255 caratteri alcuni browser potrebbero troncarla.
Conclusioni
Spero di aver dimostrato che la serializzazione è un metodo pratico ed efficace per immagazzinare strutture complesse di dati con l'obiettivo di recuperarli intatti in modo rapido. Quando affronteremo il nuovo mondo dei Web Services vedremo come trasmettere informazioni serializzate in XML consente la comunicazione e l'interoperabilità tra le applicazioni più disparate.