Corso PHP MySQL
Lezioni | di Gianni Tomasicchio
1.
Setup del server e creazione del database
Corso PHP MySQL: creazione del database di prova
2.
Connessione a MySQL e selezione del DB
Connessione con PHP al server MySQL e selezione del database.
3.
Script di connessione
Script PHP di connessione a MySQL
4.
Inserimento dei dati
Inserire dati in una tabella MySQL con PHP
5.
Selezione dei dati e recupero dei risultati -Leggere i dati di una tabella MySQL
con PHP. Eseguire una SELECT con PHP e MySQL.
Script PHP per la lettura dei dati di una tabella MySQL
6.
Cancellazione dei dati
Cancellare un record di una tabella MySQL con PHP. Eseguire una DELETE con PHP e MySQL.
7.
Aggiornamento dei dati
Modificare i dati di una tabella MySQL con PHP. Eseguide una UPDATE con PHP e MySQL
8.
Debug degli script PHP/MySQL
Risolvere gli errori di uno script PHP MySQL. Debug degli script PHP/MySQL
Setup del server e creazione del database
Per far girare script PHP che si interfacciano ad un database MySQL è necessario avere a disposizione un
server web con il supporto al PHP ed un server MySQL, presenti generalmente, ma non necessariamente,
sullo stesso PC. Il PHP deve inoltre caricare l'estensione mysql, ovvero la libreria di funzioni che
permettono di interagire con MySQL. Questa estensione è attiva di default solo nelle versioni Windows di
PHP 4. Per una descrizione dettagliata della procedura di installazione e configurazione di Apache 2, PHP
5 e MySQL 4.1 su Window XP è possibile consultare questo articolo che mostra anche come verificare il
corretto funzionamento dell'intero sistema.
Vivamente consigliata anche l'installazione di un client MySQL. Installando MySQL avremo a
disposizione il MySQL Command Line Client, un semplice programma a riga di comando (in stile DOS) per
inviare comandi ed eseguire query. Se si desidera qualcosa di meno spartano e con più funzionalità è
possibile utilizzare un client MySQL con interfaccia grafica, come ad esempio MySQL Query browser o
phpMyAdmin.
Prima di iniziare a realizzare i nostri script PHP/MySQL dobbiamo preparare un database nel quale salvare
i nostri dati. Gli esempi proposti nelle prossime lezioni fanno riferimento ad un database di nome prova
contenente un'unica tabella di nome utenti. Per creare il database e la tabella in questione basta lanciare
le seguenti query:
create database prova;
use prova;
CREATE TABLE utenti (
id int(11) NOT NULL auto_increment,
nome varchar(128) NOT NULL default '',
email varchar(128) NOT NULL default '',
sesso tinyint(4) NOT NULL default '0',
newsletter tinyint(4) NOT NULL default '0',
attivita tinyint(4) NOT NULL default '0',
messaggio text NOT NULL,
PRIMARY KEY (id));
La tabella utenti memorizza un ipotetico profilo di un utente che fa richiesta di un servizio offerto da un
sito web. Essa contiene il nome dell'utente, il suo indirizzo email, il sesso (1= maschio, 2=femmina), se
accetta di registrarsi alla newsletter (0=no, 1=si), l'attività svolta (0=non dichiarata,1=studente,
2=impiegato, 3=disoccupato) ed infine un messaggio inviato alla redazione del sito. Si noti anche che
per la chiave primaria viene impiegato un campo id di tipo intero auto_increment. Questa tabella non ha
alcun valore pratico ma la sua struttura ci permetterà di esplorare le tecniche necessarie a gestire le
differenti tipologie di informazione.
Effettuato il setup del nostro server e creato il database di prova possiamo procedere con la prossima
lezione nella quale impareremo a connetterci a MySQL con PHP
Connessione a MySQL e selezione del DB
Vediamo ora come realizzare un semplice script PHP in grado di connettersi ad un server MySQL. La
connessione avviene grazie ala funzione mysql_connect(). I dati necessari alla connessione sono:

indirizzo del server: bisogna specificare su quale computer è in esecuzione il server MySQL.
Nella maggior parte dei casi MySQL gira sulla stessa macchina in cui è in funzione il server web (e
quindi il PHP) pertanto l'indirizzo da specificare è "localhost". Se invece il server si trova su un diverso
computer, raggiungibile attraverso una connessione TCP/IP, allora bisogna specificare il nome di
dominio (es. sql.mioserver.it) o l'indirizzo IP (es. 162.54.23.12) della macchina che lo ospita.

nome utente e password: si tratta delle credenziali che permettono al server MySQL di
identificare l'utente che effettua la richiesta di connessione. Se abbiamo appena installato MySQL
allora possiamo usare il nome utente "root" e per password quella specificata durante l'installazione.
La funzione mysql_connect() restituisce come risultato un numero che costituisce l'identificativo di
connessione MySQL ("MySQL link identifier" oppure " MySQL-Link resource") in caso di successo oppure
FALSE in caso contrario. E' quindi possibile verificare l'esito della connessione semplicemente controllando
il risultato restituito da mysql_connect().
Se qualcosa va storto possiamo inoltre ottenere maggiori informazioni sull'accaduto utilizzando un'altra
funzione, mysql_error(), che ci mostrerà un messaggio esplicativo sul problema verificatosi.
Effettuata la connessione a MySQL si è soliti specificare il nome del database su cui si vogliono effettuare
le query. Infatti uno script PHP generalmente effettua diverse query ma tutte su tebelle appartenenti ad
uno stesso database. Per dichiarare il nome del database si usa la funzione mysql_select_db() che
restituisce TRUE o FALSE a seconda dell'esito dell'operazione. Anche questa volta conviene subito
controllare tale risultato ed eventualmente mostrare un messaggio di errore con la funzione
mysql_error().
Anche se tutte le connessioni ai server MySQL vengono automaticamente chiuse alla fine dell'esecuzione
dello script, è buona norma chiudere la connessione esplicitamente, appena lo script ha terminato le
operazioni con il database. Per farlo basta richiamare la funzione mysql_close() a cui passiamo come
parametro l'identificativo di connessione ottenuto da mysql_connect().
Vediamo quindi un semplice esempio di connessione, selezione del DB e chiusura del link:
<?php
$link = mysql_connect('localhost', 'nome_utente', 'password');
if (!$link) {
die ('Non riesco a connettermi: ' . mysql_error());
}
$db_selected = mysql_select_db('prova', $link);
if (!$db_selected) {
die ("Errore nella selezione del database: " . mysql_error());
}
echo 'connesso con successo';
mysql_close($link);
?>
Ecco nel dettaglio il funzionamento del precedente script:

la riga 2 effettua la connessione al server attraverso la funzione mysql_connect() a cui viene
passato l'indirizzo del server ('localhost'), il nome dell'utente di MySQL ('nome_utente') e la relativa
password ('password');

la riga 3 controlla il valore di $link restituito da mysql_connect(). Se vale FALSE la riga 4 mostra
un messaggio di errore grazie alla funzione mysql_error() e termina lo script;

la riga 7 seleziona il database da utilizzare, richiamando la funzione mysql_select_db() a cui
passiamo il nome del database 'prova' ed il $link che identifica la connessione appena effettuata.

la riga 8 controlla il valore di $db_selected restituito da mysql_select_db(). Se vale FALSE la riga
9 mostra un messaggio di errore grazie alla funzione mysql_error() e termina lo script;

la riga 14 infine chiude la connessione al server MySQL attraverso la funzione mysql_close().
La connessione va eseguita una sola volta per script, generalmente all'inizio del codice, poiché è
necessario che questa preceda l'esecuzione delle query. E' possibile effettuare più connessioni all'interno
di uno stesso script ma questo ha senso solo se ci si deve interfacciare con diversi server MySQL,
circostanza piuttosto rara.
Quella vista è la procedura di connessione comunemente impiegata negli script PHP/MySQL. Si possono
verificare però condizioni e circostanze che richiedono un uso differente della funzione mysql_connect(),
mostrate di seguito attraverso degli esempi:

per collegarsi al server MySQL presente sull'host miodominio.it (diverso da localhost) usando una
porta differente da quella standard (3306), ad esempio la 3307:
$link = mysql_connect('miodominio.it:3307', 'nome_utente', 'password');

per collegarsi al server MySQL presente il localhost usando una porta differente da quella
standard (3306), ad esempio la 3307:
$link = mysql_connect('127.0.0.1:3307', 'nome_utente', 'password');
Si noti che non è stato usato come host 'localhost:3307' perché PHP avrebbe creato una connessione
attraverso socket (named pipe su Windows) invece di usare il TCP/IP e quindi avrebbe ignorato il
numero di porta.

