back to top

Guida allโ€™utilizzo di PDO

PDO (PHP Data Objects) รจ una delle estensioni piรน interessanti introdotte nellโ€™implementazione della versione 5 di PHP; essa nasce infatti dallโ€™esigenza di recuperare una grave mancanza che per lungo tempo aveva caratterizzato questo linguaggio di sviluppo, cioรจ lโ€™assenza di unโ€™unica interfaccia per lโ€™accesso alle basi di dati.

Cosโ€™รจ PDO e a cosa serve?

Si immagini di lavorare su un progetto che prevede la creazione di un sito Web basato sullโ€™interazione con un DBMS per lโ€™accesso ai dati, il Database manager utilizzato potrebbe essere per esempio MySQL; a questo scopo lo sviluppatore potrร  sfruttare le potenzialitร  messe a disposizione dalle funzioni native mysql_ based o mysqli_ based, ma cosa succederebbe se, per una qualsiasi esigenza, ci si ritrovasse di fronte alla necessitร  di trasferire il sito Web su un altro server che ha come DBMS di riferimento PostgreSQL?

Pubblicitร 

Semplicemente si presenterebbe la necessitร  di dover riscrivere la propria applicazione da zero, o quasi, almeno per quanto riguarda la parte relativa allโ€™accesso ai dati; PDO รจ stata realizzata proprio per evitare il verificarsi di problemi simili a quello appena descritto, grazie ad essa lo sviluppatore non avrร  piรน delle semplici funzioni dedicate allโ€™interazione con i database ma una vera e propria classe in grado di fornire metodi utilizzabili indipendentemente dal DBMS di riferiemento.

Questa estensione รจ stata introdotta nella versione 5.1 di PHP ma era giร  disponibile come componente PECL (PHP Extension Community Library) nella distribuzione 5.0; grazie ad essa รจ possibile accedere a dati messi a disposizione da differenti tipologie di Database manager tra cui anche MySQL, SQLite, PostgreSQL, SQLserver e Oracle.

Essenzialmente รจ possibile dire che PDO fornisce un data-access abstraction layer, cioรจ un livello di astrazione per lโ€™accesso ai dati; si tratta infatti di una classe, definita forse impropriamente anche come "libreria", che mette a disposizione un insieme di sotto-classi derivate che agiscono in modo trasparente rispetto allโ€™utente; il suo utilizzo permetterร  di creare applicazioni dotate della massima portabilitร  possibile, lโ€™utilizzatore potrร  scegliere il DBMS di riferimento sulla base delle proprie esigenze senza particolari constrizioni relative alla tipologia di accesso ai dati dellโ€™applicazione.

Naturalmente anche PDO presenta qualche limite seppur di importanza marginale, non รจ per esempio retrocompatibile con versioni di PHP precedenti alla 5, si tratta infatti di unโ€™estensione che necessita della presenza nel core del linguaggio delle funzionalitร  relative alla programmazione per oggetti, indisponibili per i rilasci piรน datati che comunque, ormai, non godono di alcun supporto. Inoltre, รจ bene tenere conto del fatto che PDO non mette a disposizione una database abstraction, quindi non รจ in grado di riscrivere istruzione in linguaggio SQL o di emulare funzioni che non vengano fornite dal DBMS stesso.

Per coloro che sviluppano abitualmente applicazioni utilizzando il paradigma OOP, lโ€™utilizzo di PDO non propone particolari novitร  a livello sintattico, diverso รจ il discorso per quanto riguarda coloro che sfruttano ancora lโ€™approccio procedurale, a questi ultimi si consiglia in via propedeutica la lettura dellโ€™apposita guida su "PHP e la Programmazione Orientata agli Oggetti" pubblicata in questo sito.

Installazione e connessione al database

Uno dei vantaggi derivanti dallโ€™utilizzo di PDO sta nel fatto che grazie ad esso non sarร  necessario installare set di librerie dedicate a DBMS specifici; PDO รจ disponibile di default per le piรน recenti distribuzioni di PHP nellโ€™apposita directory dedicata alle estensioni; per abiltarla, se questo non รจ stato giร  fatto, รจ possibile aprire il file di configurazione del linguaggio, Php.ini, ed editare la seguente riga decommentandola:

extension=php_pdo.dll

