Repository 32bit  Forum
Repository 64bit  Wiki

PHP HOWTO - Forms Framework: ffDb Sql: differenze tra le versioni

Da Slacky.eu.
 
Riga 285: Riga 285:
do
do
{
{
echo $db->getResult($i, "contenuto") . "<br />";
+
echo $db->getField("contenuto") . "<br />";
} while ($db->nextRecord());
} while ($db->nextRecord());
}
}

Versione attuale delle 12:32, 6 feb 2009

Indice

[modifica] Introduzione

Questo articolo fa parte di una serie di HOW-TO sui componenti di Forms, un framework Open Source sviluppato da Samuele "Nuitari" Diella, un programmatore con esperienza decennale nel ramo dello sviluppo web.

Nella fattispecie, ffDb_Sql è la classe preposta alla gestione della connessione ai database di tipo SQL, largamente utilizzata in tutto il framework ma utilizzabile separatamente in qualsiasi progetto.

In questo articolo verrà trattata la versione MySQL.

Si tratta di una classe che l'autore ha trovato parecchi anni fa (almeno 8) su web e che nel tempo ha modificato/riadattato/espanso/migliorato. Se qualcuno dovesse riconoscere la classe di base (per quanto non ne rimanga più molto), può segnalarlo all'autore in modo che i dovuti credits siano inclusi nel codice.

[modifica] Cosa questa guida non dice

Questa guida non spiega ne come utilizzare MySQL ne come scrivere SQL. Si tratta di nozioni che esulano dall'argomento e richiedono guide a se stanti. Lo stesso dicasi per la programmazione PHP o l'utilizzo del paradigma ad oggetti.

[modifica] Come Ottenerla

E' possibile scaricare la classe da questo indirizzo: http://www.noveanelli.com/ffDb_Sql_mysql.php.tar.gz. Per utilizzarla è sufficiente scompattare il file e posizionarlo in un qualsiasi punto del sito, a patto che sia raggiungibile con una direttiva di php "require" od "include"

[modifica] Sinossi

La classe si preoccupa di creare e gestire un link ad una connessione MySQL, fornendo al programmatore una comoda interfaccia per eseguire tutte le operazioni più comuni come l'esecuzione di una query ed il recupero di campi.

Al di la del semplice approccio ad oggetti (comunque importante per gli amanti di questo paradigma) alcuni dei vantaggi nell'utilizzo di questa classe sono:

  • permette di scrivere codice adattabile ad un qualsiasi dbms SQL, con la semplice sostituzione della classe senza dover alterare il codice a posteriori (a patto che si scriva SQL standard, ovviamente)
  • include funzioni trasparenti di bufferizzazione dei risultati per permettere la riduzione dell'overhead sul server
  • permette di gestire la connessione al Database on-demand, in modo da ridurre il carico sul server
  • permette di lavorare su più connessioni / database contemporaneamente in modo del tutto trasparente
  • include una gestione degli errori centralizzata e parametrizzabile
  • include un meccanismo di logging degli eventi utile ai fini del debug
  • permette un approccio a "cursore" nella programmazione del codice d'accesso ai risultati
  • include una funzione di escaping per evitare l'SQL injection
  • è perfettamente integrata con l'oggetto ffData, parte del framework Forms, il quale permette di gestire in modo del tutto trasparente l'internazionalizzazione e la conversione dei dati

[modifica] Istanziazione dell'oggetto

