Laboratorio di Sistemi Informativi
Validazione dati
Veniamo ad una problematica tipica delle applicazioni sulle basi di dati: la validazione dei dati inseriti. Si tratta di fare in modo che le operazioni richieste dall'utente non violino i vincoli di integrità imposti sulla base di dati. Ad esempio, nel caso della procedura di inserimento di nuovi oggetti, sarebbe preferibile che il sistema si rifiutasse di inserire un oggetto con il nome o la descrizione vuota. Analogamente, non dovrebbe essere possibile inserire nel campo categoria_id
di un oggetto un valore inesistente (si tratta di un vincolo di integrità referenziale). Notare che, per quanto riguarda l'ultimo punto, visto che all'utente viene presentata la scelta tra le sole categorie esistenti, sembra impossibile che il problema si verifichi: vedremo che così non è.
Ci sono tre possibili ambienti in cui effettuare la validazione dei
dati in una
architettura a tre livelli:
- nel server database
- nel server applicativo (il server web nel nostro caso)
- nel client (il browser)
La validazione all'interno del browser fa sì che i dati non
vengano neanche spediti al server web. Ad esempio, se l'utente immette
una stringa dove il sistema si aspetta un numero, è possibile
far sì che il browser si accorga di questo fatto e richieda
all'utente di reimmettere i dati. La validazione lato client richiede
l'utilizzo del linguaggio Javascript.
Notare che la validazione lato client non è sicura!! L'utente
può sempre disattivare Javascript e quindi aggirare (volontariamente o no) i controlli.
Quando la validazione dati è eseguita sul server web, il
programma che gestisce l'applicazione (i vari script PHP nel nostro
caso) effettuano dei controlli prima di effetturare eventuali modifiche
nella base di dati. La modifica viene effettuata solo se il controllo
ha successo, altrimenti si visualizza un appropriato messaggio di
errore.
Infine, si può relegare tutto al DBMS: questi può
controllare la validità dei vincoli di chiavi esterna, o anche
vincoli più complessi qualora questi siano supportati.
Tuttavia, un approccio di questo tipo è poco
efficiente (i dati devono viaggiare dal client fino al server DBMS
anche quando errati). Inoltre, è necessario comunque che
l'errore venga successivamente mostrato all'utente, per cui lo script PHP deve
accorgersi che si è verificato un errore nell'esecuzione di un
comando SQL, capire a cosa è dovuto questo errore, e
visualizzare il messaggio appropriato. Se a ciò aggiungiamo che
MySQL offre solo un supporto limitato al trattamento dei vincoli di
integrità, si capisce che questo è un approccio che
è meglio lasciar perdere.
Modelli di validazione
Decidere come visualizzare i messaggi di errori dovuti alla presenza di
dati sbagliati è un problema difficile. Se questo aspetto
dell'applicazione viene pensato male, l'utente può trovarsi
infastidito da continui messaggi di errori, difficili da eliminare,
magari anche inaccurati o poco informativi. Non c'è niente di
peggio che avere un messaggio che dice che si è verificato un
errore ma non spiega in che campo di input c'è il problema.
La validazione è in
realtà un processo composto da due fasi: individuazione degli errori e presentazione degli errori all'utente.
L'individuazione degli errori può essere:
- interattiva, quando gli
errori vengono controllati mano a mano che l'utente immette i dati;
- a posteriori, quando gli
errori vengono controllati quando l'utente ha finito di immettere tutti i
dati.
La validazione interattiva era praticabile solo lato client. Se si voleva realizzare lato server, richiedeva l'invio di dati al server web e il successivo ricarimento della pagina ogni volta che l'utente modificava il valore di campo di una form, causando una perdita di tempo inaccettabile. Negli ultimi anni, con l'evoluzione della tecnologia nota come AJAX (Asynchronous JavaScript and XML), comincia a essere possibile realizzare una validazione interattiva anche lato server, senza eccessiva degradazione delle prestazioni.
Per quanto riguarda la presentazione degli errori, questa può avvenire in due modi:
- un campo alla volta,
se gli errori vengono mostrati a mano mano che sono individuati;
- in maniera batch,
quando tutti gli errori sono presentati con un messaggio unico.
Come per il caso precedente, quando la validazione è lato server
la presentazione batch degli errori è preferibile, perché
più efficiente. Visualizzare gli errori uno alla volta, chiedere
di correggerli, e passare poi all'errore successivo richiede continui
passaggi di dati tra browser e server web.
In generale la soluzione migliore è avere una validazione batch
a posteriori sul lato server assieme ad una validazione lato client
(che può essere di qualunque tipo si desideri). Perché
raddoppiare le validazioni?
- la validazione lato server è essenziale perchè non
può essere aggirata dall'utente. In questo modo ci si assicura
veramente che i dati inseriti rispettino tutti i vincoli richiesti.
- la validazione lato client è però più
efficiente, e inoltre può essere sia eseguita a posteriori che
interattiva, adattandosi quindi meglio al tipo di applicazione. Dunque,
essa è opzionale ma altamente desiderabile.
ATTENZIONE! Purtroppo mentre PHP
è indipendente dal browser che sta usando l'utente, lo stesso
non vale per Javascript. Ne segue che, quando si adotta la validazione
lato client, occorre controllare su vari tipi di browser che la
validazione funzioni bene, visto che non tutto si comporta esattamente
allo stesso modo con browser diversi.
Validazione batch della pagina di inserimento
Una prima soluzione al problema può essere data dallo script inserimento-validate.php. Questo controlla che il nome e la descrizione dell'oggetto non siano vuoti. Qualora ciò non accada, invece di inserire l'oggetto viene visualizzata una pagina di errore.
È possibile tuttavia visualizzare gli errori in maniera più accattivante. Ad esempio, possiamo modificare lo script in modo che, in caso di errore, venga rivisualizzata la pagina di inserimento dati, con in più l'elenco dei problemi che si sono verificati e con i campi di input già pre-riempiti. Si ottiene inserimento-validate2.php.
In questo script abbiamo fatto uso delle variabili di sessione $_SESSION['postdata']
e $_SESSION['errors']
per passare i valori immessi dall'utente e gli errori che si sono generati dalla fase di controllo dati/inserimento alla fase di richiesta dati/visualizzazione errori. Le variabili vengono controllate nella FORM di immissione dati e, se presenti, causano il preriempimento dei campi di input e la visualizzazione dei messaggi di errore. Una volta che queste variabili di sessione non servono più, ovvero alla fine dello script con la FORM, è necessario eliminarle, e lo abbiamo fatto usando la seguente funzione:
void unset (mixed var1 [, mixed var [, mixed ...]])
: svuota le variabili che le vengono passate come parametro. È equivalente ad assegnarle il valore NULL
.
Come alternativa a unset
si potrebbe pensare di utilizzare session_destroy
, che però distrugge tutte le variabili di sessione, compresa quella che ci indica se abbiamo o no fatto il login: almeno nel nostro caso, questa soluzione non è applicabile.
Altra caratteristica di inserimento-validate2.php
è l'uso dell'operatore @
. Si tratta di un operatore unario che ha come effetto quello di inbire la generazione dei messaggi di errore che si verificherebbero altrimenti. Ad esempio: $x=1/0
genera l'errore di divisione per zero, mentre $x=@(1/0)
non genera alcun errore. Noi la utilizziamo per preriempire i campi di input con il valore immesso precedentemente. Come abbiamo detto, questi valori si trovano in $_SESSION['postdata']
, per cui, per visualizzare il campo nome opportunamente riempito, si dovrebbe usare:
<input name="nome" type="text" value="<?php echo $_SESSION['postdata']['nome']?>">
Quando però lo script della FORM viene eseguito perché è stato premuto il pulsante "Inserisci oggetto" dal menù, la variabile di sessione $_SESSION['postdata']
non è definita, e la riga di sopra genererebbe un errore. A questo si può sopperire chiamando prima isset()
per vedere se $_SESSION['postdata']
esiste, come in:
<input name="nome" type="text" value="<?php if (isset($_SESSION['postdata']['nome'])) echo $_SESSION['postdata']['nome']?>">
o più semplicmente con:
<input name="nome" type="text" value="<?php @echo $_SESSION['postdata']['nome']?>">
ATTENZIONE! Cosa dire del vincolo di integrità che lega gli oggetti alle categorie? Il nostro software non controlla che la categoria che viene passata allo script di inserimento sia veramente presente nel database. Il controllo sembrerebbe inutile, visto che allo script di inserimento si accede tramite una form che consente di scegliere solo categorie veramente esistenti. Tuttavia, questo è vero solo se l'obiettivo è quello di impedire che l'utente "buono" non sbagli, ma non impedisce assolutamente che l'utente "cattivo" violi i vincoli di integrità della nostra base di dati. Vedremo i dettagli nella lezione sulla sicurezza.
Validazione interattiva della pagina di inserimento voli
Visto che non è scopo del corso insegnare il JavaScript,
mostriamo solo un esempio di come è possibile implementare un
controllo interattivo lato client: inserimento-validate-js.php.
Esercizio 1
Modificare inserimento-validate2
in modo tale che, quando si verifica un errore, l'etichetta dei campi di input che sono stati riempiti con i dati sbagliati vengano visualizzati in corsivo. A questo proposito, è possibile usare il tag <em>
.