Il passo successivo sarร  quello di decommentare le righe relative alle DLL di supporto per i DBMS che si desidera utilizzare, nellโ€™esempio che segue รจ stata decommentata soltanto la riga per il caricamento della libreria specifica per MySQL, la libreria per SQLite รจ invece abilitata di default a partire dalla versione 5.1 di PHP:

extension=php_pdo_sqlite.dll
;extension=php_pdo_firebird.dll
;extension=php_pdo_mssql.dll
extension=php_pdo_mysql.dll
;extension=php_pdo_oci.dll
;extension=php_pdo_ibm.dll
:extension=php_pdo_informix.dll
;extension=php_pdo_oci8.dll
;extension=php_pdo_odbc.dll
;extension=php_pdo_pgsql.dll

Fatto questo basterร  salvare le modifiche effettuate e riavviare il Web server, ad esempio Apache, per usufruire delle funzionalitร  di PDO; a questo punto si potrร  passare alle fasi di collegamento e di connessione al database, la prima richiede la definizione di una stringa allโ€™interno della quale verranno passati come argomenti il nome di host della macchina su cui gira MySQL (nel nostro esempio "localhost") e il nome del database da selezionare ("agenda"):

// collegamento al database con PDO
$col = 'mysql:host=localhost;dbname=agenda';

La seconda fase, quella di connessione, detta anche di costruzione di un oggetto PDO, non รจ altro che unโ€™istanza e prevede il passaggio di tre parametri obbligatori:

  1. la stringa di collegamento al database precedentemente definita;
  2. il nome dellโ€™utente attraverso i quale si desidera effettuare lโ€™autenticazione;
  3. la password relativa allโ€™utente scelto;
  4. opzionalmente รจ possibile aggiungere un argomento relativo alle opzioni del driver utilizzato per la connessione.

Nel nostro caso, il codice necessario per la costruzione di un oggetto PDO potrebbe essere il seguente:

// connessione al database con PDO
$db = new PDO($col , 'username', 'password');

Se tutto dovesse andare per il meglio e non si dovessero ricevere notifiche di errore, la nostra applicazione sarร  pronta per lโ€™accesso ai dati; in ogni caso, dato che in fase connessione possono verificarsi non di rado degli imprevisti, รจ bene affrontarla allโ€™interno di un blocco per la gestione delle eccezioni, ad esempio:

# gestione delle eccezioni in fase di connessione con PDO

// collegamento al database
$col = 'mysql:host=localhost;dbname=agenda';

// blocco try per il lancio dell'istruzione
try {
  // connessione tramite creazione di un oggetto PDO
  $db = new PDO($col , 'username', 'password');
}
// blocco catch per la gestione delle eccezioni
catch(PDOException $e) {
  // notifica in caso di errorre
  echo 'Attenzione: '.$e->getMessage();
}

Nellโ€™esempio abbiamo:

  • definito la stringa di collegamento, in questa fase non ci sono eccezioni da gestire, eventuali notifiche verranno infatti prodotte in fase di connessione;
  • aperto un blocco "try" allโ€™interno del quale lanciare lโ€™istruzione per la connessione attraverso la costruzione di un oggetto PDO;
  • aperto un blocco "catch" allโ€™interno del quale gestire eventuali eccezioni verificatesi durante la connessione.

Il blocco "catch" entrerร  in azione soltanto nel caso in cui dovesse presentarsi unโ€™eccezione da gestire, diversamente lโ€™esecuzione si esaurirร  con il lancio della connessione tramite "try" e si potrร  procedere con la fase di accesso ai dati.

Generare transazioni con PDO

Una volta superata la fase di costruzione di un oggetto PDO, questo metterร  a disposizione alcuni metodi grazie ai quali manipolarne le proprietร ; uno dei vantaggi nellโ€™impiego di PDO sta nella possibilitร  di effettuare delle transazioni, esse consentono di rendere inefficaci determinate istruzioni SQL nel caso in cui esse non abbiano avuto esito positivo. Un DBMS non รจ in grado di dar vita ad una transazione se questa non le viene richiesta, le interrogazioni sono infatti regolate da un meccanismo denominato auto-commit che applica delle modifiche a carico dei dati o della loro struttura una volta che sono state lanciate; una transazione permetterร  invece di ritornare allo stato originario con evidenti implicazioni per la sicurezza e lโ€™integritร  dei dati.