per collegarsi a localhost specificando il percorso del socket:
$link = mysql_connect('/tmp/mysql', 'nome_utente', 'password');
oppure:
$link = mysql_connect('localhost:/tmp/mysql.sock', 'nome_utente', 'password')
;
Accanto all'indicazione del server, del nome utente e della password, la funzione mysql_connect() accetta
due ulteriori parametri:
new_link: se in uno script viene chiamata la funzione mysql_connect() due o più volte con gli stessi
parametri, questa restituirà sempre lo stesso identificativo di connessione. Questo perché
mysql_connect() evita di aprire nuovi (inutili) link ad uno stesso server MySQL. Se si vuole disabilitare
questo comportamento basta porre il parametro new_link a TRUE.
client_flags: una combinazione di flag (MYSQL_CLIENT_IGNORE_SPACE,
MYSQL_CLIENT_IGNORE_SPACE, MYSQL_CLIENT_INTERACTIVE, MYSQL_CLIENT_SSL).
Per i dettagli si rimanda alla documentazione ufficiale.
Connessioni persistenti
La procedura di connessione vista comporta la creazione di un nuovo link tra PHP e MySQL ad ogni
esecuzione dello script. Questo collegamento viene interrotto con la funzione mysql_close() o
automaticamente al termine dello script. Alla semplicità di questo approccio si contrappone un'evidente
inefficienza: la procedura di connessione, ripetuta ad ogni esecuzione dello script, potrebbe risultare
onerosa in termini di prestazioni, soprattutto se PHP e MySQL sono su macchine differenti.
Per ovviare a questo problema è possibile utilizzare le connessioni persistenti. In pratica, attraverso la
funzione mysql_pconnect() che segue la stessa sintassi della mysql_connect(), si cerca di riciclare
vecchie connessioni precedentemente aperte e ancora attive. Il funzionamento è il seguente:

per prima cosa la funzione mysql_pconnect() cerca un link persistente ancora attivo, aperto da
una precedente chiamata a mysql_pconnect() effettuata impiegato gli stessi dati per host, username
e password;

se tale link viene trovato allora mysql_pconnect() non apre una nuova connessione ma restituisce
l'identificativo di connessione del vecchio collegamento, ovvero riutilizza la vecchia connessione;

se non è disponibile un link persistente ancora attivo allora se ne crea uno nuovo;

