PDO

Per semplificare la manipolazione dei database dal punto di vista dell'interfacciamento e della sicurezza, a partire dalla versione 5.1 del PHP, è stata introdotta l'interfaccia PDO (PHP Data Objects).

Si tratta di utilizzare una classe per la creazione delle query e la lettura dei risultati, il tutto con dei controlli già integrati per garantire il massimo della sicurezza nelle tecniche di hackeraggio più diffuse, a partire dalla SQL injection.

Il vantaggio più grande dell'utilizzo dei PDO, consiste però nella scalabilità di interfacciamento con i diversi driver di connessione al database, da Oracle a MySQL, fino anche ai database non relazionali di ultima generazione (MongoDB su tutti), cambiando una sola istruzione.

PDO

L'unico requisito per poter utilizzare l'intefaccia PDO è una versione superiore alla 5 di PHP.
Verificata la versione, occorre abilitare il modulo dal php.ini chiamato pdo.so o php_pdo.dll (Win).

Generalmente però quest'ultimo passaggio è superfluo per chi ha già installato una versione di PHP idonea.

Utilizzo PDO

Integrazione del PDO in un progetto PHP

Utilizzo PDO

L'utilizzo dei PDO all'interno del PHP, è legato al concetto try/catch, un altro pattern di programmazione molto diffuso che permette di controllare in maniera semplificata la generazione di errori di elaborazione.

Il pattern try/catch consiste infatti nell'esecuzione di alcune operazioni all'interno della procedura try, le quali in caso di errore saranno preventivamente gestite nell'apposito catch al fine di evitare interruzioni impreviste nel software.

<?php
try{
	# Operazioni da svolgere
}catch(Exception $e) {
	# In caso di errore stampa un messaggio a schermo
	echo "Generazione dell'errore ".$e->getMessage();
}

Utilizzando l'approccio try/catch si riescono a gestire dunque tutte le sezioni critiche del codice.

Essendo l'interazione con il database molto delicato come processo, si consiglia dunque l'implementazione delle query sempre all'interno di un try/catch.

Utilizzo PDO

La logica di implementazione nel PHP, è sempre legata all'ormai deprecato metodo mysql visto all'inizio del corso.
Il primo step, consiste sempre nell'istanza della classe PDO che stabilisce una connessione al database.

<?php
try {
	$connection = new PDO("mysql:host=".$host.";dbname=".$database, $user, $password);

	// In caso di errore (catch) associa le informazioni provenienti dal PDO
	$connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
catch(PDOException $e) {
	echo $e->getMessage();
}

Come si può notare, si crea un'istanza della Classe PDO, passando come parametri:

  • Una stringa che concatena il driver di connessione al database (mysql), l'host e il nome del database
  • L'username per l'autenticazione
  • La password per l'autenticazione

Utilizzo PDO

Una volta definita la connessione, è possibile svolgere le query.
Grazie ai PDO ci sono tre diverse opzioni a disposizione per l'esecuzione delle query:

  • Esecuzione diretta
  • Data binding
  • Transazioni

Utilizzo PDO

Per poter effettuare un'esecuzione diretta della query, si utilizzano i metodi:

    query - se si tratta di operazioni di SELECT exec - se si tratta di operazioni di UPDATE, DELETE o INSERT

Il metodo query è infatti previsto per restituire un resultset gestibile con i risultati estratti, mentre il metodo exec, restituisce di default il numero di righe interessate nell'elaborazione.

L'esecuzione delle query con questi due metodi (sempre protetta da try/catch), sarà dunque del tipo:

<?php
$righe_eliminate = $connection->exec("DELETE FROM utenti WHERE id > 10"); 
echo "Righe eliminate: ".$righe_eliminate;
<?php
$risultato = $connection->query("SELECT * FROM utenti WHERE id < 10"); 
foreach($risultato->fetchAll(PDO::FETCH_ASSOC)) AS $chave => $valore) {
	# Gestisco riga risultato
}

L'opzione PDO::FETCH_ASSOC utilizzata nella lettura dei risultati, permette di avere un array associativo per la gestione chiave valore. Se si omette questa opzione, tutti i risultati saranno presenti sia con chiave numerica progressiva, che con chiave rispettiva al nome del campo estratto.

Utilizzo PDO