PDO mette a disposizione unโ€™apposito metodo per inizializzare una transazione (beginTransaction()), grazie ad esso sarร  possibile introdurre un numero indefinito di interrogazioni avendo la sicurezza di poter riportare le proprie tabelle alla condizione originaria:

// lancio di una transazione con PDO
$db->beginTransaction();

Per introdurre delle query, PDO mette a disposizione il metodo exec(), da richiamare per ogni diversa interrogazione, la loro esecuzione non apporterร  modifiche ai dati o alla struttura in cui sono inseriti e questi potranno essere riportati allo stato precedente tramite il metodo rollback(); si analizzi il seguente esempio:

// disabilitazione dell'auto-commit
$db->beginTransaction();

// esecuzione delle query
$sql = $db->exec("DROP TABLE recapiti");
$sql = $db->exec("UPDATE anagrafica SET nome = 'Rossi' WHERE id = 1");

// ritorno alla situazione precedente
$db->rollBack();

Nel codice proposto:

  • viene inizializzata una transazione;
  • vengono lanciate due query differenti che perรฒ potrebbero dar luogo ad anomalie nella nostra struttura dei dati;
  • viene introdotto il metodo rollback() per rendere inefficaci le modifiche che sarebbero state apportate dalle query.

Lโ€™introduzione rollback(), a transazione iniziata, impedisce che vengano applicate delle modifiche, ma รจ bene tenere presente che il suo utilizzo riabilita lโ€™auto-commit, quindi, se si desidera disabilitarlo per le query successive sarร  necessario dare il via ad una nuova transazione.

Naturalmente, nello sviluppo di unโ€™applicazione รจ necessario, prevedere anche funzionalitร  in grado di apportare modifiche ai dati gestiti, in modo da rendere permanenti i risultati dell query quando lo si desidera; con PDO ciรฒ รจ possibile grazie al metodo commit() che ha il compito di riabilitare lโ€™auto-commit disabilitato tramite beginTransaction(); commit() ha una funzione opposta a quella di rollback, grazie ad esso infatti verranno apportati a carico dei dati i cambiamenti previsti dalle query; si analizzi il seguente esempio:

// disabilitazione dell'auto-commit
$db->beginTransaction();

// esecuzione delle query
$sql = $db->exec("UPDATE anagrafica SET nome = 'Luca' WHERE id = 1");

// applicazione delle modifiche
$db->commit();

commit() non necessita del passaggio di argomenti, รจ infatti valido allโ€™interno del contesto della transazione che va a terminare; naturalmente, come nel caso di rollback(), ma per motivi ancora piรน evidenti, questo metodo riporta le query in stato di auto-commit che potrร  essere disabilitato iniziando una nuova transazione; in ogni caso รจ bene ricordare che รจ un errore annidare delle transazioni, ognuna di esse dovrร  concludersi infatti con il ritorno allโ€™auto-commit.

Metodi per lโ€™esecuzione delle query

Il metodo piรน semplice messo a disposizione da un oggetto PDO per lโ€™esecuzione delle interrogazioni รจ query(), a differenza di altri metodi analizzati nel corso di questa trattazione, esso ha il compito di eseguire una query direttamente cosรฌ comโ€™รจ senza alcuna preparazione. query() deve essere utilizzato nei casi in cui si debba accedere a fonti di dati interne e sicure e quando si ha la sicurezza che la query potrร  produrre qualche risultato valido, per il resto si tratta di un metodo estremamente semplice da utilizzare perchรฉ consente lโ€™accesso diretto ai dati. A questo proposito, si analizzi il seguente esempio:

# utilizzo del metodo query()

// definizione della query 
$sql = 'SELECT nome, cognome, cap FROM anagrafica ORDER BY id';  

// visualizzazione dei risultati
foreach($db->query($sql) as $row){  
  echo $row['nome']. '<br>';
  echo $row['cognome']. '<br>';  
  echo $row['cap']. '<br>';  
}

Il codice proposto mostra un esempio di esecuzione di una query attraverso il metodo query(), lโ€™interrogazione viene passata come argomento al metodo allโ€™interno di un ciclo foreach in quanto il suo risultato รจ un PDOStatement object (oggetto restituito da PDO), contenente i risultati ottenuti tramite lโ€™interrogazione, che puรฒ essere manipolato come un array.

