Oggetti restituiti da PDO
Fino ad ora abbiamo visto come utilizzare direttamente un oggetto PDO ma non abbiamo ancra visto niente sul cosa restituiscono i metodi dell' oggetto PDO.
Questi ulteriori oggetti / variabili restituiti da alcuni dei metodi gia' descritti si chiamano PDOStatement .
La descrizione degli statements a differenza di quella per i metodi non seguira' l' ordine di php.net poiche' vanno chiariti gradualmente alcuni concetti.
PDOStatement::execute
Abbiamo visto il metodo prepare per usare query sicure ma siamo rimasti ad un punto morto ... cosa me ne faccio di prepare se poi la query non ha senso ?
Infatti tale metodo restituisce una variabile / oggetto in grado di effettuare le modifiche necessarie per eseguire la query.
Primo esempio:
Codice PHP:
// preparo la query
$stpdo = $mypdo->prepare('UPDATE mytable SET user = "pippo" WHERE id > 10');
// eseguo la query
$stpdo->execute();
Tutto qua' .... preparo, eseguo, questa e' la situazione base con la quale interagire su un prepare ... inutile e dispendiosa contro PDO::query o PDO::exec ???
Ovvio, se consideriamo che la query ce la siamo fatta noi a mano ... ma non sono queste le situazioni tipo dove il PDOStatement e' veramente formidabile.
Quali allora ? Ecco solo un esempio della stessa query:
Codice PHP:
// preparo la query
$stpdo = $mypdo->prepare('UPDATE mytable SET user = ? WHERE id > ?');
// non posso eseguirla poiche' ci sono 2 punti interrogativi che in SQL non avrebbero significato ...
// come fare ? ... ecco un metodo:
$stpdo->execute(array('pippo', 10));
execute puo' ricevere come parametro un array contenente le informazioni da sostituire ai punti di domanda.
Quando utilizziamo i punti di domanda l' ordine con cui inviare i parametri ad execute e' fondamentale ed e' esattamente da sinistra a destra.
Una cosa da notare e' la mancanza di ->quote o di escaping della stringa pippo ... non serve ? esatto, non serve poiche' se ne occupa la classe.
Questa sapra' che al primo punto di domanda della query ci deve essere esattamente la stringa contenuta nel primo parametro dell' array passato quindi anche se questo dovesse contenere chissacosa, non sara' possibile tentare sql injections piu' comuni perche' il valore verra' trattato esattamente come stringa tra apici e non come testo su una query.
Idem per il secondo punto di domanda,ovvio che se al posto di un intero passiamo una stringa la query non andra' a buon fine ma comunque non ci saranno risultati inaspettati.
Bellino eh ? ... e c'e' un altro modo gia' accennato molto piu' leggibile che ci permette di personalizzare ulteriormente la fase di preparazione query e quella di esecuzione.
Esempio piu' leggibile:
Codice PHP:
// preparo la query
$stpdo = $mypdo->prepare('UPDATE mytable SET user = :user WHERE id > :id');
// non posso passare in execute un array a chiavi numeriche
// perche' il PDO non saprebbe cosa farsene o meglio on troverebbe riscontri
// nella query ... quindi ? facciamo cosi'
$stpdo->execute(array(':user'=>'pippo', ':id'=>10));
// oppure, visto che e' indifferente a questo punto l 'ordine ..
$stpdo->execute(array(':id'=>10, ':user'=>'pluto'));
Diciamo che l' utilita' dello Statement e' gia' strabiliante ? ... boh, io lo dico 
Ma cosa fa oltre a questo execute ? Piu' che altro c'e' da chiedersi cosa fa il prepare , perche' la vera chicca sta' nel fatto che una volta preparata una struttura di una query e' possibile fare tanti execute quanti sono quelli che ci servono.
L' esempio mostrato precedentemente ad esempio funziona alla perfezione e il risultato sara' quello del secondo execute, visto che e' un update.
Ma se avessimo bisogno di tanti insert, di diverse select o di altre query dove la sintassi e' identica ma i valori cambiano ( id, nomi, quello che vi pare ) sara' possibile evitare di rifare il prepare ed utilizzare quello gia' dichiarato in fase di assegnazione / creazione dello Statement.
Codice PHP:
$stpdo = $mypdo->prepare('UPDATE mytable SET user = :user WHERE id > :id');
$stpdo->execute(array(':user'=>'pippo', ':id'=>1));
$stpdo->execute(array(':user'=>'pluto', ':id'=>5));
$stpdo->execute(array(':user'=>'paperino', ':id'=>10));
$stpdo->execute(array(':user'=>'topolino', ':id'=>15));
$stpdo->execute(array(':user'=>'cip', ':id'=>20));
$stpdo->execute(array(':user'=>'ciop', ':id'=>25));
$stpdo->execute(array(':user'=>'ziopaperone', ':id'=>30));
// ... e cosi' via ...
Inoltre le prestazioni sono veramente elevate perche' viene sfruttato un sistema interno di caching da parte del prepare.
A questo statement aggiungo solo che torna un valore di tipo booleano che ci permette di sapere se la query e' stata eseguita con successo oppure no.
if( $stpdo->execute() ) // ... OK, query eseguita con successo
PDOStatement::bindParam
Se con execute possiamo lavorare tranquilli in inserimento dati, con bindParam possiamo anche stare tranquilli sul tipo di dato utilizzato in input o output durante le stored procedures.
Sintassi:
bool PDOStatement::bindParam ( mixed parameter, mixed &variable [, int data_type [, int length]] )
Cominciamo con i primi 2 ...
mixed parameter e' un numero intero se utilizziamo il punto interrogativo, altrimenti una stringa se utilizziamo il metodo piu' leggibile.
mixed &variable e' una variabile passata per riferimento con il contenuto che deve sostituire il valore descritto nel primo parametro.
Esempio:
Codice PHP:
// assegno la variabile da usare
$user = 'pippo';
// preparo la query
$stpdo = $mypdo->prepare('UPDATE mytable SET user = ? WHERE id > 1');
// assegno il valore per il punto interrogativo
$stpdo->bindParam(1, $user);
// eseguo la query
$stpdo->execute();
// ... oppure ...
$stpdo = $mypdo->prepare('UPDATE mytable SET user = :user WHERE id > 1');
$stpdo->bindParam(':user', $user);
$stpdo->execute();
Fin qui' potrebbe risultare piu' comodo utilizzare l' array da inviare all' execute poiche' non ci sono differenze sostanziali per sicurezza o prestazioni se non per tempo e codice 'sprecato' per scrivere tutti questi bind.
Infatti per i soli input e' consigliato nel sito stesso di php.net di utilizzare execute e l' array per l' execute.
Cerchiamo di capire meglio dov'e' che bindParam fa la differenza ... e cominciamo con il terzo parametro opzionale, il data_type.
Questo specifica il tipo di dato in input o output all' interno della stored procedures e puo' essere piu' di uno grazie all' operatore di bitwise | .
In caso di parametro da usare in output e' necessario specificare il quarto parametro ovvero la length.
Per approfondimenti o esempi andate in questa pagina:
http://it2.php.net/manual/it/functio...-bindparam.php
PDOStatement::bindColumn
Metodo per Statement molto interessante, analogo al forse piu' conosciuto bindResult , quello usato ad esempio per MySQLI in PHP >= 5.
In pratica si tratta di specificare prima come devono chiamarsi i dati letti da una query e di che tipo devono essere.
Sintassi:
bool PDOStatement::bindColumn ( mixed column, mixed ¶m [, int type [, int maxlen [, mixed driver_options]]] )
mixed column e' il numero della colonna restituita dalla query mentre mixed ¶m e' la variabile contenente il valore restituito.
Il primo e piu' semplice esempio che mi viene in mente e' il seguente:
Codice PHP:
// preparo la query
$stpdo = $mypdo->prepare('SHOW TABLES');
// preparo il risultato
$stpdo->bindColumn(1, $table_name);
// eseguo la query
$stpdo->execute();
// eseguo il loop sul risultato per avere tutti i nomi delle tabelle
while($row = $stpdo->fetch(PDO_FETCH_BOUND))
echo $table_name.'
'; // stampo i nomi
Cosa c'e' di anomalo ? Una valanga di cose ... innanzitutto la variabile $table_name era inesistente e cio' nonostante non ho avuto alcun notice o warning poiche' questa viene popolata e inizializzata ( se inesistente ) automaticamente all' interno della chiamata a bindColumn ... cosa infattibile normalmente con php.
Poi , oltre a questo , la variabile viene automaticamente riassegnata ad ogni ciclo del while , permettendoci di evitare i soliti $row[N] per il fetch_row o $row['key'] per il fetch_assoc.
Divertente ? Penso proprio di si, soprattutto comodo e molto piu' leggibile.
Codice PHP:
$stpdo = $mypdo->prepare('SELECT id, user FROM mytable');
$stpdo->bindColumn(1, $id, PDO_PARAM_INT);
$stpdo->bindColumn(2, $user, PDO_PARAM_STR);
$stpdo->execute();
while($row = $stpdo->fetch(PDO_FETCH_BOUND))
echo "{$id} => {$user}
";
Ecco che subentrano il terzo e quarto parametro opzionale per questo metodo di statement, il tipo di dato e la lunghezza massima.
Sinceramente dai tests effettuati non sembrano cambiare di molto il risultato ... anzi , sembrano non influenzarlo affatto nel senso che non esiste un casting del tipo di dato.
Probabilmente sono utili al fine di eseguire una query ottimizzata, dove lo scripter dice a priori il tipo di dato da cercare per quel campo e il sistema interno si "organizza" per fare quel tipo di query.
Beh, lascio a voi i tests del caso, ma se dovessero essere piu' performanti specificando il tipo di dato, sarebbe bene usarlo sempre 
L' ultimo parametro, il driver_options, e' quasi una conseguenza logica del fatto che PDO e' ancora giovane e che non si vuole lasciare niente di trascurato.
Non c'e' alcuna docuentazione per ora in merito a questo parametro ma quasi sicuramente servira' in futuro per effettuare operazioni dedicate durante la query a seconda del database utilizzato.
PDOStatement::fetch
Abbiamo usato il "solito while" , seppur auiutati dalla procedura bindColumn ... ma come possiamo predere i valori in modo rapido / semplice ?
Grazie al fetch , metodo implementato nello statement in grado di spostare il cursore dei risultati di una query in un ciclo, proprio come un while.
Esempio:
Codice PHP:
// stessa procedura di prima ...
while($stpdo->fetch(PDO_FETCH_BOUND))
echo "{$id} => {$user}
";
Stesso risultato, infatti il "$row =" in questo caso non serve proprio perche' usiamo il parametro PDO_FETCH_BOUND che si basa sui vari bindColumn precedentemente dichiarati.
Ma non e' detto che ogni volta si debba usare il comodo bindColumn, no ?
E infatti il row potrebbe esserci utile con altri parametri da specificare nel fetch, ovvero:
PDO_FETCH_ASSOC #usatelo come mysql_fetch_assoc
PDO_FETCH_BOTH #usatelo come mysql fetch_array, risultato duplicato, quello di default
PDO_FETCH_BOUND #usatelo come da esempio per il bindColumn
PDO_FETCH_LAZY #combina *_BOTH e *_OBJ , l' ammazza prestazioni , imho ...
PDO_FETCH_OBJ #ritorna un oggetto con le proprieta' che hanno il nome dei campi, interessante
PDO_FETCH_NUM #il mio preferito ovviamente .... analogo a mysql_fetch_row
Alcuni esempi basati sulla query precedente:
Codice PHP:
// *_NUM
while($row = $stpdo->fetch(PDO_FETCH_NUM))
echo "{$row['0']} => {$row['1']}
";
// *_ASSOC
while($row = $stpdo->fetch(PDO_FETCH_ASSOC))
echo "{$row['id']} => {$row['user']}
";
// *_OBJ
while($row = $stpdo->fetch(PDO_FETCH_OBJ))
echo "{$row->id} => {$row->user}
"; // : sbav :
Per ora abbiamo visto il solo primo parametro ma ce ne sono altri, in effetti mi sono dimenticato la sintassi ...
mixed PDOStatement::fetch ( [int fetch_style [, int cursor_orientation [, int cursor_offset]]] )
int fetch_style e' quanto abbiamo visto fino ad ora mentre int cursor_orientation e' una costante di tipo PDO_FETCH_ORI_* riferita al tipo di cursore da utilizzare, solitamente e' NEXT quello di default, ovvero man mano che si legge il risultato questo scorre al record successivo se presente.
L? ultimo e' l' offset, ma va ? .... e cos'e' ??? ... boh
... testato ma senza successo ... spiacenti, questo offset non funziona con mysql, perlomeno per ora.
PDOStatement::fetchAll
Identico al fetch per certi versi, piu' comodo per altri ma meno configurabile.
array PDOStatement::fetchAll ( [int fetch_style] )
Come e' facile intuire con questo metodo si evita ogni loop su query ( per crearne uno su array, meglio o peggio ??? ... dipende da quanti dati vengono restituiti e quante operazioni dobbiamo fare per questi valori restituiti o con questi ) .
Non perdo tempo per dire che il fetch_style e' ovviamente identico a quello di fetch ( l' ho perso ... !!! ) e passo subito agli esempi:
Codice PHP:
$row = $stpdo->fetchAll(PDO_FETCH_OBJ);
echo '<pre>';
var_dump($row);
... un array di oggetti ... bello no ? idem con ASSOC, BOTH, NUM e gli altri, solo che non sono oggetti ( ma dai ?
)