Sessioni HTTP e PHP

Venerdì, 6 Dicembre 2007

OBIETTIVI DELLA LEZIONE

In questa lezione:

  1. Vedremo cosa sono le sessioni e perchè servono
  2. Miglioreremo la conoscenza di base del linguaggio PHP

Ripasso: variabili esterne $_GET e $_POST

Il client HTTP deve richiedere la pagina al server con uno dei due metodi GET e POST. Come abbiamo visto nelle scorse lezioni, utilizzando il metodo GET gli eventuali controlli provenienti da un form vengono includi nell'URL. In alternativa con il metodo POST vengono inclusi nel corpo della richiesta HTTP. Esempio:



nome: cognome:


...
<form action="provaform.php" method="GET">
nome: <input type="text" name="nome">
cognome: <input type="text" name="cognome">
<input type="submit" value="Visualizza">
</form>
...

La richiesta HTTP sarà:

GET /~sassi/LIA2007/provaform.php?nome=Giovanni&cognome=Rossi HTTP/1.1
Host: www.dti.unimi.it

Lato server, il nostro script può accedere ai valori dei controlli inviati dall'utente tramite la variabile $_GET, che viene popolata automaticamente dal modulo PHP (molto comodo!). Nell'esempio $_GET["nome"] conterrà il valore "Giovanni" e $_GET["cognome"] conterrà il valore "Rossi"

Se invece la La richiesta HTTP fosse stata:

...
<form action="provaform.php" method="POST">
nome: <input type="text" name="nome">
cognome: <input type="text" name="cognome">
<input type="submit" value="Visualizza">
</form>
...

La richiesta HTTP sarebbe diventata:

POST /~sassi/LIA2007/provaform.php HTTP/1.0
Host: www.dti.unimi.it
Content-Type: application/x-www-form-urlencoded
Content-Length: 27

nome=Giovanni&cognome=Rossi

Content-Length è il numero di caratteri contenuti nel corpo della richiesta, mentre Content-Type è il tipo di codifica effettuata. Lato server, il nostro script può accedere ai valori dei controlli inviati dall'utente tramite la variabile $_POST, anch'essa popolata automaticamente dal modulo PHP. Nell'esempio $_POST["nome"] conterrà il valore "Giovanni" e $_POST["cognome"] conterrà il valore "Rossi"




Esempio 4B (uso avanzato dei cookie)

Utilizzo (leggermente) più avanzato nell'uso dei cookie (escookie1.php).
L'utente si collega alla pagina www.dti.unimi.it/~sassi/LIA2007/escookie1.php:
<html>
<head>
<title>Gazzetta di Crema</title>
</head>
<body>

<?php
if( !isset($_COOKIE['idUtente']) || 
    (($_COOKIE['idUtente']!=='1') && ($_COOKIE['idUtente']!=='2')) )  
{
    print "<h2>Benvenuto nella Gazzetta di Crema</h2>\n";
    print "Per accedere devi registrarti\n";
    print "<br />\n";
    print "<br />\n";
    print "Per cortesia, inserisci i tuoi dati:\n";
    print "<form action=\"escookie2.php\" method=\"get\">\n";
    print "Nome: \n";
    print "<input type=\"text\" name=\"nome\" />\n";
    print "<br />\n";
    print "Cognome: \n";
    print "<input type=\"text\" name=\"cognome\" />\n";
    print "<br />\n";
    print "<input type=\"submit\" value=\"Invia\">\n";
    print "</form>\n";
}
else
{
?>

<h2>Benvenuto nella Gazzetta di Crema</h2>
<p>ID dell'utente connesso: <font color="#FF0000">
<?php print $_COOKIE['idUtente']; ?>
</font>
(utente autorizzato a leggere <?php print $_COOKIE['articoliPagati']; ?> 
articoli gratuitamente entro un ora)
<br />
<br />
NOTIZIE
<br />
<br />
BLA, BLA, ...

<?php
// Qui potreste permettere all'utente di visualizzare gli articoli per cui
// è autorizzato, etc..
}
?>