Il metodo prepare() di PDO mette a disposizione uno strumento piรน avanzato e piรน sicuro per lโ€™esecuzione delle query, attraverso di esso infatti sarร  possibile accedere in "modalitร  protetta" anche a dati provenienti da fonti esterne, inoltre, il metodo consente di mettere il database interrogato al riparo di minacce come per esempio le SQL Injections; si analizzi il seguente esempio:

# utilizzo del metodo prepare()

// preparazione della query 
$sql = $db->prepare('SELECT nome, cognome FROM anagrafica');

// esecuzione della query 
$sql->execute(); 

// creazione di un array dei risultati 
$res = $sql->fetchAll();

// visualizzazione dei risultati 
print_r($res);

Nel codice proposto prepare() mette a disposizione una query che dovrร  essere eseguita tramite lโ€™oggetto execute() (restituito dallo stesso prepare()), il PDOStatement object fetchAll() avrร  quindi il compito di restituire un array() multidimensionale contenente lโ€™output dellโ€™interrogazione i cui valori potranno essere stampati tramite la funzione print_r() associati alle relative chiavi.

PDO mette a disposizione un metodo appositamente concepito per il quoting delle stringhe anche allโ€™interno delle istruzioni SQL; come รจ noto, il quoting รจ una procedura che si utilizza allโ€™interno di un sorgente per la corretta chiusura di stringhe tra apici, in modo che queste siano definite tramite la giusta sintassi e non siano confuse dallโ€™applicazione con altri frammenti di codice; a questo scopo in PDO si utilizza quote() che svolge una funzione molto simile alle ben conosiute mysql_escape_string() e mysql_real_escape_string(), si tratta di un metodo in grado di delimitare una stringa di input con apici e di effettuare lโ€™escaping degli apici singoli per la stringa stessa. Si analizzi il seguente esempio:

# quoting delle stringhe con quote()

// definizione della stringa
$stringa = 'Ciao!';

// stampa a video senza quoting
echo $stringa;

// stampa a video con quoting
echo $db->quote($stringa);

La prima istruzione del codice proposto stamperร :

Ciao!

La seconda stamperร  invece:

'Ciao!'

In quanto il metodo quote() provvederร  a delimitarla tramite apici eseguendone il quoting.

PDO e data binding

Nei capitoli precedenti รจ stata sottolineata la capacitร  di prepare() nel permettere di formulare query sicure sia su dati provenienti da fonti interne che esterne; prepare() mette perรฒ a disposizione anche altre funzionalitร  tra cui, per esempio, quella importantissima di poter eseguire il bind dei dati allโ€™interno di query. Grazie al bind sarร  possibile infatti utilizzare dei valori da passare come argomenti delle query senza specificarli direttamente, cioรจ sostituendoli con dei caratteri che ne forniscano la rappresentazione.

A proposito di quanto appena detto, si analizzi il seguente esempio:

# bind dei dati con PDO

// preparazione della query
$sql = $db->prepare('DELETE FROM anagrafica WHERE nome = ? AND anni = ?');

// esecuzione della query con sostituzione dei valori 
$sql->execute(array('Luca', 43));

Teoricamente, una query come quella contenuta nel codice proposto non avrebbe senso, infatti il linguaggio SQL non prevede lโ€™utilizzo di punti interrogativi al posto dei valori, il DBMS non sarebbe quindi in grado di intepretarli e darebbe luogo ad un insuccesso dellโ€™interrogazione. Grazie ad execute() รจ perรฒ possibile proporre un array che presenti quali valori gli argomenti da passare come parametri alla query; nel caso specifico dellโ€™esempio proposto la query verrร  reintepretata quindi nel modo seguente:

DELETE FROM anagrafica WHERE nome = 'Luca' AND anni = 42

Il bind dei dati puรฒ essere molto utile nel caso in cui si desideri lavorare su query a cui passare arbitrariamente dei valori, naturalmente questo sistema ha i suoi limiti e per utilizzarlo sarร  necessario passare i valori da sostituire ai punti interrogativi nellโ€™ordine corretto in cui dovranno essere utilizzati (in pratica, da sinistra verso destra). Inoltre, allโ€™interno dellโ€™array introdotto da prepare(), i valori dovranno rispettare il tipo di dato relativo ai campi utilizzati in query, quindi una stringa dovrร  essere delimitata da apici mentre questi dovranno essere assenti nel caso dei valori numerici.

