Lezione Precedente
Lezione Successiva

Laboratorio di Sistemi Informativi

PHP e MySQL

Il PHP 5 mette a disposizione del programmatore due librerie diverse per interfacciarsi a un database MySQL: la librerie mysql e mysqli (dove i sta per improved). La libreria mysqli è sicuramente la migliore, sia perchè supporta alcune funzionalità di MySQL 4.1 e 5.0 che non sono supportate dall'altra libreria, sia perchè ha una struttura object oriented che la rende più simile ad API simili sviluppate per altri linguaggi (ad esempio al JDBC per il linguaggio Java).

Tuttavia, la mysqli non è disponibile ancora su tutte le distribuzioni di Linux. Questo, e il fatto che in realtà non usiamo nessuna delle funzioni evolute di MySQL che ne richiederebbero l'utilizzo, ci induce a utilizzare la vecchia libreria mysql.

Accesso a MySQL

Cominciamo ad imparare come accedere ad un database MySQL tramite PHP. Tutto si basa su alcune funzioni che il linguaggio ci mette a disposizione.
Il risultato di mysql_connect è un tipo che non abbiamo ancora visto, il tipo resource. Il tipo resource contiene valori che identificano una qualche risorsa che è stata concessa al programma PHP, nel nostro caso la risorsa è una connessione al database. Il valore di una variabile resource non ha nessun significato per un essere umano: la sua unica funzione, tipicamente, è quella di essere passata come parametro ad altre funzioni che agiscono sulla stessa risorsa.
Ad esempio, consideriamo il seguente programma che esegue la query SELECT * FROM voli nel database airdb e visualizza il risultato come pagina HTML.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>Prova PHP e MySQL</title>
</head>
<body>
<?php
mysql_connect("localhost","studente");
mysql_select_db("airdb");
$res=mysql_query("select * from voli");
while ($riga=mysql_fetch_array($res))
  echo $riga["id"]," ",$riga["idsrc"]," ",$riga["iddst"],"<br>";
mysql_close();
?>
</body>
</html>

Lo script inizia con la connessione al server mysql su localhost come studente. Successivamente viene selezionato il database airdb, inviata la query, e il risultato viene messo nella variabile $result. Da lì, una alla volta viene estratta una riga dei risultati e inserita nella variabile $riga, per poi essere stampata. I valori delle chiavi per $riga (ovvero id, idsrc, iddst) sono gli stessi che si ottengono come intestazione delle colonne quando la query select * from voli è inviata da mysqlc. Se si modifica la linea

$res=mysql_query("select * from voli");

con

$res=mysql_query("select id, idsrc, iddst as destinazione from voli");

occorre anche modificare l'echo finale in

echo $riga["id"]," ",$riga["idsrc"]," ",$riga["destinazione"],"<br>";

altrimenti viene generato un errore, che ci avverte che stiamo tentando di accedere alla chiave "iddst" che non esiste.

Controllo degli errori

Cosa succede se si sbaglia a scrivere il nome del database nella funzione mysql_select_db o la query SQL in mysql_query? Ad esempio, modifichiamo mysql_select_db("airdb") con mysql_select_db("airodd"). Si noti che non viene generato nessun errore in corrispondenza di  mysql_select_db, ma viene invece generato il seguente warning in corrispondenza di mysql_fetch_array:

Warning: mysql_fetch_array(): supplied argument is not a valid MySQL result resource in /home/studente/public_html/mysqlprova.php on line 11

Quello che succede è che mysql_select_db fallisce in maniera silenziosa, restituendo il valore false (che non utilizziamo). Successicamente mysql_query fallisce, anch'esso in maniera silenziosa, restituendo ancora il valore false (che noi assegnamo alla variabile $res). Quando si tenta di eseguire la mysql_fetch_array il PHP si lamenta dicendo che $res non contiene una variabile di tipo risorsa come si aspettava.

Il problema è che questo messaggio non da nessuna informazione su dove effettivamente si sia verificato l'errore iniziale. Nasce pertanto l'esigenza, per il programmatore, di gestire gli errori di MySQL in proprio.  Tra le funzioni della libreria mysql, le seguenti possono tornare utili a questo scopo:
Ad esempio, si può modificare la pagina PHP di prima nel modo seguente:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>Prova PHP e MySQL</title>
</head>
<body>
<?php
mysql_connect("localhost","studente");
if (!mysql_select_db("airdbd")) {
?>
  Non riesco ad accedere al database<br>
  Il numero di errore restituito da PHP &egrave;:  <?php echo mysql_errno() ?> <br>
  Il messaggio di errore restituito da PHP &egrave;:  <?php echo mysql_error() ?> <br>
<?php
} else {
  $res=mysql_query("select * from voli");
  while ($riga=mysql_fetch_array($res))
    echo $riga["id"]," ",$riga["idsrc"]," ",$riga["iddst"],"<br>";
  mysql_close();
}
?>
</body>
</html>