Il primo imprescindibile passo per poter utilizzare la classe è, ovviamente, crearne un istanza. Se si da un occhiata al file, si potrà notare che a dispetto del nome di quest'ultimo, la classe si chiama "ffDb_Sql" e non "ffDb_Sql_mysql". Questa scelta dipende dal fatto che, in questo modo, è possibile semplicemente cambiare l'inclusione del file (ad esempio con ffDb_Sql_oracle") per cambiare a tutti gli effetti l'engine di connessione al DB senza la necessità di dover riscrivere codice.

Procediamo quindi ad instanziare l'oggetto:

$db = new ffDB_Sql();

A questo punto, è necessario indicare all'oggetto come connettersi al database. Esistono 3 modi diversi.

Il primo consiste nel richiamare in modo esplicito il metodo connect:

$db->connect($Database, $Host, $User, $Password);

il significato dei parametri dovrebbe essere chiaro.

Il secondo consiste nell'impostare manualmente i parametri di connessione nell'istanza dell'oggetto:

$db->database = "";
$db->user     = "";
$db->password = "";
$db->host     = "";

L'ultimo consiste nel settare le seguenti costanti:

// DEFAULT DB CONNECTION
define("DATABASE_NAME", "");
define("DATABASE_HOST", "");
define("DATABASE_USER", "");
define("DATABASE_PASSWORD", "");

Se queste costanti sono presenti, tutti gli oggetti ff_Db_Sql creati utilizzeranno questi valori di default salvo differenti indicazioni. E' la scelta consigliata per un utilizzo normale della classe.

In genere, è meglio evitare di chiamare esplicitamente il metodo "connect", in favore degli altri due metodi. Il motivo è che il metodo connect esegue esplicitamente una connessione al database, anche se magari non è immediatamente richiesto. Gli altri due metodi permettono di lasciare che sia la classe a decidere quando effettuare la connessione, in modo da risparmiare utili risorse.

E' bene ricordare che dopo ogni connessione al database, qualsiasi sia il metodo impiegato, i membri dell'istanza relativi ai parametri di connessione verranno sempre reimpostati con i valori correnti. Si tratta di un meccanismo necessario a garantire la persistenza delle impostazioni.

Per effettuare una connessione ad un database MySQL, non è richiesto altro. E' tuttavia possibile impostare altri parametri utili ai fini della connessione con MySQL. Questi sono:

  • "charset_names" e "charset_collation" : permettono di forzare il tipo di locale che verrà utilizzato sulla connessione
  • persistent : permette di utilizzare una sola connessione per ogni interrogazione allo stesso server (abilitato di default)

inutile dire che se si utilizza il metodo connect() in modo esplicito questi parametri vanno impostati prima dell'esecuzione del metodo.

[modifica] Esecuzione di operazioni sul database

Una volta istanziato ed impostato l'oggetto, è possibile utilizzare i metodi messi a disposizione della classe per effettuare operazioni sul database. I metodi principali sono tre:

  • query : permette di eseguire una istruzione SQL di tipo SELECT che restituisce un risultato sotto forma di recordset.
  • execute : permette di eseguire una istruzione SQL senza che questa restituisca un recordset come risultato. E' il caso di INSERT, UPDATE e DELETE
  • lookup : permette di ottenere il valore di uno o più campi passando il nome della tabella e l'elenco delle chiavi con i rispettivi valori senza scrivere istruzioni SQL

Analizziamoli tutti nel dettaglio.

[modifica] Metodo query

Il metodo query permette di eseguire una qualsiasi istruzione di tipo SELECT che restituisce un risultato sotto forma di recordset. In realtà è possibile utilizzarlo per tutte le operazioni di tipo SQL, ma il codice della funzione è ottimizzato per la gestione di risultati, per cui è consigliato utilizzarlo solo quando necessario, per evitare spreco di risorse.

L'utilizzo di query è abbastanza banale, è sufficiente richiamarlo con la stringa SQL come parametro. Ad esempio, supponendo di voler selezionare tutti i campi di tutti record della tabella "messaggi", sarà sufficiente scrivere:

$db->query("SELECT * FROM messaggi");

Non è necessario scrivere altro, per eseguire la query in se. In caso di successo, il metodo restituirà l'id della risorsa PHP che punta al recordset risultante. Ovviamente una volta eseguita la query bisognerà gestire il risultato. A tal scopo si può tanto usare l'id della risorsa con le normali funzioni di api mysql (scelta sconsigliata), quanto le funzioni di gestione dei risultati della classe stessa, descritte più avanti (scelta consigliata).

[modifica] Metodo execute

Il metodo execute non è dissimile dal metodo query, per quanto riguarda l'utilizzo: è sufficiente invocarlo con una stringa SQL come parametro. La differenza rispetto a query consiste nel fatto che execute non contiene codice di gestione dei risultati della query, per cui è indicato per eseguire quelle operazioni SQL come INSERT, UPDATE e DELETE che non danno risultati sotto forma di recordset.

Una volta richiamato il metodo execute, è possibile verificare quanti record sono stati influenzati dall'operazione usando il metodo "affectedRows()". Ad esempio:

$db->execute("DELETE FROM messaggi WHERE ID_user = 5");
$righe_cancellate = $db->affectedRows();

Nel caso in cui si sia usato il metodo execute per inserire un record in una tabella con ID auto-incrementante, è possibile ottenere l'ID del record inserito usando il metodo getInsertID(). Ad esempio:

$db->execute("INSERT INTO messaggi (ID_user, contenuto) VALUES (1, \"hello world!\")");
$new_id = $db->getInsertID();

Ovviamente non è consigliato inserire del testo "grezzo" in un istruzione SQL in questo modo. La classe offre un metodo di gestione dell'escaping che verrà descritto in seguito.

[modifica] Metodo lookup

Il metodo lookup permette di accedere alle informazioni del database senza scrivere esplicitamente codice SQL, ma solo impostando alcuni parametri. Ovviamente si tratta di un metodo limitato rispetto al metodo query, in quanto permette di recuperare dati da un solo record di una tabella esistente, ciononostante si tratta di un metodo alquanto comoda che permette di ridurre la scrittura del codice per task semplici.

Il metodo lookup accetta i seguenti parametri:

lookup($tabella, $chiave, $valorechiave = NULL, $defaultvalue = NULL, $nomecampo = NULL, $tiporestituito = "Text", $bReturnPlain = FALSE)

che spieghiamo subito nel dettaglio:

  • tabella : palesemente il nome della tabella da cui recuperare il/i valore/i
  • chiave : il campo su cui eseguire il match, può assumere valori differenti a seconda della necessità. Può essere tanto una singola stringa con il nome della chiave su cui si desidera fare il match, quanto un array contenente una serie di coppie chiave/valore per un match multiplo.
  • valorechiave : nel caso in cui chiave sia una stringa, contiene il valore con cui fare il match. Può essere un oggetto ffData.
  • defaultvalue : il valore da restituire nel caso in cui la lookup non trovi occorrenze. Può essere lasciato a NULL e può essere un oggetto ffData.
  • nomecampo : il campo il cui valore va recuperato, può assumere valori differenti a seconda delle necessità. Può essere tanto una stringa indicante un campo singolo da recuperare, quanto un array contenente un elenco di campi con i rispettivi tipi dati. Nel caso in cui sia un array, la funzione restituirà un array come risultato contente le coppie chiave/valore.
  • tiporestituito : il tipo di dato da restituire nel caso in cui nomecampo sia una stringa, altrimenti ignorato. Nel caso in cui non si stia usando l'integrazione con Forms Framework, è possibile specificare solo "Text" e "Number".
  • bReturnPlain : permette di forzare la restituzione dei valori sotto forma di plain values se si sta utilizzando Forms Framework.

Quindi, supponendo si voglia recuperare il campo "contenuto" della tabella "messaggi", con campo chiave "ID" di valore "5", scriveremo:

$valore = $db->lookup("messaggi", "ID", 5, NULL, "contenuto");

Supponendo di voler recuperare lo stesso campo della stessa tabella, ma facendo il match con l'utente avente ID "1000" in data "01/01/2008", scriveremo:

$valore = $db->lookup(
			"messaggi"
			, array	(
					  "ID_user"	=> "1000"
					, "data"	=> "2008-01-01"
				)
			, NULL
			, NULL
			, "contenuto"
		);

supponiamo infine di voler fare la selezione di cui sopra, ma di voler recuperare oltre al contenuto anche l'orario:

$valore = $db->lookup(
			"messaggi"
			, array	(
					  "ID_user"	=> "1000"
					, "data"	=> "2008-01-01"
				)
			, NULL
			, NULL
			, array (
					  "contenuto"	=> "Text"
					, "orario"	=> "Text"
				)
		);

eccetera eccetera. L'utilizzo del metodo dovrebbe risultare chiaro.

[modifica] Gestione dei risultati

Supponendo di aver eseguito il metodo query ed avendo quindi ottenuto un risultato sotto forma di recordset, quali metodi offre la classe per la loro gestione? Ecco un elenco completo:

Metodi di recupero delle informazioni

  • numRows() : permette di conoscere la lunghezza del recordset
  • numFields() : permette di conoscere quanti campi sono presenti in un record
  • isSetField() : permette di sapere se esiste un campo chiamato $Name nel risultato

Metodi di recupero dei valori

  • getResult() : permette di recuperare il valore di un campo di una record specifico, specificando il nome o l'offset ed il numero della riga.
  • getField() : permette di recuperare il valore di un campo del record corrente, specificandone il nome. Richiede l'utilizzo dei metodi di spostamento.

Metodi di spostamento fra i record

  • nextRecord() : si sposta al record successivo. Nel caso di una query appena eseguita, si sposta al primo record.
  • seek() : si posiziona ad un record specifico.
  • jumpToPage() : suddivide il recordset in pagine ipotetiche di cui viene specificata la grandezza e si posiziona al primo record della pagina indicata.

Analizziamoli nello specifico.

[modifica] Metodi di recupero delle informazioni

L'utilizzo di questi metodi dovrebbe essere palese e non necessitare di una spiegazione. L'unica nota va fatta su "isSetField()", il quale richiede come parametro il nome del campo di cui verificare l'esistenza.

[modifica] Metodi di recupero dei valori

A seconda del tipo di metodo utilizzato per leggere il recordset, è possibile utilizzare rispettivamente getResult() o getField(). Il primo è da utilizzare nel caso si voglia ciclare i record in base al loro indice numerico, come accade con un semplice ciclo for. I parametri accettati da getResult() sono i seguenti:

getResult($row = NULL, $Name, $data_type = "Text", $bReturnPlain = FALSE)

in dettaglio:

  • row : il numero della riga da cui recuperare il campo
  • Name : il nome o l'offset del campo da recuperare. Per offset s'intende la posizione partendo da sinistra, per cui 0 per il primo, 1 per il secondo e via dicendo.
  • data_type : il tipo di dato da restituire. Nel caso non si usi Forms Framework, accetta solo "Text" e "Number".
  • bReturnPlain : forza la restituzione di un valore di tipo plain nel caso si usi Forms Framework.

Volendo fare un esempio del suo utilizzo, supponiamo ad esempio di voler stampare il contenuto di tutti i campi "contenuto" della tabella "messaggi":

$db->query("SELECT contenuto FROM messaggi");
if ($db->numRows())
{
	for ($i = 0; $i < $db->numRows(); $i++)
	{
		echo $db->getResult($i, "contenuto") . "<br />";
	}
}
else
{
	echo "No Results.<br />";
}

Il funzionamento di getField(), invece, implica l'utilizzo delle funzioni di spostamento fra record. Pertanto, il suo utilizzo sarà descritto nel prossimo capitolo.

[modifica] Metodi di spostamento fra i record

Come anticipato nella Sinossi, la classe permette di programmare il codice di accesso ai risultati usando un metodo a cursore, invece che ad indice. Per cursore s'intende l'interrogazione del recordset sulla base del concetto di "record corrente". Per posizionarsi su un record specifico è possibile usare tre metodi differenti.

Il più importante ed utilizzato è nextRecord(). Questo metodo permette di posizionarsi al record successivo. Nel caso di una query appena eseguita, permette di posizionarsi sul primo record disponibile. In caso d'insuccesso nextRecord() restituisce FALSE. Pertanto, tramite nextRecord è possibile tanto determinare se sono presenti record, quanto capire se si è raggiunta la fine del recordset. Ad esempio, volendo riscrivere l'esempio del capitolo precedente usando nextRecord(), scriveremo:

$db->query("SELECT contenuto FROM messaggi");
if ($db->nextRecord())
{
	do
	{
		[...]
	} while ($db->nextRecord());
}
else
{
	echo "No Results.<br />";
}

La pulizia di un codice del genere, rispetto all'approccio a for (genericamente obsoleto, nel paradigma ad oggetti), dovrebbe apparire evidente. Una volta posizionati su un record specifico, è possibile recuperare il valore di un campo utilizzando il metodo getField(). I parametri sono i seguenti:

getField($Name, $data_type = "Text", $bReturnPlain = FALSE)

in dettaglio:

  • Name : il nome del campo di cui si desidera il valore.
  • data_type : il tipo di dato da restituire. Nel caso non si usi Forms Framework, accetta solo "Text" e "Number".
  • bReturnPlain : forza la restituzione di un valore di tipo plain nel caso si usi Forms Framework.

Volendo quindi completare l'esempio precedente, scriveremo:

$db->query("SELECT contenuto FROM messaggi");
if ($db->nextRecord())
{
	do
	{
		echo $db->getField("contenuto") . "<br />";
	} while ($db->nextRecord());
}
else
{
	echo "No Results.<br />";
}

La classe permette di spostarsi non solo semplicemente al record successivo, ma anche a record abitrari. A tal scopo, è possibile utilizzare il metodo seek() ed il metodo jumpToPage().

Il metodo seek(), come spiegato precedentemente, permette di posizionarsi ad un record specifico passando come parametro l'offset. Ad esempio, volendo posizionarsi al primo record digiteremo:

$db->seek(0);

Il metodo jumpToPage() invece, leggermente più complesso, permette di posizionarsi al primo record di una pagina "ideale", previa inputazione del numero di record per pagina e del numero della pagina desiderata. Ad esempio, supponendo di voler dividere idealmente il nostro risultato in insiemi di 30 record per pagina e di volersi posizionare al record corrispondente alla quinta pagina, digiteremo:

$db->jumpToPage(5, 30);

Una volta posizionati, sia con seek() che con jumpToPage(), sarà possibile accedere ai valori desiderati con getField(), come descritto precedentemente.

NB: per poter utilizzare getField(), è indispensabile effettuare prima un posizionamento, qualsiasi sia il metodo utilizzato.

[modifica] Escaping dei valori

Come accennato nella parte relativa al metodo execute(), la classe mette a disposizione un metodo per l'escaping dei valori, in modo da evitare qualsiasi possibilità di subire un attacco di SQL injection. Fra le altre cose, questo metodo è integrato con ffData, permettendo nel caso lo si utilizzi una conversione automatica dei tipi e dei locale. Il metodo in questione è toSql(), ed accetta i seguenti parametri:

toSql($cDataValue, $data_type = NULL, $enclose_field = TRUE, $transform_null = TRUE)

in dettaglio:

  • cDataValue : il valore di cui effettuare l'escaping. Può essere un oggetto ffData.
  • data_type : il tipo di dato. Nel caso non si usi Forms Framework, accetta solo "Text" e "Number". In tal caso, "Text" sarà l'unico tipo soggetto ad escaping.
  • enclose_field : aggiunge i trailing quotes al valore.
  • transform_null : impedisce che un valore di tipo NULL arrivi al database, sostituendolo opportunamente.

Pertanto, supponendo di voler eseguire un INSERT con valori prelevati da un normale form HTML, scriveremo:

$sSQL = "
	INSERT INTO
		messaggi (
			contenuto
		) VALUES (
			" . $db->toSql($_REQUEST["contenuto"]) . "
		)
	";
$db->execute($sSQL);

Ovviamente, è necessario prestare attenzione ed utilizzarlo SEMPRE per avere il giusto livello di sicurezza. La classe mette a disposizione il metodo, sta al programmatore utilizzarlo.

[modifica] Gestione degli errori e debugging

Ad ogni operazione effettuata, la classe eseguirà automaticamente un controllo degli errori, comportandosi in modo diverso a seconda delle impostazioni. I settaggi utili a questo scopo sono i seguenti:

  • halt_on_connect_error : normalmente abilitata, blocca l'esecuzione nel caso in cui la classe non dovesse riuscire ad effettuare la connessione al DB.
  • on_error : influenza il comportamento della classe in caso di errore. Può assumere i seguenti valori:
    • halt : blocca l'esecuzione e visualizza un messaggio (impostazione di default).
    • report : visualizza un messaggio ma non blocca l'esecuzione.
    • ignore : non visualizza nessun tipo di output.
  • HTML_reporting : visualizza i messaggi usando testo formattato in HTML

Oltre a questi settaggi, è presente un ulteriore impostazione che determina la visualizzazione di messaggi di debug ogni qual volta viene eseguita un operazione su DB. Il settaggio è, molto significativamente:

  • debug : normalmente impostato su FALSE, visualizza una quantità di messaggi di debug.

Fra le altre cose, debug attiva automaticamente la visualizzazione degli errori.

[modifica] Funzioni Varie

Fra le altre cose, all'interno della classe sono presenti due comode funzioni, mysqlPassword() e mysqlOldPassword(), le quali rispettivamente permettono di convertire una stringa nel formato delle funzioni PASSWORD() ed OLD_PASSWORD() di MySQL.

[modifica] Conclusioni

Abbiamo analizzato questa semplice ma utile classe per l'accesso ai Database di tipo SQL/MySQL. Tramite di essa è possibile accedere ai database usando l'efficiente paradigma a classi, con una serie di vantaggi che dovrebbero essere risultati evidenti dalla lettura. Per quanto semplice e sicuramente passibile di miglioramenti ed espansione, sono convinto che la sua potenza come strumento possa giustificarne l'adozione al posto della semplice API di PHP e magari fornire lo spunto per ulteriori approfondimenti, dell'argomento o del framework da cui nasce.

[modifica] Articoli Correlati

PHP HOWTO - Forms Framework: ffTemplate

[modifica] Credits

Tutto il codice presente nell'articolo e nei file collegati è coperto dal licenza GPL v3.

L'autore dell'articolo, di Forms, il framework php e di tutti i file correlati al progetto è Samuele "Nuitari" Diella, mailto: samuele.diella@gmail.com

[modifica] Ringraziamenti

Conraid, per la simpatia ed il costante supporto/bug-tracking

La comunità di Slacky.eu, la migliore del mondo

Strumenti personali
Namespace

Varianti