Per contro, รจ interessante notare come nella query in cui รจ stato effettuato il bind dei dati non siano stati utilizzati gli apici per delimitare il punto interrogativo che rappresenta il valore stringa:

DELETE FROM anagrafica WHERE nome = ? ..

Ciรฒ non รจ stato necessario perchรฉ PDO รจ in grado di capire il tipo di dato da utilizzare grazie a quello giร  associato al corrispondente valore contenuto nellโ€™array; nel caso in cui lโ€™array dovesse contenere valori associati a tipi di dato incoerenti con quelli previsti per la query, il risultato sarร  semplicemente un insuccesso dellโ€™interrogazione, ma il database non sarร  esposto a pericoli derivanti da SQL injections.

I punti interrogativi possono essere sostituiti utilizzando un altro sistema per il bind dei dati basato su stringhe; si analizzi il seguente esempio:

# bind dei dati con PDO

// preparazione della query
$sql = $db->prepare('DELETE FROM anagrafica WHERE nome = :nome AND anni = :anni');

// esecuzione di query con sostituzione dei valori 
$sql->execute(array(':nome'=>'Luca', ':anni'=>42));
$sql->execute(array(':nome'=>'Max', ':anni'=>56));

Il sistema si basa su un meccanismo molto simile a quello proposto in precedenza ma si rivela piรน leggibile in quanto permette di fare dei riferimenti diretti ai nomi dei campi coinvolti durante lโ€™interrogazione.

Sempre a proposito del bind dei dati, PDO mette inoltre a disposizione un oggetto, bindParam(), che consente di specificare una variabile o un valore come parametro per una query prima della sua esecuzione e la posizione di questo argomento nellโ€™istruzione SQL:

# bind dei dati con PDO

// definizione di una variabile  
$nome = 'Luca';

// preparazione della query
$sql = $db->prepare('DELETE FROM anagrafica WHERE nome = :nome AND anni = 42');

// sostituzione di valori
$sql->bindParam(':nome', $nome);

// esecuzione della query
$sql->execute();

Nellโ€™esempio, BindParam() presenta due argomenti: il primo indica quale rappresentazione sotto forma di stringa (":nome") dovrร  essere sostituita nella query con il relativo valore, il secondo indica la variabile ("$nome") contenente il valore da utilizzare per la sostituzione. In generale, รจ possibile dire che bindParam() accetta due argomenti:

  1. un numero intero nel caso in cui si utilizzi il punto interrogativo, una stringa se si utilizza il metodo basato sulle stringhe; se si utilizza un intero, questo sarร  il numero dโ€™ordine del parametro nella query.
  2. una variabile passata per riferimento il cui valore ha il compito di sostituire quello rappresentato dal primo argomento.

Si riformuli la query proposta in precedenza utilizzando il sistema di bin basato sul punto interrogativo:

$sql = $db->prepare('DELETE FROM anagrafica WHERE nome = ? AND anni = 42');

In questo secondo caso, i parametri introdotti da bindParam() saranno i seguenti:

$sql->bindParam(1,$nome);

Il valore numerico intero "1" indicherร  infatti che la varibile passata come secondo parametro dovrร  essere utilizzata per la sostituzione del primo valore utilizzato nel confronto introdotto tramite la clausola WHERE.

Altri contenuti interessanti

Pubblicitร 
Claudio Garau
Claudio Garau
Web developer, programmatore, Database Administrator, Linux Admin, docente e copywriter specializzato in contenuti sulle tecnologie orientate a Web, mobile, Cybersecurity e Digital Marketing per sviluppatori, PA e imprese.

Leggi anche...

Cannot modify header information โ€“ headers already sent: come risolvere lโ€™errore PHP

L'errore di PHP cannot modify header information - headers...

Ricavare lโ€™estensione di un file con PHP

Quando si lavora con i file in un'applicazione web,...

GD Library: creazione, manipolazione e ridimensionamento immagini con PHP

Le librerie GD (o GD Library), sono componenti fondamentali...

PHP: impostare il fuso orario italiano

Le tue pagine PHP non mostrano lโ€™orario corretto? Probabilmente...

PHP BBCode: script pronti allโ€™uso

A volte puรฒ aversi l'esigenza di dover offrire agli...
Pubblicitร