infine, al termine dello script, il link non viene chiuso, neanche se viene invocata la funzione
mysql_close().
Come accennato, le connessioni persistenti hanno come unico obbiettivo la riduzione dell'overhead legato
alla fase di creazione dei link tra PHP e MySQL. Purtroppo però l'uso delle connessioni persistenti richiede
una certa abilità nel configurare il server. Se infatti MySQL è configurato per non aprire più di un certo
numero di connessioni contemporaneamente, la richiesta di una ulteriore connessione da parte di PHP
non verrà soddisfatta con conseguenze ovvie sul suo funzionamento dello script.
Per questo, e per altri inconvenienti che si potrebbero verificare, si rimanda alla documentazione di PHP
(in particolare a questa pagina) e MySQL.
Script di connessione
Le operazioni di connessione e selezione del database sono così comuni all'interno degli script PHP che
spesso si preferisce inserirle in un file indipendente, da richiamare negli script che ne hanno bisogno. Il
codice è sempre lo stesso, pertanto conviene scriverlo una volta sola, salvarlo in un file ed includerlo
dove necessario.
connect.php
<?php
$link = mysql_connect('localhost', 'nome_utente', 'password');
if (!$link) {
die ('Non riesco a connettermi: ' . mysql_error());
}
$db_selected = mysql_select_db('prova', $link);
if (!$db_selected) {
die ("Errore nella selezione del database: " . mysql_error());
}
?>
Ecco come potrebbe presentarsi il generico script che fa uso del precedente codice di connessione:
<?php
// richiamo lo script responsabile della connessione a MySQL
require 'connect.php';
// il resto dello script: posso effettuare query
// senza preoccuparmi della connessione a MySQL
?>
Si noti però che il riutilizzo dello scipt connect.php in altri contesti (altri programmi PHP/MySQL) non è
ancora immediato, poiché al suo interno sono presenti diversi dati generalmente soggetti a variazione
(host, username, password e nome del DB). Per questo motivo risulta comodo separare tali informazioni
dal codice ed inserirle in un apposito file di configurazione config.php, come mostrato di seguito:
config.php
<?php
$DB_host
= 'localhost';
$DB_user
= 'nome_utente';
$DB_password = 'password';
$DB_name
= 'prova';
?>
connect.php
<?php
$link = mysql_connect($DB_host, $DB_user, $DB_password);
if (!$link) {
die ('Non riesco a connettermi: ' . mysql_error());
}
$db_selected = mysql_select_db($DB_name, $link);
if (!$db_selected) {
die ("Errore nella selezione del database: " . mysql_error());
}
?>
Il generico script pertanto diventa:
<?php
// richiamo il file di configurazione
require 'config.php';
// richiamo lo script responsabile della connessione a MySQL
require 'connect.php';
// il resto dello script: posso effettuare query
// senza preoccuparmi della connessione a MySQL
?>
Al di là degli esempi mostrati in questa lezione ciò che conta è rendersi conto che bisogna fin da subito
organizzare lo script affinché risulti snello, di facile manutenzione e, almeno in parte, riutilizzabile. Come
vedremo, le applicazioni PHP/MySQL tendono con estrema facilità a "complicarsi", sopratutto quando si
deve far interagire l'utente con i dati presenti nel database. Gli script che realizzeremo nelle prossime
lezioni faranno uso dei file config.php e connect.php qui presentati.
Inserimento dei dati
Adesso che sappiamo connetterci al server MySQL vediamo come effettuare le query. Cominciamo con un
inserimento dati, che segue la sintassi:
INSERT INTO
nome_tabella
SET
nome_campo_1 = valore_1,
nome_campo_2 = valore_2
oppure:
INSERT INTO nome_tabella
(nome_campo_1, nome_campo_2)
VALUES
(valore_1, valore_2)
In generale per lanciare una query in uno script PHP dobbiamo semplicemente chiamare la funzione
mysql_query() a cui passare come parametro la stringa contenete la query. Nel caso di un inserimento
mysql_query() restituisce TRUE o FALSE, a seconda dell'esito dell'operazione. Ci basta quindi controllare
tale valore per verificare se i dati sono stati correttamente immessi nella tabella. In caso negativo con la
funzione mysql_error() possiamo ottenere maggiori indicazioni su cosa è andato storto.
Il seguente esempio fa riferimento al database ed alla tabella presentati nella Lezione 2. La connessione
avviene grazie agli script config.php e connect.php presentati nella Lezione 4.
<?php
// richiamo il file di configurazione
require 'config.php';
// richiamo lo script responsabile della connessione a MySQL
require 'connect.php';
// preparo la query
$query = "INSERT INTO utenti
(nome,email,sesso,newsletter,attivita,messaggio)
VALUES ('Mario Rossi','[email protected]',1,1,2,'inviatemi
del materiale illustrativo')";
// lancio la query
$result = mysql_query($query);
// controllo l'esito
if (!$result) {
die("Errore nella query $query: " . mysql_error());
}
// chiudo la connessione a MySQL
mysql_close();
echo 'Query eseguita correttamente';
?>
E' importante osservare che si è preferito non passare direttamente alla funzione mysql_query() la
stringa contenente la query. Tale stringa è stata memorizzata nella variabile $query perché potrebbe
risultare utile mostrarla in caso di errore, soprattutto se è stata costruita dinamicamente in funzione di
dati provenienti dall'utente (caso mostrato più avanti).
Se avessimo effettuato due o più connessioni a diversi server MySQL, per specificare a quale di questi
stiamo inviando la query avremmo dovuto passare alla funzione mysql_query() come secondo parametro
anche l'identificativo di connessione ottenuto durante la procedura di connessione al server.
Vediamo ora come eseguire la query di inserimento nel caso in cui i dati provengano da un form
compilato da un utente. L'interazione con l'utente complica l'esecuzione della query poiché questa volta
non sappiamo a priori cosa verrà inviato con il form. Sono necessarie diverse precauzioni quindi e nel
seguente esempio ci concentreremo su quelle che assicurano la creazione di una query corretta,
tralasciando altri controlli di validità dei dati non meno importanti ma al di la degli scopi di questo corso.
Lo script che segue si occupa di presentare all'utente un form composto da diversi campi (nome, email,
sesso, newsletter, attivita, messaggio) appartenenti a differenti tipologie di controlli (textbox, radio
button, checkbox, lista, textarea). Quando l'utente invia il form, lo script estrae dall'array $_POST le
informazioni necessarie a construire la query di inserimento.
Uno dei principali problemi di questo processo è costituito dall'inserimento all'interno della query di
stringhe arbitrarie inviate dall'utente. Sappiamo infatti che in una query le stringhe vanno racchiuse da
apici:
INSERT INTO elenco_film SET titolo = 'Al bar dello sport'
ma cosa succede se la stringa contiene a sua volta un apice (apostrofo)?
INSERT INTO elenco_film SET titolo = 'L'allenatore nel pallone'
E' evidente che questa query non è corretta, infatti non è possibile distinguere l'apostrofo del titolo del
film dall'apice che delimita la fine della stringa. Per evitare l'ambiguità la query va corretta nel seguente
modo, inserendo il carattere \ (backslash) prima dell'apostrofo:
INSERT INTO elenco_film SET titolo = 'L\'allenatore nel pallone'
Il PHP possiede una funzionalità chiamata magic quotes GPC che consiste nell'eseguire
automaticamente l'escape, ovvero l'inserimento di un backslash, appena prima di un ' (apice singolo), "
(apice doppio), \ (backslash) e NUL per tutti i dati provenienti da GPC (Get, Post e Cookie), ma solo se
nel php.ini è abilitata la direttiva magic_quotes_gpc. Sembrerebbe quindi che il problema sia
immediatamente risolvibile abilitando tale direttiva.
In realtà la questione è più complessa, infatti ci sono altri caratteri che potrebbero creare dei problemi se
inseriti in una query ed inoltre questi dipendono anche dal set di caratteri impiegato nella connessione a
MySQL. L'approccio più corretto quindi è disabilitare la funzionalità "magic quotes GPC" ed affidarsi ad
una particolare funzione, mysql_real_escape_string(), che effettua il corretto escape di tutti i caratteri
potenzialmente pericolosi, tenendo in conto il particolare set di caratteri impiegato nella connessione tra
PHP e MySQL (alcuni programmatori utilizzano la funzione addslash() al posto di
mysql_real_escape_string(), approccio non del tutto errato ma sicuramente meno specifico e sicuro per
un database MySQL).
Se non è possibile disabilitare il "magic quotes GPC", oppure se vogliamo realizzare uno script
indipendente da tale funzionalità, è possibile procedere in questo modo. Dapprima controlliamo,
interrogando la funzione get_magic_quotes_gpg(), se il magic quote è attivato. In caso affermativo
rimuoviamo i backslash inseriti dal PHP con la funzione stripslashes(). Procediamo poi con
mysql_real_escape_string() per effettuare un corretto escape di tutte le stringhe da inserire nella query.
Ciò che stiamo facendo è proteggere il nostro codice da uno dei più pericolosi problemi di sicurezza:
l'SQL Injection.
Vediamo lo script completo:
<?php
if($_POST) {
inserisci_record();
}
else {
mostra_form();
}
function inserisci_record()
{
// richiamo il file di configurazione
require 'config.php';
// richiamo lo script responsabile della connessione a MySQL
require 'connect.php';
// recupero i campi di tipo "stringa"
$nome
= trim($_POST['nome']);
$email
= trim($_POST['email']);
$messaggio = trim($_POST['messaggio']);
// verifico se devo eliminare gli slash inseriti automaticamente
da PHP
if(get_magic_quotes_gpc())
{
$nome
= stripslashes($nome);
$email
= stripslashes($email);
$messaggio = stripslashes($messaggio);
}
$nome
= mysql_real_escape_string($nome);
$email
= mysql_real_escape_string($email);
$messaggio = mysql_real_escape_string($messaggio);
// recupero gli altri campi del form
$sesso
= isset($_POST['sesso']) ? intval($_POST['sesso']) :
0;
$newsletter = isset($_POST['newsletter']) ? 1 : 0;
$attivita
= intval($_POST['attivita']);
// verifico la presenza dei campi obbligatori
if(!$nome)
{
$messaggio = urlencode("Non hai inserito il nome");
header('location:
'.$_SERVER['PHP_SELF'].'?msg='.$messaggio);
exit;
}
// preparo la query
$query = "INSERT INTO utenti
(nome,email,sesso,newsletter,attivita,messaggio)
VALUES
('$nome','$email',$sesso,$newsletter,$attivita,'$messaggio')";
// invio la query
$result = mysql_query($query);
// controllo l'esito
if (!$result) {
die("Errore nella query $query: " . mysql_error());
}
// recupero l'id autoincrement generato da MySQL per il
nuovorecord inserito
$id_inserito = mysql_insert_id();
// chiudo la connessione a MySQL
mysql_close();
$messaggio = urlencode("Inserimento effettuato con successo
(ID=$id_inserito)");
header('location: '.$_SERVER['PHP_SELF'].'?msg='.$messaggio);
}
function mostra_form()
{
// mostro un eventuale messaggio
if(isset($_GET['msg']))
echo '<b>'.htmlentities($_GET['msg']).'</b><br /><br />';
?>
<form name="form_registrazione" method="post" action="">
<label>nome:
<input name="nome" type="text" />
</label>
(obbligatorio)
<p>
<label>email:
<input name="email" type="text" />
</label>
</p>
<p> Sesso:
<label>
<input type="radio" name="sesso" value="1" />
M</label>
<label>
<input type="radio" name="sesso" value="2" />
F</label>
</p>
<p>
<label>inviami newletter:
<input name="newsletter" type="checkbox" value="1" />
</label>
</p>
<p>
<label>attività:
<select name="attivita">
<option value="0">:: seleziona ::</option>
<option value="1">studente</option>
<option value="2">lavoratore</option>
<option value="3">disoccupato</option>
</select>
</label>
</p>
<p>
<label>messaggio:<br />
<textarea name="messaggio" cols="40" rows="5"></textarea>
</label>
</p>
<p>
<input name="invia" type="submit" value="Invia" />
</p>
</form>
<?php
}
?>
Tutto lo script si basa su due funzioni, mostra_form() per mostrare il form all'utente e inserisci_record()
per prelevare i dati inviati via POST, preparare ed eseguire la query di inserimento.
La funzione mostra_form() non necessita di particolari commenti. Si osservino le righe 72-73 che
permettono di mostrare un messaggio passato via GET la cui utilità verrà spiegata in seguito.
La funzione inserisci_record() invece presenta diversi aspetti su cui vale la pena soffermarsi:

E' buona norma eliminare eventuali spazi vuoti presenti all'estremità delle stringhe inviate
dall'utente. Questi spazi, oltre a costituire uno spreco di risorse del database, possono
compromettere il corretto funzionamento dei controlli sulla stringa stessa. Si pensi ad esempio alla
verifica della lunghezza della stringa. Per eliminare questi spazio basta usare la funzione trim() (righe
18-20).

Come ampiamente discusso in precedenza, è necessario effettuare l'escape dei caratteri
potenzialmente "pericolosi" presenti nelle stringhe inviate dall'utente. Per prima cosa controlliamo se
il PHP (probabilmente in maniera non adeguata) ha già effettuato tale operazione interrogando
get_magic_quotes_gpc() (riga 23). In caso affermativo dobbiamo eliminare i backslash inseriti con
stripslashes() (righe 25-27). Procediamo poi al corretto escape delle stringhe con la funzione
mysql_real_escape_string() (righe 30-32).

Particolare attenzione bisogna prestare al trattamento dei dati provenienti dalle checkbox e dai
radio button. Se una checkbox viene selezionata dall'utente allora il suo valore sarà presente
nell'array $_POST, altrimenti tale valore sarà assente. Pertanto usiamo la funzione isset() per
effettuare tale verifica. Anche un set di radio buttons potrebbe essere privo della selezione
dell'utente, per cui usiamo anche in questo caso la funzione isset().

Se un campo della tabella ospita un intero (come i campi "sesso" e "attivita") allora è meglio
assicurarci che la query contenga un valore intero da inserire. Per farlo utilizziamo la funzione
intval(). Anche questo accorgimento serve a proteggere lo script da una SQL Injection.

Quando si inserisce un record in una tabella è necessario assicurarsi che almeno un campo sia
stato riempito. Nell'esemio riportato si è scelto di verificare che il campo "nome" non venga lasciato
vuoto. Questo controllo sarebbe stato inefficace se non avessimo usato precedentemente la funzione
trim().

E' importante preparare la query e conservarla in una variabile ($query) invece di passarla
direttamente alla funzione mysql_query(). In caso di errore è molto utile visualizzare la query
costruita con i dati dell'utente per cercare al suo interno eventuali errori causati dalla sua
realizzazione dinamica.

Una tabella contenente un campo auto_increment genera automaticamente un numero intero ad
ogni inserimento di un nuovo record. Questo numero spesso rappresenta l'identificativo (ID) del
record inserito e pertanto potrebbe essere utile recuperarlo dopo aver eseguito la query. La funzione
mysql_insert_id() serve proprio ad ottenere questo numero.
Uno dei problemi più comuni in questo tipo di script è la gestione del refresh della pagina. Se l'utente,
dopo aver inviato il form, ricarica la pagina (pulsante "aggiorna" del browser) è possibile che lo script di
inserimento venga mandato nuovamente in esecuzione e che quindi la query di inserimento venga
ripetuta. Una soluzione è quella di reindirizzare il browser verso un URL qualsiasi, anche quello dello
stesso script di inserimento, in modo da perdere i dati inviati dall'utente (POST). Pertanto, eseguita
l'interazione col database, lo script termina chiamando la funzione header() che conduce il browser
all'indirizzo specificato. Conviene comunque mostrare un messaggio all'utente, per informarlo dell'esito
dell'operazione. Per farlo possiamo accodare all'URL utilizzato per il redirect il messaggio da mostrare
all'utente. Siccome l'URL non può contenere qualsiasi carattere allora prepariamo il messaggio
convertendo gli eventuali caratteri illeciti con la funzione urlencode(). Ecco spiegato anche l'utilità della
riga 73 che provvede a visualizzare il contenuto di $_GET['msg'].
Selezione dei dati e recupero dei risultati
Adesso che sappiamo inserire dei dati in una tabella MySQL vediamo come poterli recuperare e mostrare
all'utente. Si tratta ancora una volta di eseguire una query, in particolare bisogna lanciare una SELECT,
ma ora dobbiamo occuparci anche di un altro compito, ovvero il recupero del set di risultati (result
set) ottenuto.
Come visto in precedenza la funzione mysql_query() ci permette di inviare una query al server MySQL.
Se ad esempio inviando una query del tipo:
SELECT id, nome FROM utenti
chiediamo al server i dati presenti nei campi "id" e "nome" della tabella "utenti". Ma dove vengono inviati
questi dati? Il comportamento della funzione mysql_query() prevede che tutti i record ottenuti vengano
inviati immediatamente da MySQL a PHP. Quest'ultimo li conserva nella sua memoria (in un "buffer", da
cui il nome buffered query) ma non li rende direttamente disponibili allo script. In pratica mysql_query()
non restituisce i record estratti dal database, ma ci consegna una semplice "ricevuta" che prende il nome
di "MySQL result resource". Per prelevare i dati è necessaria un'ulteriore procedura detta fetch. Grazie
alla "ricevuta" fornitaci da mysql_query() possiamo chiamare una delle funzioni di fetch e ottenere
finalmente, un record alla volta, i dati appartenenti al set di risultati.
Ci sono diverse funzioni di fetch disponibili, che si distinguono per la modalità con cui restituiscono il
record estratto:

mysql_fetch_row() - restituisce un record del set di risultati all'interno di un array con indici
numerici;

mysql_fetch_assoc() - restituisce un record del set di risultati all'interno di un array
associativo;

mysql_fetch_object() - restituisce un record del set di risultati all'interno di un oggetto con
proprietà che corrispondono ai campi del record.
Disponibile inoltre la funzione mysql_fetch_array() che cambia il suo comportamento in funzione del
secondo parametro passatogli:

mysql_fetch_array($ricevuta, MYSQL_ASSOC)
restituisce un record del set di risultati all'interno di un array associativo. Equivalente a
mysql_fetch_assoc()

mysql_fetch_array($ricevuta, MYSQL_NUM)
restituisce un record del set di risultati all'interno di un array con indici numerici - Equivalente a
mysql_fetch_row()

mysql_fetch_array($ricevuta, MYSQL_BOTH)
restituisce un record del set di risultati all'interno di un array con indici numerici e associativi Comportamento di default in caso di assenza del secondo parametro.
Se si vogliono recuperare tutti i record restituiti da una query le funzioni di fetch devono essere
richiamata tante volte quanti sono i record. Queste funzioni infatti restituiscono, in formati diversi, solo
un record e non tutto il set di risultati. Ad ogni nuova esecuzione, queste funzioni estraggono il record
successivo, fino all'ultimo. Raggiunto l'ultimo record un'ulteriore esecuzione di queste funzioni restituisce
il valore FALSE, segnalando quindi che non ci sono più dati da prelevare.
Estratti tutti i risultati non bisogna dimenticare che PHP sta conservando ancora nella sua memoria tutti i
record restituiti dalla query. E' quindi possibile (e consigliato) liberare tale memoria, attraverso la
funzione mysql_free_result().
Tenendo presente questo comportamento delle istruzioni di fetch è facile realizzare un ciclo WHILE che ne
iteri l'esecuzione fino all'estrazione completa dei record. I seguenti esempi mostrano proprio questa
tecnica, applicata alle diverse funzioni di fetch.
- mysql_fetch_row
$ricevuta = mysql_query("SELECT id, nome FROM utenti ");
while ($row = mysql_fetch_row($ricevuta)) {
echo 'ID: ', $row[0] , ' Nome: ', $row[1] , "\n";
}
- mysql_fetch_assoc
$ricevuta = mysql_query("SELECT id, nome FROM utenti ");
while ($row = mysql_fetch_assoc($ricevuta)) {
echo 'ID: ', $row['id'] , ' Nome: ', $row['nome'] , "\n";
}
- mysql_fetch_object
$ricevuta = mysql_query("SELECT id, nome FROM utenti ");
while ($obj = mysql_fetch_object($ricevuta)) {
echo 'ID: ', $obj->id , ' Nome: ', $obj->nome , "\n";
}
- mysql_fetch_array
$ricevuta = mysql_query("SELECT id, nome FROM utenti ");
while ($row = mysql_fetch_array($ricevuta, MYSQL_BOTH)) {
echo 'ID: ', $row[0] , ' Nome: ', $row['nome'] , "\n";
}
Le istruzioni di fetch mostrate estraggono un record dal set dei risultati. Non è possibile specificare quale
record estrarre poiché questi sono restituiti in sequenza, dal primo all'ultimo. PHP infatti tiene traccia
della posizione corrente all'interno del set di risultati attraverso un puntatore, ovvero un indice che,
supponendo che la query abbia prelevato dal database N record, va da 0 (il primo) a N-1 (l'ultimo).
Effettuata una query questo puntatore è inizializzato da PHP a 0 e viene incrementato di una unità ad
ogni esecuzione di una funzione di fetch. Questo spiega il funzionamento dei cicli WHILE degli esempi
precedenti.
Normalmente non è necessario agire su questo puntatore, poiché quasi sempre siamo interessati ad
estrarre tutti i record provenienti dalla query, nell'ordine con cui sono stati prodotti ed inviati da MySQL.
Se però volessimo prelevare solo alcuni record allora possiamo utilizzare la funzione
mysql_data_seek() con cui è possibile spostarsi all'interno del set di risultati. Nel seguente esempio
verrà estratto solo il primo e l'ultimo record dal set dei risultati:
$ricevuta = mysql_query("SELECT nome FROM utenti ORDER BY nome");
$row = mysql_fetch_assoc($ricevuta);
echo 'Primo nome: ', $row['nome'], "\n";
$numero_record = mysql_num_rows($ricevuta);
mysql_data_seek($ricevuta, $numero_record - 1);
$row = mysql_fetch_assoc($ricevuta);
echo 'Ultimo nome: ', $row['nome'], "\n";
Analizziamo nel dettaglio il funzionamento del precedente script:

Per prima cosa viene lanciata la query

Con la funzione mysql_fetch_assoc() otteniamo un record del set di risultati. Si tratta ovviamente
del primo record, poiché il puntatore interno al set di risultati è inizialmente a 0

Per sapere quanti record $numero_record sono stati estratti impieghiamo la funzione
mysql_num_rows()

Spostiamo il puntatore del set di risultati all'ultimo record ($numero_record - 1) con
mysql_data_seek()

Con la funzione mysql_fetch_assoc otteniamo un ulteriore record del set di risultati. Si tratta
dell'ultimo record presente.
Prima di concludere con le istruzioni di fetch è doveroso citare mysql_result(). A differenza delle altre
funzioni di fetch che prelevano un intero record dal set di risultati, mysql_result() permette di ottenere
solo un campo di un particolare record (una sola cella). Ecco un esempio del suo funzionamento:
$ricevuta = mysql_query("SELECT id, nome FROM utenti ORDER BY id");
// mostra il nome del TERZO record restituito dalla query
echo mysql_result($ricevuta, 2,'nome');
Con gli esempi della nella precedente lezione abbiamo visto come recuperare dei dati da un database
MySQL impiegando le diverse procedure di fetch dei risultati di una query. Nella maggior parte dei casi
questi dati devono poi essere mostrati all'utente, all'interno di una pagina (X)HTML, per cui è necessario
prestare attenzione ad ulteriori aspetti, non meno importanti, legati sia a questioni estetiche e di rispetto
degli standard, sia alla sicurezza dello script. In particolare è importante tener presente che:

alcuni caratteri non possono essere inseriti così come si presentano all'interno di una pagina
HTML ma è necessario prima convertirli nelle relative "enità HTML". Questa conversione risolve seri
problemi di sicurezza che si possono verificare se tali stringhe sono state precedentemente inserite
nel database da un utente.

gli "a capo" presenti in un testo non hanno alcun effettuo sulla formattazione di una pagina HTML.
Devono pertanto essere convertiti negli "a capo" dell' HTML, ovvero nei tag <br> .

in alcune circostanze, ad esempio quando si inseriscono contenuti in una tabella HTML, bisogna
sostituire le stringhe vuote con qualcosa di trasparente, ad esempio l'entità  

molte informazioni memorizzate nel DB hanno una qualche forma di codifica che va
correttamente trattata. Ad esempio il campo "attivita" della tabella "utenti" codifica con i numeri da 0
a 3 le attività "non dichiarata","studente", "impiegato", "disoccupato". All'utente andrà mostrata
l'attività, non il numero che la rappresenta.
Vediamo quindi come tradurre in codice le precedenti osservazioni, considerando uno script che mostra i
dati contenuti nella tabella "utenti" all'interno di una tabella HTML.
<?php
// richiamo il file di configurazione
require 'config.php';
// richiamo lo script responsabile della connessione a MySQL
require 'connect.php';
// preparo la query
$query = "SELECT nome,email,sesso,newsletter,attivita,messaggio FROM
utenti";
// invio la query
$result = mysql_query($query);
// controllo l'esito
if (!$result) {
die("Errore nella query $query: " . mysql_error());
}
$sesso_arr = array(' ','M','F');
$attivita_arr = array(' ','studente','lavoratore','disoccupato');
echo '
<table border="1">
<tr>
<th>Nome</th>
<th>Email</th>
<th>Sesso</th>
<th>Newsletter</th>
<th>Attività</th>
<th>Messaggio</th>
</tr>';
while ($row = mysql_fetch_assoc($result)) {
$nome
= htmlentities($row['nome']);
$email
= htmlentities($row['email']);
$messaggio = htmlentities($row['messaggio']);
$messaggio = nl2br($messaggio);
if(!$email)
$email = ' ';
if(!$messaggio) $messaggio = ' ';
$sesso = $sesso_arr[$row['sesso']];
$newsletter = $row['newsletter'] ? 'Si' : 'No';
$attivita = $attivita_arr[$row['attivita']];
echo "<tr>
<td>$nome</td>
<td>$email</td>
<td>$sesso</td>
<td>$newsletter</td>
<td>$attivita</td>
<td>$messaggio</td>
</tr>";
}
echo '</table>';
// libero la memoria di PHP occupata dai record estratti con la SELECT
mysql_free_result($result);
// chiudo la connessione a MySQL
mysql_close();
?>
Le righe 34-36 provvedono alla conversione dei caratteri speciali con le relative entità HTML attraverso
l'istruzione htmlentities(). La corretta visualizzazione degli "a capo" è invece affidata alla funzione
nl2br() (riga 37)
La decodifica delle informazioni del sesso e dell'attività avviene attraverso l'uso degli array $sesso_arr e
$attivita_arr. Per evitare la creazione di celle vuote abbiamo utilizzato l'entità   sia negli array
$sesso_arr e $attivita_arr (righe 19-20), sia come alternativa alla stringa vuota per le variabili $email e
$messaggio (righe 39-40).
Al di là delle precauzioni prese in questo script di esempio, ciò che è doveroso sottolineare è l'importanza
di una corretta valutazione del formato di destinazione dei dati prelevati dal database ((X)HTML, PDF,
email, file di testo, XML, ecc.). Lo script PHP infatti ha il compito di fare da tramite tra il database e
l'output finale pertanto non si può limitare ad estrarre i record dalla tabella ma deve effettuare
un'adeguata conversione degli stessi, operazione che se sottovalutato può condurre a seri problemi di
sicurezza.
Query buffered e unbuffered
Come accennato all'inizio della lezione, questo modo di effettuare una query è detto buffered perché è il
PHP a conservare nella sua memoria tutti i record restituiti, accessibili allo script attraverso la procedura
di fetch. E' possibile però lasciare a MySQL il compito di conservare i record e prelevarli, uno ad uno,
direttamente con le istruzioni di fetch. Questa modalità è detta unbuffered. Per effettuare una query in
modalità unbuffered basta usare la funzione mysql_unbuffered_query() al posto della classica
mysql_query().
A questo punto è lecito chiedersi il motivo della presenza di due approcci distinti per la gestione del set di
risultati, considerato che lo scopo ultimo rimane comunque l'estrazione dei dati dal database. Vediamo i
pro ed i contro delle due tecniche:

con le query buffered i dati vengono conservati da PHP che ha quindi la possibilità di poterli
manipolare più agevolmente. E' infatti possibile utilizzare mysql_data_seek() per spostarsi
liberamente all'interno dei risultati. Inoltre è possibile richiamare mysql_num_rows() per sapere
subito quante righe sono state restituite dall'ultima query. mysql_data_seek() e mysql_num_rows()
non sono disponibili per una unbuffered query.

Liberando immediatamente MySQL, le query buffered permettono al server di sbloccare subito i
dati coinvolti dalla query in modo che altri processi (altri utenti) possano accedervi. Se ad esempio
uno script legge un dato dal database, questo stesso dato non potrà essere modificato da un altro
script fino a che non si chiude il set di risultati (mysql_free_result).

Le query buffered costringono il PHP a conservare nella sua memoria tutto il set di risultati e
questo compito può risultare oneroso se vengono gestite grosse quantità di dati.

Le query buffered constringono lo script a sospendere la sua esecuzione fino a quando tutti i dati
non hanno raggiunto il PHP, quindi il primo risultato è disponibile solo quando tutti i risultati sono
estratti da MySQL, inviati e memorizzati dal PHP.

Con una query unbuffered i risultati sono accessibili al PHP esclusivamente in maniera
sequenziale ma sono disponibili non appena MySQL inizia a restituirli.

Le query unbuffered hanno un ulteriore inconveniente. Poiché l'interazione tra PHP e MySQL non
si conclude con l'invio della query ma continua per tutta la fase di fetch, per lanciare una nuova query
è necessario prima prelevare tutti i risultati della precedente oppure chiudere il set di risultati
(mysql_free_result).
E' chiaro quindi che non esiste una soluzione migliore rispetto all'altra. Anche se vengono privilegiate le
buffered query perché permettono una migliore gestione del set di risultati, in casi di alto carico
dell'applicazione e per grossi volumi di dati coinvolti, potrebbe convenire impiegare le unbuffered query.
Con gli esempi della nella precedente lezione abbiamo visto come recuperare dei dati da un database
MySQL impiegando le diverse procedure di fetch dei risultati di una query. Nella maggior parte dei casi
questi dati devono poi essere mostrati all'utente, all'interno di una pagina (X)HTML, per cui è necessario
prestare attenzione ad ulteriori aspetti, non meno importanti, legati sia a questioni estetiche e di rispetto
degli standard, sia alla sicurezza dello script. In particolare è importante tener presente che:

alcuni caratteri non possono essere inseriti così come si presentano all'interno di una pagina
HTML ma è necessario prima convertirli nelle relative "enità HTML". Questa conversione risolve seri
problemi di sicurezza che si possono verificare se tali stringhe sono state precedentemente inserite
nel database da un utente.

gli "a capo" presenti in un testo non hanno alcun effettuo sulla formattazione di una pagina HTML.
Devono pertanto essere convertiti negli "a capo" dell' HTML, ovvero nei tag <br> .

in alcune circostanze, ad esempio quando si inseriscono contenuti in una tabella HTML, bisogna
sostituire le stringhe vuote con qualcosa di trasparente, ad esempio l'entità  

molte informazioni memorizzate nel DB hanno una qualche forma di codifica che va
correttamente trattata. Ad esempio il campo "attivita" della tabella "utenti" codifica con i numeri da 0
a 3 le attività "non dichiarata","studente", "impiegato", "disoccupato". All'utente andrà mostrata
l'attività, non il numero che la rappresenta.
Vediamo quindi come tradurre in codice le precedenti osservazioni, considerando uno script che mostra i
dati contenuti nella tabella "utenti" all'interno di una tabella HTML.
<?php
// richiamo il file di configurazione
require 'config.php';
// richiamo lo script responsabile della connessione a MySQL
require 'connect.php';
// preparo la query
$query = "SELECT nome,email,sesso,newsletter,attivita,messaggio FROM
utenti";
// invio la query
$result = mysql_query($query);
// controllo l'esito
if (!$result) {
die("Errore nella query $query: " . mysql_error());
}
$sesso_arr = array(' ','M','F');
$attivita_arr = array(' ','studente','lavoratore','disoccupato');
echo '
<table border="1">
<tr>
<th>Nome</th>
<th>Email</th>
<th>Sesso</th>
<th>Newsletter</th>
<th>Attività</th>
<th>Messaggio</th>
</tr>';
while ($row = mysql_fetch_assoc($result)) {
$nome
= htmlentities($row['nome']);
$email
= htmlentities($row['email']);
$messaggio = htmlentities($row['messaggio']);
$messaggio = nl2br($messaggio);
if(!$email)
$email = ' ';
if(!$messaggio) $messaggio = ' ';
$sesso = $sesso_arr[$row['sesso']];
$newsletter = $row['newsletter'] ? 'Si' : 'No';
$attivita = $attivita_arr[$row['attivita']];
echo "<tr>
<td>$nome</td>
<td>$email</td>
<td>$sesso</td>
<td>$newsletter</td>
<td>$attivita</td>
<td>$messaggio</td>
</tr>";
}
echo '</table>';
// libero la memoria di PHP occupata dai record estratti con la SELECT
mysql_free_result($result);
// chiudo la connessione a MySQL
mysql_close();
?>
Le righe 34-36 provvedono alla conversione dei caratteri speciali con le relative entità HTML attraverso
l'istruzione htmlentities(). La corretta visualizzazione degli "a capo" è invece affidata alla funzione
nl2br() (riga 37)
La decodifica delle informazioni del sesso e dell'attività avviene attraverso l'uso degli array $sesso_arr e
$attivita_arr. Per evitare la creazione di celle vuote abbiamo utilizzato l'entità   sia negli array
$sesso_arr e $attivita_arr (righe 19-20), sia come alternativa alla stringa vuota per le variabili $email e
$messaggio (righe 39-40).
Al di là delle precauzioni prese in questo script di esempio, ciò che è doveroso sottolineare è l'importanza
di una corretta valutazione del formato di destinazione dei dati prelevati dal database ((X)HTML, PDF,
email, file di testo, XML, ecc.). Lo script PHP infatti ha il compito di fare da tramite tra il database e
l'output finale pertanto non si può limitare ad estrarre i record dalla tabella ma deve effettuare
un'adeguata conversione degli stessi, operazione che se sottovalutato può condurre a seri problemi di
sicurezza.
Cancellazione dei dati
La cancellazione di record presenti in una tabella è un'operazione che richiede l'esecuzione di una
semplice query. Ad esempio per cancellare dalla tabella utenti il record con id pari a 5 basta lanciare la
seguente query.
DELETE FROM utenti WHERE id = 5
Per eseguire questa cancellazione in PHP è sufficiente quindi il seguente script:
<?php
// richiamo il file di configurazione
require 'config.php';
// richiamo lo script responsabile della connessione a MySQL
require 'connect.php';
// preparo la query
$query = "DELETE FROM utenti WHERE id = 5";
// invio la query
$result = mysql_query($query);
// controllo l'esito
if (!$result) {
die("Errore nella query $query: " . mysql_error());
}
// chiudo la connessione a MySQL
mysql_close();
echo 'Query eseguita correttamente';
?>
Di solito però lo script che deve cancellare dei dati non sa a priori quale record eliminare. In un contesto
interattivo è l'utente ad indicare cosa cancellare, e lo fa cliccando su di un link o selezionando in altro
modo i record da eliminare. Lo script quindi dovrebbe occuparsi di mostrare i dati all'utente, permettergli
di selezionare quelli da cancellare, ed infine effettuare l'operazione.
Vediamo quindi come realizzare uno script che mostra l'elenco dei record presenti nella tabella "utenti",
associando a ciascuno di essi un link e una checkbox attraverso cui selezionare quelli da eliminare. Lo
script verrà suddiviso in due pari, la prima adibita alla creazione dell'elenco di record disponibili, con
relativi link e checkbox, la seconda per l'eliminazione del record identificato dall'id passato via $_GET o
via $_POST.
<?php
// richiamo il file di configurazione
require 'config.php';
// richiamo lo script responsabile della connessione a MySQL
require 'connect.php';
if($_POST)
{
$ids = isset($_POST['id']) ? $_POST['id'] : array();
elimina_record($ids);
}
elseif(isset($_GET['id']))
{
elimina_record(array($_GET['id']));
}
else
mostra_lista();
function mostra_lista()
{
// mostro un eventuale messaggio
if(isset($_GET['msg']))
echo '<b>'.htmlentities($_GET['msg']).'</b><br /><br />';
// preparo la query
$query = "SELECT id,nome FROM utenti";
// invio la query
$result = mysql_query($query);
// controllo l'esito
if (!$result) {
die("Errore nella query $query: " . mysql_error());
}
echo '
<form name="form1" method="post" action="">
<table border="1">
<tr>
<th> </th>
<th>Nome</th>
<th> </th>
</tr>';
while ($row = mysql_fetch_assoc($result))
{
$nome = htmlentities($row['nome']);
// preparo il link per la modifica dei dati del record
$link = $_SERVER['PHP_SELF'].'?id=' . $row['id'];
echo "<tr>
<td><input name=\"id[]\" type=\"checkbox\"
value=\"$row[id]\" /></td>
<td>$nome</td>
<td><a href=\"$link\">elimina</a></td>
</tr>";
}
echo '</table>
<br />
<input type="submit" name="Submit" value="Elimina record
selezionati" />
</form>';
// libero la memoria di PHP occupata dai record estratti con la
SELECT
mysql_free_result($result);
// chiudo la connessione a MySQL
mysql_close();
}
function elimina_record($ids)
{
// verifico che almeno un id sia stato selezionato
if(count($ids) < 1)
{
$messaggio = urlencode("Nessun record selezionato!");
header('location:
'.$_SERVER['PHP_SELF'].'?msg='.$messaggio);
exit;
}
// per precauzione converto gli ID in interi
$ids = array_map('intval',$ids);
// creo una lista di ID per la query
$ids = implode(',',$ids);
// preparo la query
$query = "DELETE FROM utenti WHERE id IN ($ids)";
// invio la query
$result = mysql_query($query);
// controllo l'esito
if (!$result) {
die("Errore nella query $query: " . mysql_error());
}
// conto il numero di record cancellati
$num_record = mysql_affected_rows();
// chiudo la connessione a MySQL
mysql_close();
$messaggio = urlencode("Numero record cancellati: $num_record");
header('location: '.$_SERVER['PHP_SELF'].'?msg='.$messaggio);
}
?>
La funzione mostra_lista() si occupa di creare l'elenco dei record all'interno di un form. Per ciascuno di
essi viene mostrato il nome, a cui viene applicata la funzione htmlentities() per la conversione dei
caratteri nelle relative entità HTML, il link per la cancellazione diretta e la checkbox di selezione.
La funzione elimina_record() invece provvede alla cancellazione dei record selezionati. Gli id dei record
vengono passati all'interno dell'array $ids (riga 10,11,15). Questi vengono prima convertiti in numeri
interi per precauzione (riga 83), trasformati in una stringa di id separai da virgola (riga 86) ed infine
inseriti nella query (riga 89).
Eseguita la query vengono contati i record effettivamente interessati da questa operazione attraverso la
funzione mysql_affected_rows() (riga 100) e viene effettuato un redirect all'URL corrente per evitare
possibili problemi legati al refresh della pagina, come visto nella Lezione 5. Anche in questo caso un
messaggio viene accodato all'URL che verrà visualizzato all'inizio della funzione mostra_lista().
Un punto debole dello script presentato risiede nel passaggio degli id dei record da cancellare attraverso
un link ($_GET) o una checkbox ($_POST), elementi comunque accessibili all'utente e pertanto soggetti a
manomissione. Supponiamo ad esempio che un utente abbia i permessi sufficienti per poter cancellare
solo i record con id compreso tra 1 e 10. La funzione mostra_lista() deve quindi mostrare solo questi
record ma nulla vieta all'utente di manomettere un link per la cancellazione inserendo un id superiore a
10. Senza nessun controllo quindi l'utente potrebbe eliminare record su cui non ha i permessi. Diverse le
possibili soluzioni al problema. Prima di effettuare la DELETE si potrebbe verificare che l'id passato sia tra
quelli accessibili all'utente impiegando la stessa logica usata da mostra_lista() per la loro individuazione.
Se tale procedimento risulta oneroso mostra_lista() potrebbe salvare in sessione gli id dei record
accessibili all'utente. Contro la manomissione dei link esiste anche una particolare tecnica denominata
HMAC implementata nel package PEAR::Crypt_HMAC.
Aggiornamento dei dati
L'aggiornamento dei dati presenti in una tabella MySQL è affidato ad una query di tipo UPDATE. Ad
esempio, per modificare i dati associati al record con id = 5 della tabella "utenti" potremmo impiegare la
seguente query:
UPDATE
utenti
SET
nome = 'Mario',
email = '[email protected]',
sesso = 1,
newsletter = 0,
attivita = 2,
messaggio = 'Ciao a tutti!'
WHERE
id = 5
La semplice esecuzione della query in PHP richiede la realizzazione di un banale script:
<?php
// richiamo il file di configurazione
require 'config.php';
// richiamo lo script responsabile della connessione a MySQL
require 'connect.php';
// preparo la query di aggiornamento
$query = "UPDATE utenti SET
nome = 'Mario',
email = '[email protected]',
sesso = 1,
newsletter = 0,
attivita = 2,
messaggio = 'Ciao a tutti!'
WHERE id = 5";
// invio la query
$result = mysql_query($query);
// controllo l'esito
if (!$result) {
die("Errore nella query $query: " . mysql_error());
}
// chiudo la connessione a MySQL
mysql_close();
echo 'Query eseguita correttamente';
?>
Similmente a quanto visto nelle precedenti lezioni, anche l'aggiornamento dei dati all'interno di una reale
applicazione web può richiedere ulteriori accorgimenti. Spesso infatti è necessario interagire con l'utente
per determinare quale record aggiornare. Inoltre, se è l'utente a dover inserire i nuovi dati, è conveniente
che possa farlo partendo da quelli attualmente presenti nel record. Un ipotetico script interattivo per la
modifica dei dati della tabella "utenti" quindi dovrà prevedere le seguenti fasi:

mostrare un elenco dei record presenti nella tabella, associando a ciascuno un link che punta alla
pagina di modifica dei relativi dati;

mostrare un form contenente i dati correntemente associati al record selezionato;

aggiornare i dati del record con quelli inviati dall'utente attraverso il form.
Vediamo il codice necessario a realizzare questo script:
<?php
// richiamo il file di configurazione
require 'config.php';
// richiamo lo script responsabile della connessione a MySQL
require 'connect.php';
if($_POST && isset($_GET['id']))
{
aggiorna_record();
}
elseif(isset($_GET['id']))
{
mostra_record();
}
else
mostra_lista();
function mostra_lista()
{
// mostro un eventuale messaggio
if(isset($_GET['msg']))
echo '<b>'.htmlentities($_GET['msg']).'</b><br /><br />';
// preparo la query
$query = "SELECT id,nome FROM utenti";
// invio la query
$result = mysql_query($query);
// controllo l'esito
if (!$result) {
die("Errore nella query $query: " . mysql_error());
}
echo '
<table border="1">
<tr>
<th>Nome</th>
<th> </th>
</tr>';
while ($row = mysql_fetch_assoc($result))
{
$nome = htmlspecialchars($row['nome']);
// preparo il link per la modifica dei dati del record
$link = $_SERVER['PHP_SELF'] . '?id=' . $row['id'];
echo "<tr>
<td>$nome</td>
<td><a href=\"$link\">modifica</a></td>
</tr>";
}
echo '</table>';
// libero la memoria di PHP occupata dai record estratti con la
SELECT
mysql_free_result($result);
// chiudo la connessione a MySQL
mysql_close();
}
function aggiorna_record()
{
// recupero i campi di tipo "stringa"
$nome
= trim($_POST['nome']);
$email
= trim($_POST['email']);
$messaggio = trim($_POST['messaggio']);
// verifico se devo eliminare gli slash inseriti automaticamente
da PHP
if(get_magic_quotes_gpc())
{
$nome
= stripslashes($nome);
$email
= stripslashes($email);
$messaggio = stripslashes($messaggio);
}
// effettuo l'escape dei caratteri speciali per inserirli
all'interno della query
$nome
= mysql_real_escape_string($nome);
$email
= mysql_real_escape_string($email);
$messaggio = mysql_real_escape_string($messaggio);
// recupero gli altri campi del form
$sesso
= isset($_POST['sesso']) ? intval($_POST['sesso']) :
0;
$newsletter = isset($_POST['newsletter']) ? 1 : 0;
$attivita
= intval($_POST['attivita']);
$id = intval($_GET['id']);
// verifico la presenza dei campi obbligatori
if(!$nome)
{
$messaggio = urlencode("Non hai inserito il nome");
header("location:
$_SERVER[PHP_SELF]?id=$id&msg=$messaggio");
exit;
}
// preparo la query
$query = "UPDATE utenti SET
nome = '$nome',
email = '$email',
sesso = $sesso,
newsletter = $newsletter,
attivita = $attivita,
messaggio = '$messaggio'
WHERE id = $id";
// invio la query
$result = mysql_query($query);
// controllo l'esito
if (!$result) {
die("Errore nella query $query: " . mysql_error());
}
// chiudo la connessione a MySQL
mysql_close();
$messaggio = urlencode('Aggiornamento effettuato con successo');
header("location: $_SERVER[PHP_SELF]?msg=$messaggio");
}
function mostra_record()
{
// mostro un eventuale messaggio
if(isset($_GET['msg']))
echo '<b>'.htmlentities($_GET['msg']).'</b><br /><br />';
$id = intval($_GET['id']);
// preparo la query
$query = "SELECT nome,email,sesso,newsletter,attivita,messaggio
FROM utenti WHERE id = $id";
// invio la query
$result = mysql_query($query);
// controllo l'esito
if (!$result) {
die("Errore nella query $query: " . mysql_error());
}
// controllo che la SELECT abbia restituito un record
// l'id passato via GET potrebbe essere stato manipolato
if(mysql_num_rows($result) != 1) {
die("l'ID passato via GET è errato");
}
list($nome,$email,$sesso,$newsletter,$attivita,$messaggio) =
mysql_fetch_row($result);
$nome
= htmlspecialchars($nome);
$email
= htmlspecialchars($email);
$messaggio = htmlspecialchars($messaggio);
?>
<form name="form_registrazione" method="post" action="">
<label>nome:
<input name="nome" type="text" value="<?echo $nome?>" />
</label>
<p>
<label>email:
<input name="email" type="text" value="<?echo $email?>" />
</label>
</p>
<p> Sesso:
<label>
<input type="radio" name="sesso" value="1" <?if($sesso==1)
echo 'checked="checked"'?> />
M</label>
<label>
<input type="radio" name="sesso" value="2" <?if($sesso==2)
echo 'checked="checked"'?>/>
F</label>
</p>
<p>
<label>inviami newletter:
<input name="newsletter" type="checkbox" value="1"
<?if($newsletter) echo 'checked="checked"'?> />
</label>
</p>
<p>
<label>attività:
<select name="attivita">
<option value="0">:: seleziona ::</option>
<option value="1" <?if($attivita==1) echo
'selected="selected"'?>>studente</option>
<option value="2" <?if($attivita==2) echo
'selected="selected"'?>>lavoratore</option>
<option value="3" <?if($attivita==3) echo
'selected="selected"'?>>disoccupato</option>
</select>
</label>
</p>
<p>
<label>messaggio:<br />
<textarea name="messaggio" cols="40" rows="5"><?echo
$messaggio?></textarea>
</label>
</p>
<p>
<input name="invia" type="submit" value="Invia" />
</p>
</form>
<?
}
?>
Analizziamo nel dettaglio il funzionamento dello script. Per prima cosa è necessario capire in quale fase
della procedura di aggiornamento ci troviamo. Per farlo verifichiamo la presenza di dati inviati dall'utente
($_POST) e dell'id del record passato attraverso un link ($_GET['id']). Se non è presente nessuno di
questi dati allora viene richiamata la funzione mostra_lista(). Se invece è presente solo l'id allora l'utente
avrà cliccato su un link e quindi mostriamo il form per la modifica dei dati, attraverso la funzione
mostra_record(). Se infine è presente sia l'id, sia i dati inviati col form allora procediamo
all'aggiornamento del record, richiamando aggiorna_record(). Le righe 8-17 fungono quindi da "gestore
degli eventi" del nostro script
La funzione mostra_lista() si occupa di estrarre e visualizzare l'elenco dei record presenti nella tabella
"utente", associando a ciascuno di essi un link per l'aggiornamento dei dati (riga 48). Le righe 22-23
mostrano un eventuale messaggio generato da aggiorna_record(), funzione discussa in seguito. Si noti
come i testi dinamici inseriti nella pagina vengono adeguatamente passati prima alla funzione
htmlentities() per la conversione dei caratteri in entità HTML (righe 23,45).
La funzione mostra_record() recupera i dati associati al record da modificare, individuato da $_GET['id'],
e li inserisce in un form. La funzione intval() applicata a $_GET['id'] ci assicura che l'id passato alla query
è costituito da un numero intero (riga 131). Effettuata l'interrogazione al database viene verificata
l'effettiva restituzione del record (riga 146) come controllo sulla validità dell'id. Anche in questo caso le
stringhe dinamiche da inserire nel form ($nome, $email, $messaggio) vengono preventivamente passate
alla funzione htmlentities() (righe 152-154).
La funzione aggiorna_record() ha infine il compito di effettuare la query di UPDATE con i nuovi dati inviati
dall'utente. Eliminati i possibili spazi vuoti alle estremità delle stringhe $nome, $email e $messaggio con
la funzione trim (righe 68-70), la funzione si occupa di effettuare un corretto escape delle stesse (righe
73-83). Come ampiamente discusso nella Lezione 5, dapprima si eliminano i backslash eventualmente
inseriti da PHP e poi si procede ad effettuare l'escape con la funzione mysql_real_escape_string(). Viene
verificato anche se l'utente ha inserito del testo nel campo "nome" considerato obbligatorio nell'esempio.
In caso contrario viene ricaricata la pagina accodando all'URL un messaggio esplicativo (righe 93-97). Se
invece il controllo ha esito positivo si procede con l'esecuzione della query di aggiornamento dati e si
conclude lo script con un redirect del browser che riporta l'utente alla fase iniziale. Si noti ancora una
volta l'uso fatto del redirect attraverso la funzione header() (righe 96, 122) come possibile soluzione al
problema del refresh della pagina, già discusso nella Lezione 6.
Tutta la logica di funzionamento dello script si basa sull'id del record passato attraverso l'URL
($_GET['id']) pertanto il codice è potenzialmente affetto da un problema di sicurezza legato alla possibile
alterazione di tale valore da parte dell'utente. Continuano a valere le considerazioni fatte a riguardo nella
Lezione 7.
Prima di concludere è doveroso fare un breve riepilogo ed alcune precisazioni sull'uso della funzioni
mysql_num_rows() e mysql_affected_rows() incontrate più volte nei precedenti esempi:

mysql_num_rows() restituisce il numero di record appartenenti ad un result set, prodotto ad
esempio (ma non solo!) da una query di tipo SELECT;

mysql_num_rows() non funziona correttamente con mysql_unbuffered_query(), poiché restituirà
il risultato corretto solo al termine della procedura di fetch;

mysql_affected_rows() restituisce il numero di record realmente interessato dall'ultima query di
tipo INSERT, UPDATE o DELETE;

nelle versioni di MySQL precedenti alla 4.1.2, se viene effettuata una DELETE senza la clausola
WHERE, mysql_affected_rows() restituisce sempre 0;

mysql_affected_rows() per una UPDATE non considera i record aggiornati con dati identici a quelli
preesistenti. Solo i record realmente modificati vengono conteggiati. Tale comportamento può essere
modificato passando il numero 2 come quinto parametro della mysql_connect():
mysql_connect("localhost", "user", "password", false, 2);

mysql_affected_rows() per una query di tipo REPLACE restituisce la somma dei record cancellati e
di quelli inseriti;

se si è all'interno di una transazione non bisogna attendere la COMMIT per eseguire
mysql_affected_rows() ma è necessario lanciarla subito dopo la query di INSERT, UPDATE o DELETE.
Debug degli script PHP/MySQL
Individuare i bachi di uno script PHP/MySQL ed interpretare correttamente le segnalazioni di errore può
essere un compito non banale, sopratutto se si è alle prime armi. Inutile sottolineare che una corretta
impostazione degli script ed una adeguata gestione degli errori facilitano le operazioni di debug e
sopratutto riducono la probabilità di malfunzionamenti del codice. Senza alcuna pretesa di completezza,
di seguito vengono proposte alcune linee guida da seguire nella realizzazione di script PHP/MySQL:

Durante la fase di sviluppo, impostare il livello di segnalazione degli errori al massimo; attraverso
il php.ini, impostando la direttiva "error_reporting" a "E_ALL", oppure direttamente da script, con
l'istruzione error_reporting(E_ALL) da inserire all'inizio del codice. E' più semplice risolvere un baco
quando si hanno informazioni anche sulle anomalie considerate (forse a torto) meno importanti.

Controllare l'esito delle funzioni mysql_connect, mysql_select_db, mysql_query ecc. verificando il
valore che restituiscono e confrontandolo con quello atteso. Ad esempio se il valore restituito da
mysql_query è false allora la query non è corretta.

Usare il carattere @ anteposto alle funzioni mysql come "silenziatore" dei messaggi di errore solo
se l'esito della funzione stessa viene verificato in altro modo. In caso contrario un errore passerebbe
inosservato.

Interrompere l'esecuzione dello script al primo errore segnalato. Si eviterà così la generazione di
errori a catena che possono confondere e complicare la ricerca della causa primaria del
malfunzionamento. Ad esempio, è inutile cercare di eseguire una query quando la connessione al
server non ha avuto successo. Si otterrebbe un errore anche per la query ma il realtà il problema è a
monte.

Memorizzare tutte le query create dinamicamente in una variabile prima di lanciarle con la
funzione mysql_query. In caso di errore mostrare il contenuto di questa variabile, ad esempio con la
funzione die(), per verificare se la costruzione della query è corretta.

Prima di realizzare lo script provare ad eseguire le query direttamente con un client MySQL in
modo da essere sicuri della loro correttezza. Come approfondiremo più avanti, è importante saper
distinguere tra gli errori dell'SQL e quelli dello script PHP.
Nonostante tutte le precauzioni che è possibile (e doveroso) prendere, prima o poi qualcosa nei vostri
script andrà storto e vi ritroverete ad interpretare le classiche segnalazioni di errore di PHP, alcune delle
quali possono risultare davvero sibilline. Di seguito vengono riportate quelle più comuni insieme alla
spiegazione delle possibili cause e soluzioni.
Fatal error: Call to undefined function mysql_connect()
PHP non riconosce la funzione mysql_connect() a causa del mancato caricamento del modulo mysql. Solo
la versione Windows di PHP 4 ha il modulo mysql già presente all'interno di PHP. Trovate le istruzioni per
caricare il modulo mysql con PHP 5 su Windows XP in questo articolo. Con Linux è invece necessario
ricompilare PHP con l'opzione --with-mysql oppure installare i pacchetti (RPM, deb, ecc.) opportuni offerti
dalla distribuzione in uso.
Warning: mysql_connect() [function.mysql-connect]: Unknown MySQL server host
'nome_server' (11001)
PHP non riesce ad individuare l'host (la macchina) su cui dovrebbe girare il server MySQL. Controllare
quindi la sintassi del nome_server.
Warning: mysql_connect() [function.mysql-connect]: Can't connect to MySQL server on
'localhost' (10061)
Nella maggior parte dei casi questo errore si verifica se il server MySQL non è attivo. In Windows è facile
verificarlo, accedendo al Task Manager (Ctrl+Alt+Canc) e verificando la presenza di un processo dal
nome mysqld-nt.exe. Con linux si può invece cercare il demone mysqld dalla shell con il comando "ps
aux". Se non è attivo le cause possono essere diverse: MySQL non è stato installato, l'installazione non è
stata completata correttamente, MySQL semplicemente non è stato avviato. Se state cercando di
collegarvi ad un server remoto (non in localhost), la connessione potrebbe essere impedita da un firewall,
oppure MySQL potrebbe essere stato configurato per non accettare connessioni dall'esterno.
Warning: mysql_connect() [function.mysql-connect]: Access denied for user
'nome'@'localhost' (using password: YES/NO)
Il nome e/o la password forniti alla funzione mysql_connect() non sono corretti. L'errore mostra il nome
utente e l'host al quale ci si sta connettendo e pertanto potrebbe costituire un problema di sicurezza. In
questo caso è consigliato usare il silenziatore degli errori @ e verificare l'esito di mysql_connect
controllandone il risultato restituito.
Client does not support authentication protocol requested by server; consider upgrading
MySQL client
Tipico errore che si presenta la prima volta che si esegue uno script di connessione con PHP 4 a MySQL
4.1 o successivi. PHP 4 infatti viene distribuito con una libreria per l'accesso a MySQL che non supporta la
nuova modalità di connessione introdotta con MySQL 4.1 e pertanto il tentativo fallisce. Una possibile
soluzione risiede nell'indicare a MySQL di usare il vecchio metodo di autenticazione. Per farlo basta
lanciare questa query impiegando un client MySQL:
SET PASSWORD FOR 'nome_uteste'@'nome_host' = OLD_PASSWORD('nuova_password');
Unknown database 'db_inesistente'
E' stata eseguita l'istruzione mysql_select_db() specificando il nome di un database che però non esiste.
Controllare quindi il nome del database indicato.
Warning: mysql_select_db(): supplied argument is not a valid MySQL-Link resource
Il messaggio avverte che il secondo parametro passato alla funzione mysql_select_db() non è un valido
identificativo di connessione MySQL, ovvero il valore restituito da una mysql_connect() eseguita con
successo. Una delle cause più frequenti è un fallito tentativo di connessione al server MySQL ed il
successivo tentativo di selezione del database. In tal caso quindi bisogna verificare la procedura di
connessione. Oppure, più banalmente, il secondo parametro passato a mysql_select_db() non è il
risultato restituito dalla mysql_connect().
Warning: mysql_fetch_array(): supplied argument is not a valid MySQL result resource
Warning: mysql_free_result(): supplied argument is not a valid MySQL result resource
Il messaggio avverte che il parametro passato all'istruzione di fetch o alla mysql_free_result() non è un
valido "MySQL result resource", ovvero quella particolare "ricevuta" restituita da mysql_query() in caso di
una SELECT eseguita con successo (se n'è discusso nella Lezione 6). Probabilmente quindi stiamo
cercando di effettuare un operazione sul set di risultati ma in realtà tali dati non esistono perché la query
non ha avuto successo. Oppure, più banalmente, il parametro passato all'istruzione di fetch o alla
mysql_free_result() non è il risultato restituito da mysql_query().
Questi errori si verificano quando è stata effettuata una query contenente degli errori. L'istruzione
mysql_query ha segnalato il problema restituendo il valore FALSE ma nello script non c'è stato un
controllo su tale valore. Lo script pertanto prosegue fino ad arrivare alle istruzioni di fetch che però non
possono agire poiché non esiste un set di risultati.
Errori nella sintassi delle query
Le segnalazioni di errore fin qui mostrate vengono generate da PHP, ad esempio perché non è in grado di
connettersi al server MySQL, oppure perché le funzioni non vengono adoperate correttamente. Sono
quindi errori che coinvolgono PHP direttamente che pertanto provvede subito a segnalarli con dei
Warning. Ci sono però errori che non riguardano PHP ma si verificano nel server MySQL, generati ad
esempio da una query con sintassi errata. In questi casi è MySQL ad accorgersi del problema ma non
potendo comunicare direttamente con l'utente, può solo segnalare l'accaduto a PHP che a sua volta si
limita a restituire FALSE come risultato della mysql_query(). Tali errori quindi possono passare del tutto
inosservati se nello script PHP non vengono implementati gli opportuni controlli. Per simili errori la
funzione mysql_error() è l'unica a poterci dare informazioni precise sul problema verificatosi