</body>
</html>
L'action del form punta al file www.dti.unimi.it/~sassi/LIA2007/escookie2.php che provvederà a richiedere il cookie:
<?php
if( !isset($_COOKIE['idUtente']) )
{
    if( ($_GET['nome']=='Roberto') AND ($_GET['cognome']=='Sassi') ) {
    $ID=1;
    $articoli=5;
  }
  elseif( ($_GET['nome']=='Mario') AND ($_GET['cognome']=='Bianchi') ) {
    $ID=2;
    $articoli=3;
  }
  else {
    $ID=0;
    $articoli=0;
  }

    // I cookie verranno cancellati dal client dopo 3600 secondi = 1 ora
    setcookie("idUtente", $ID, time()+3600);
    setcookie("articoliPagati", $articoli, time()+3600);
}
    // rimanda il browser alla pagina di partenza
  header("Location: http://" . $_SERVER['HTTP_HOST'] . 
    dirname($_SERVER['PHP_SELF']) . "/escookie1.php");
?>
escookie2.php produce la seguente risposta del server (attenzione: è sempre il server a richiedere il cookie):
HTTP/1.x 302 Found
Date: Fri, 06 Dec 2007 11:26:09 GMT
Server: Apache/2.0.54 (Debian GNU/Linux) PHP/4.3.10-16 mod_ssl/2.0.54 [...]
X-Powered-By: PHP/4.3.10-16
Set-Cookie: idUtente=1; expires=Fri, 06 Dec 2007 12:26:09 GMT
Set-Cookie: articoliPagati=5; expires=Fri, 06 Dec 2007 12:26:09 GMT
Location: http://www.dti.unimi.it/~sassi/LIA2007/escookie1.php
Keep-Alive: timeout=15, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: text/html; charset=iso-8859-1
La risposta del server è "302 Found" (non "200 OK") e chiede al client una redirezione temporanea (nel senso che eventuali richieste future a questo indirizzo non devono necessariamente essere redirette) al sito indicato nell'header HTTP da "Location: ..." (lo avevamo impostato tramite il comando PHP header()).
Nell'header della risposta HTTP, anche in questo caso troviamo la richiesta al client di creare un cookie. Il client, accetta il cookie e lo scrive sul suo disco. Nel caso di Firefox, aggiunge due righe al file cookie.txt
# HTTP Cookie File
# http://www.netscape.com/newsref/std/cookie_spec.html
# This is a generated file!  Do not edit.
# To delete cookies, use the Cookie Manager.

www.dti.unimi.it FALSE /~sassi/LIA2007/ FALSE 1133526791 articoliPagati 5
www.dti.unimi.it FALSE /~sassi/LIA2007/ FALSE 1133526791 idUtente 1
... (altri cookies)

Da adesso in poi, il client, ad ogni richiesta a www.dti.unimi.it/~sassi/LIA2007/... aggiugerà il valore dei cookies:

GET /~sassi/LIA2007/escookie1.php HTTP/1.1
Host: www.dti.unimi.it
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; it-IT; rv:1.7.12) [...]
Accept: text/xml,application/xml,application/xhtml+xml,text/html; [...]
Accept-Language: it,it-it;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Referer: http://www.dti.unimi.it/~sassi/LIA2007/escookie1.php
Cookie: idUtente=1; articoliPagati=5

PHP e SESSIONI

Abbiamo visto che HTTP è un protocollo senza memoria di stato e, di sua natura, ogni coppia "domanda del client"/"risposta del server" è indipendente dalle altre.

Una SESSIONE HTTP è semplicemente una serie di domande/risposte effettuate da un client ad un server che condividono uno stato (condividono informazioni che persistono tra una connessione e le successive).

Inoltre, abbiamo visto come esistano sostanzialmente tre metodi per creare persistenza tra diverse coppie di richiesta/risposta HTTP (si veda la lezione 15). Dei tre metodi, quello che utilizza i cookie è sostanzialmente il più efficace. Nel precedente esempio 4B e negli esempi delle scorse lezioni, abbiamo infatti utilizzato i cookie per creare uno stato tra le diverse connessioni. Nell'esempio della lezione scorsa, lo stato era una semplice variabile che conteggiava il numero delle volte che si era visitata una certa pagina. Nell'esempio visto oggi, lo stato era il nome dell'utente contenuto in un cookie. Quindi, costituiscono degli esempio di sessione HTTP.