Il vero vantaggio nell'utilizzo dei PDO, si nota però con il data binding, ovvero l'elaborazione di query dove i campi sono associati in maniera manuale e ben definita per garantire il massimo dell'efficienza e della sicurezza.

Ci sono due diverse tecniche per l'applicazione del data binding. Il primo metodo consiste nell'associazione ordinata dei campi:

<?php
$query = "INSERT INTO utenti(nome, cognome) VALUES(?, ?)";
$query_preparata = $connection->prepare($query);
$risultato1 = $query_preparata->execute(array("Francesco", "Pisciotta"));
$risultato2 = $query_preparata->execute(array("Antonio", "De Giorgi"));

Analizzando la prima tecnica di data binding, si nota che:

  • Nella query i valori sono sostituiti dal simbolo "?"
  • Si crea una query prepared parametrica, pronta per essere utilizzata (anche più volte) con i parametri da fornire di volta in volta
  • Si sfrutta il metodo execute per passare tramite array i parametri delle query da eseguire

Nel caso di INSERT, per leggere il contenuto dell'ID autoincrement, si utilizzerà:
$connection->lastInsertId();

Utilizzo PDO

Il secondo approccio al data binding è quello più ordinato, che permette di associare in maniera inequivocabile i singoli campi da elaborare nella query:

<?php
$query = "INSERT INTO utenti(nome, cognome) VALUES(:nome_utente, :cognome_utente)";
$query_preparata = $connection->prepare($query);
$query_preparata->bindParam(":nome_utente", "Francesco", PDO::PARAM_STR);
$query_preparata->bindParam(":cognome_utente", "Pisciotta", PDO::PARAM_STR);
$risultato = $query_preparata->execute();

Analizzando la seconda tecnica di data binding, si nota che:

  • Nella query i valori sono sostituiti da un nome specifico preceduto dal simbolo ":"
  • Si crea una query prepared parametrica, pronta per essere utilizzata (anche più volte) con i parametri da fornire di volta in volta
  • Si sfrutta il metodo bindParam per associare a ciascun parametro, un valore e una tipologia di campo (stringa, numero, data)
  • Si sfrutta il metodo execute per eseguire la query preparata

Utilizzo PDO

Indipendentemente dalla tecnica utilizzata, il data binding è fondamentale in quanto include tutti i controlli relativi alla SQL injection, in maniera del tutto autonoma!

Tutti i controlli lato codice per la sicurezza, come ad esempio le funzioni addslashes o mysql_escape_string, diventano dunque superflui.

Seppur a prima vista macchinoso, l'approccio del data binding applicato all'interno dei DAO permette una eccezionale scalabilità.

Utilizzo PDO

Ultimo ma non ultimo, è il concetto di transazioni che è possibile applicare alle query del database.
Le transazioni sono una serie di query che devono essere svolte necessariamente in sequenza in quanto dipendono reciprocamente e dunque non possono andare in errore per il corretto svolgimento del processo.

Per comprendere meglio l'esigenza di una transazione sui database, si può pensare al processo di eliminazione di un utente e di tutte le informazioni ad esso correlate. Per esempio se su un social network si deve eliminare un utente, di conseguenza tutte le interazioni sui post, i contenuti pubblicati e le relazioni con i followers, devono scomparire dal portale.
Ma cosa accadrebbe se in seguito alla cancellazione della riga dell'utente, le query di cancellazione contenuti andassero in errore? Si creerebbero record morti, che necessiterebbero di apposite procedure per il riconoscimento e l'eliminazione.

Grazie alle transazioni invece, tutte le query che comportano il processo di eliminazione dell'utente, possono essere svolte in sequenza e in caso di errore su una delle query in elenco, si esegue il processo di rollback ovvero ripristino delle operazioni svolte sulle precedenti query della sequenza.

Utilizzo PDO

Con i PDO le transazioni si gestiscono in maniera agevolata attraverso il processo di seguito:

<?php
try{
    // inizilizzazione della transazione
    $connection->beginTransaction();
     
    // query da eseguire
    $connection->exec("DELETE FROM utenti WHERE id = 1"); 
    $connection->exec("DELETE FROM post WHERE id_utente = 1"); 

	// esecuzione delle query in sequenza
	$connection->commit();
}catch(PDOException $e){ 
    // annullamento in caso di errore di una query
    $connection->rollBack();
     
    // eventuale messaggio di errore
    echo $e->getMessage();
}

<Thank u!>

Sempre pronto a darti una mano