In questa variante, viene controllato il risultato di mysql_select_db. Se c'è un errore, il risultato è false e viene visualizzato un messaggio di errore, sfuttando anche le funzioni messe a disposizione da MySQL.  Notare che i messaggi di errori non sono ottenuti con una istruzione echo ma chiudendo lo script PHP iniziale, passando temporaneamente all'HTML, per ritornare successivamente a PHP. Visto che questo pezzo di HTML è all'interno del blocco then di un if, esso sarà mandato al browser solo se la condizione dell'if è verificata. Se, invece, il comando mysql_select_db ha successo, l'esecuzione procede normalmente.

Ovviamente, un controllo simile andrebbe effettuato tutte le volte che si chiama una funzione di MySQL che può terminare con un errore.  Possiamo ottenere quindi qualcosa di questo tipo:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>Prova PHP/MySQL con controllo errori</title>
</head>
<body>
<?php
if (! @mysql_connect("localhost","studente"))
   die ('Non riesco a connettermi al database: '.mysql_error());
if (! mysql_select_db("airdb"))
   die('Non riesco ad accedere al database airdb: '.mysql_error());
if (! ($res=mysql_query("select * from voli")))
   die('Non riesco ad eseguire la query: '.mysql_error());
while ($riga=mysql_fetch_array($res))
   echo $riga["id"]," ",$riga["idsrc"]," ",$riga["iddst"],"<br>";
if (!mysql_close())
   die('Non riesco ad effettuare la close: '.mysql_error());
?>
</body>
</html>

Si notino due cose:
La funzione die() visualizza il parametro di input e termina l'esecuzione del programma PHP:
Se si verifica un errore durante la mysql_connect (ad esempio il server mysql non risponde) il sistema genera automaticamente un warning e continua l'esecuzione del codice PHP. Nell'ottica che il programma gestisca autonomamente le situazioni di errore, questo warning automatico è indesiderato. Si può evitare la generazione di un qualunque messaggio di errore semplicemente facendo precedere una espressione con la chiocciola (il simbolo @).

Funzioni, file include, MySQL e controllo sugli errori

Per scrivere il meno possibile ma nel contempo controllare completamente tutte le condizioni di errore, è possibile definirsi una funzione PHP che si occupa di visualizzare gli errori generati da MySQL, e includerla in tutte le pagine PHP con require. Consideriamo ad esempio la seguente funzione:

function mysql_showerror()
{
    die ('<B>Error</B> '.mysql_errno().' : '.mysql_error());
}

Se mettiamo questa definizione dentro il file error.php, questo ci consente di riscrivere la nostra pagina PHP in questo modo:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>Prova PHP/MySQL con controllo errori e include</title>
</head>
<body>
<?php
require 'error.php';
@mysql_connect("localhost","studente") or mysql_showerror();

mysql_select_db("airdb") or mysql_showerror();
$res=mysql_query("select * from voli") or mysql_showerror();
while ($riga=mysql_fetch_array($res))
   echo $riga["id"]," ",$riga["idsrc"]," ",$riga["iddst"],"<br>";
mysql_close() or mysql_showerror();
?>
</body>
</html>

A parte l'uso della funzione mysql_showerror() e del comando require per includere error.php, un'altra novità di questo script è l'uso dell'operatore or. La notazione exp1 or exp2 equivale (per quello che ci riguarda) a if (! exp1) exp2 ma è più facile da scrivere e da leggere.

In realtà, consiglierei a tutti di utilizzare la seguente versione evoluta di mysql_showerror:

function mysql_showerror()
{
   $bt=debug_backtrace();  
   die ('<B>MySQ Error</B> '.mysql_errno().' : '.mysql_error().
        ' in <b>'.$bt[0]['file'].'</b> on line <b>'.$bt[0]['file'].'</b>');
}


A differenza della precedente, questa versione visualizza il numero di riga e il nome del file dove si è verificato l'errore, ed è quindi molto più utile a scopo di debugging. Utilizza la funzione debug_backtrace(), che spiegheremo però solo più avanti nel corso.

Esercizio 1

Salvare l'ultimo esempio di pagina PHP mostrata. Creare un file error.php che contiene  funzione mysql_showerror() definita sopra. Provare se la pagina PHP funziona. Quando tutto è a posto, provare a modificarla ed inserire degli errori, ed esaminare i risultati che si ottengono.

Esercizio 2

Scrivere una pagina PHP che, ogni volta che viene caricata, aggiunga una nuova riga nella tabella voli.

Lezione Precedente
Lezione Successiva