PROBLEMA!: per applicazioni in cui la sicurezza è critica il cookie ha un punto debole: le informazioni che si vogliono conservare tra una pagina e l'altra vengono salvate in un file sul disco del client e sono quindi modificabili dall'utente.

Ad esempio, utilizzando PuttyTel, se inviamo questa richiesta:

GET /~sassi/LIA2007/escookie1.php HTTP/1.1
Host: www.dti.unimi.it
Cookie: idUtente=2; articoliPagati=100

il server ci scambia per un utente autenticato e inoltre ci permette di leggere 100 articoli gratuitamente!

Per tentare una soluzione a questo problema vengono utilizzate contemporaneamente tre strategie:

  1. si utilizza un valore di codice identificativo molto difficile da indovinare (non "1" ma "H6K7DHDIE737462BSTG37");
  2. i dati sensibili vengono memorizzati sul disco del server (quindi NON li metto in un cookie alla balia dell'utente) associandoli al codice identificativo dell'utente;
  3. il cookie che contiene il codice identificativo viene impostato a durata temporale 0, cioè verrà eliminato chiedendo il browser.

Se ci pensate bene è proprio ciò che succede quando telefoniamo al telefono di un call center: 1) ci viene dato un codice intervento e 2) sul computer del call center vengono registrati i dati! Le tre strategie vengono implementate in PHP dal meccanismo delle "sessions" (SESSIONI PHP). Un esempio (essessione1.php):

<?php
session_start();

if( !isset($_SESSION['numero_visite'])  )
{
    $_SESSION['numero_visite']=1;
}
else
{
    $_SESSION['numero_visite']++;
}
?>

<html>
<head>
<title>Titolo</title>
</head>
<body>

Il numero di volte che ti sei connesso a questa pagina è
<b><?php print $_SESSION['numero_visite']; ?></b>.

</body>
</html>

Tutti gli script che vogliono accedere alla sessione devono iniziare con la funzione session_start(). Come funziona?:

  1. se $_COOKIE non contiene la variabile PHPSESSID (la sessione non è ancora cominciata), session_start() genera PHPSESSID, un codice di 32 cifre, e aggiunge nella risposta la richiesta di un cookie che lo contenga:
    HTTP/1.x 200 OK
    Date: Mon, 10 May 2004 08:07:29 GMT
    Server: Apache/1.3.26 (Unix) Debian GNU/Linux PHP/4.1.2 mod_ssl/2.8.9 OpenSSL/0.9.6g mod_perl/1.26
    X-Powered-By: PHP/4.1.2
    Set-Cookie: PHPSESSID=42b93d2d874a2f169896583a9e4d85db; path=/
    [...]
    
    Inoltre crea sul server un file dove verranno salvate le variabili da conservare per tutta la durata della sessione. Il nome del file contiene il valore di PHPSESSID. As esempio, viene creato il file sess_42b93d2d874a2f169896583a9e4d85db.

  2. se $_COOKIE contiene la variabile PHPSESSID (la sessione è già cominciata), legge il file sess_... e copia nell'array associativo $_SESSION tutte le variabili contenute nel file.
    Ad esempio ecco la risposta del client dopo che la sessione è cominciata:
    GET /~sassi/LIA2007/essessione1.php HTTP/1.1
    Host: www.dti.unimi.it
    [...]
    Cookie: PHPSESSID=42b93d2d874a2f169896583a9e4d85db
    [...]
    
    mentre il contenuto del file di sessione sess_42b93d2d874a2f169896583a9e4d85db è
    numero_visite|i:5;
    
    La variabile $_SESSION['numero_visite'] conterrà il valore 5.

Per fare in modo che il valore di una variabile sia disponibile per tutta la sessione, basta aggiungere un elemento all'array $_SESSION. I dati vengono automaticamente salvati nel file di sessione.

Quando si decide di chiudere una sessione:

// elimina tutte le variabili dalla sessione
session_unset();
// chiude la sessione
session_destroy();

La funzione session_start() fa quindi cose diverse a seconda che trovi o meno un file con il nome corrispondente al cookie PHPSESSID ricevuto.








Le sessioni NON sono un modo per autenticare l'utente, ma solo per capire che le richieste vengono da uno stesso utente (che eventualmente abbiamo autenticato).

PHP II

In questa breve sezione aumentiamo la conoscenza di base del PHP

Funzioni

Come in C, anche in PHP è possibile costruire delle funzioni, che ritornino o meno dei valori. La sintassi generale è

function nome_della_funzione($parametro1, $parametro2)
{
	// corpo della funzione
	return $valore_restituito;
}
Anche per le funzioni, non è necessario specificare né il tipo dei parametri, né il tipo del valore che viene restituito.
Non è necessario ma è buona abitudine dichiarare le funzioni all'inizio del file PHP: aumenta la leggibilità.
Esempio: funzione che calcola il fattoriale di un numero

<?php
function fattoriale($n)
{
    $risultato=1;
    for($i=2; $i<=$n; $i++)
        $risultato*=$i;
    return $risultato;
}
?>

<html>
<head>
<title>Fattoriale di un numero</title>
</head>
<body>

<?php
if( isset($_GET['numero']) )
{
    print "Il fattoriale di {$_GET['numero']} è " . fattoriale($_GET['numero']);
}
else
{
?>

<form action="<?php print $_SERVER['PHP_SELF']?>", method="get">
Calcola il fattoriale di
<input type="text" name="numero">
<input type="submit" value="Calcola!">
</form>

<?php
}
?>

</body>
</html>

I parametri sono passati per valore. Se si desidera che un parametro sia passato per riferimento, nella dichiarazione della funzione bisogna anteporre l'operatore & al nome della variabile.
Ne stiamo passando il puntatore? Di certo qualcosa di simile, ma poco ci importa. L'importante, al di là dei dettagli implementativi, è che il valore della variabile viene effettivamente modificato.
ATTENZIONE: l'operatore & va usato SOLO nella dichiarazione della funzione.
Esempio: il fattoriale di un numero calcolato attraverso di una provedura:

<?php
function fattoriale($n, &$risultato)
{
    $risultato=1;
    for($i=2; $i<=$n; $i++)
        $risultato*=$i;
    return $risultato;
}
?>

[...]

<?php
if( isset($_GET['numero']) )
{
    fattoriale($_GET['numero'], $r);
    print "Il fattoriale di {$_GET['numero']} è $r";
}
else
{

[...]
Le funzioni in PHP possono essere ricorsive.

Includere un FILE: include($nome_file)

Immaginate di voler includere un banner e un footer fissi nel vostro sito. Basta salvarli in due files e chiedere al PHP di includerli nella pagina.
Esempio
Dato il file header.inc:
<html>
<head>
<title>Il mio sito</title>
</head>
<body>
<font color="#FF0000"><h2>IL MIO SITO</h2></font>
<hr size="1">
ed file footer.inc:
<hr size="1">
Contatta il <a href="mailto:webmaster@ilmiosito.it">WebMaster</a>!
</body>
</html>
ecco lo script che li include (hf.php):
<?php
include("header.inc");
?>

<br />
<h2>Contenuto del sito</h2>
<br />

<?php
include("footer.inc");
?>

Lab

Come utilizzare easyPHP

Dopo averlo installato, fate partire i vari server (Start->Programmi->EasyPHP->EasyPHP).

Copiate i vostri files nella cartella: C:\Programmi\EasyPHP1-8\www (modificando opportunamente il path in base alla vostra installazione.

Per visualizzare la vostra pagina, cliccate con il tasto destro sull'icona PHP che avete vicino all'orologio. Selezionate "Local Web". Nel browser che vi si apre, aggiungete all'URL il nome della vostra pagina.

Esercizio 1

Preparare lo script memorizzanome1.php che inizia una sessione e, dopo aver chiesto all'utente il nome, lo memorizza nella variabile di sessione "nome". Se "nome" è già contenuto in $_SESSION, presenta all'utente una pagina di saluto contenente il nome dell'utente.
Preparare lo script memorizzanome2.php che legge il valore della variabile di sessione "nome" e presenta all'utente una pagina di benvenuto contenente il nome.
Funzionano entrambi? Chiudete il brower. Apritelo di nuovo e visualizzate memorizzanome2.php. La sessione è ancora aperta? Perché

©2007 Roberto Sassi