6.Php: OOP e PHP con il Browser
13.Programmazione Orientata agli Oggetti

La "OOP": "Object Oriented Programming" (Programmazione orientata agli oggetti) è un paradigma di programmazione che consiste nel modellare un problema definendo degli oggetti che interagiscono fra di loro. Un oggetto è un insieme logico costituito da dati e da funzioni che manipolano i dati stessi e creano l'interfaccia tramite la quale l'oggetto interagisce con gli altri oggetti.

13_1.Le classi

Una classe definisce una categoria di oggetti, aventi tutti le stesse caratteristiche. In effetti, il concetto di classe precede quello di oggetto:

prima penso ad una classe e ne definisco i dati, detti attributi (i quali in realtà sono delle variabili), e le funzioni che li manipolano, dette metodi in seguito istanzio un oggetto a partire dalla classe: possiamo pensare alla classe come ad uno "stampino" per creare oggetti e ad un oggetto come ad una "entità concreta" della classe. Per creare le istanze di ciascun oggetto è comodo utilizzare un costruttore, ovvero una funzione che imposti fin dall'inizio alcuni attributi fondamentali della classe.

Facciamo un esempio.
La classe "cane" con tutte le sue caratteristiche, cioè gli attributi (per es. la razza, il colore del manto). L'oggetto "Lassie" è un'istanza della classe cane in cui l'attributo razza è Collie, il colore del manto è bianco e marrone, ecc....
Per creare una classe in PHP è sufficiente utilizzare la sintassi:


class NomeClasse {
  //qui dentro vanno inserite le variabili pubbliche (gli attributi) e private della classe
  //e le funzioni (metodi)
  //questa funzione è il costruttore della classe e si chiama sempre così
  function __construct ($parametri) { 
    //istruzioni...
  }
} //qui finisce la dichiarazione della classe

Il costruttore può essere anche una funzione con lo stesso nome della classe. In questo modo è stata creata una classe NomeClasse. Per crearne una nuova instanza, sarà sufficiente scrivere: $var = new NomeClasse ($parametri_del_costruttore) //instanzia un nuovo oggetto della classe NomeClasse Con questa istruzione creiamo un nuovo oggetto dallo "stampino" NomeClasse; i parametri passati tra parentesi sono quelli richiesti dalla funzione __construct (se prevista). Per accedere agli attributi o ai metodi della classe si userà la sintassi: $var->attributo = $a; //posso leggere o modificare gli attributi della classe $b = $var->esempioMetodo(); //un metodo può restituire un valore Quando si lavora con le classi, la cosa più comoda è creare un file class.NomeClasse.php in cui inserire il codice della classe e poi richiamare tale file negli script in cui si desidera lavorare tramite l'istruzione include_once: include_once("class.NomeClasse.php"); Per accedere all'attributo dall'interno della classe si usa this, per accedervi dall'esterno: "il nome dell'oggetto -> e il nome del metodo":


class prova {
  private $a="mamma";
 
  public function stampa(){
    echo $this->a;
  }
}
 
$b=new prova();
$b->stampa();

Come si vede la funzione stampa accede alla variabile privata a tramite la keyword this e ->, si noti il $ la keyword this diventa variabile e prende il $ che non va applicato al nome della variabile privata! Per accedere al metodo stampa() usiamo: "il nome oggetto -> e il nome metodo". Potremmo accedere anche ad una variabile della classe nello stesso modo (vedi ultima riga):


class prova{
   private $a="mamma";
   public $c="gatto";
 
   public function stampa(){
      echo $this->a;
   }
 
   public function get_var(){
      return $this->a;
   }
}
 
$b=new prova();
$b->stampa();
$variabile_privata=$b->get_var();
echo $b->c;

se invece vogliamo ottenere la variabile privata, il metodo get_var() accede per noi alla variabile privata e ne restituisce il valore, che verrà salvato nella variabile $variabile_privata.

13_2.Ereditarietà

Una classe può estenderne un'altra ereditandone così i metodi e gli attributi. La classe estesa si chiama superclasse e la estendente sottoclasse. La sottoclasse può sovrascrivere (overridding, da non confondere con l'overloading) i metodi della superclasse definendone di propri, per estendere una classe è sufficiente dopo il nome della classe estendente mettere la parola chiave extends ed il nome della classe da estendere. class frutta{ public function visualizza(){ echo "questo è un frutto"; } } class mele extends frutta{ public function visualizza(){ echo "questa è una mela"; } } class pere extends frutta(){ public function visualizza(){ echo "questa è una pera"; } } $fushi=new mele(); $fushi->visualizza(); //scriverà questa è una mela $williams= new pere(); $williams->visualizza();//scriverà questa è una pera $ciquita= new frutta(); $ciquita->visualizza();//visualizzerà questo è un frutto Come si vede il metodo visualizza() è sovrascritto dalle varie sottoclassi: pere e mele. Bisogna fare particolare attenzione con gli indicatori di visibilità.

13_3.Polimorfismo

Nell'esempio sopra abbiamo una chiara dimostrazione di polimorfismo, il metodo visualizza() è chiamato più volte ma ogni volta da un risultato differente a seconda che l'oggetto appartenga ad una od un'altra classe. Se richiamo visualizza() con fushi che è un oggetto della classe mele, ecco che visualizzerà "questa è una mela", mentre se lo richiamo con williams. visualizzerà "questa è una pera", il polimorfismo è quindi la capacità di uno stesso metodo di eseguire compiti differenti in base alla classe dell'oggetto di appartenenza, infatti il metodo richiamato è sempre visualizza() ma si comporterà in modo diverso a seconda della classe dell'oggetto.

13_4.Ambito

Abbiamo detto che un attributo non è altro che una variabile della classe; bisogna tuttavia fare attenzione all'ambito dell'attributo, ovvero alla visibilità che ha l'attributo dall'esterno. public rende l'attributo o il metodo visibile e utilizzabile anche dall'esterno della classe protected l'attributo o il metodo possono essere visti/usati solo dall'interno della classe o da una sottoclasse private l'attributo o il metodo può essere visto/usato solo all'interno della classe. Se si usa la dichiarazione var, senza specificare l'ambito dell'attributo, questo avrà come ambito per default pubblico esempio: <?php class prova{ private $priv="ciao"; protected $prot="miau"; public $publ="bau"; var $var="fuffi"; function stampa(){ echo "dalla funzione stampa accedo alla varibile privata= " . $this->priv . "<br>"; } } class altro extends prova{ function ristampa(){ echo "dalla class estendente accedo alle variabili protected della classe estesa= " . $this->prot . "<br>"; } } $a=new prova(); //echo "a priv " . $a->priv . "<br>"; //echo "a prot " . $a->prot . "<br>"; echo "a publ " . $a->publ . "<br>"; echo "var= " . $a->var . "<br>"; $a->stampa(); $b=new altro(); //echo "b priv " . $b->priv . "<br>"; //echo "b prot " . $b->prot . "<br>"; echo "b publ " . $b->publ . "<br>"; $b->ristampa(); ?>

13_5.Incapsulamento

Si definisce incapsulamento la tecnica di rendere invisibile ed inaccessibile parti di codice non necessario per l'utilizzo di una classe, rendendo accessibili e visibili solo alcuni metodi e alcuni attributi. Si ottiene grazie agli indicatori di visibilità e alle interfacce.

13_6.Interfacce

Le interfacce Vengono definite tramite la keyword interface seguita dal nome voluto per l'interfaccia e le graffe. interface miainterfaccia{ public function setName($name); public function getName(); } Una classe che implementi tale interfaccia deve ridefinire tutti i metodi obbligatoriamente, vediamo l'implementazione: class miaclasse implements miainterfaccia{ public $name; public function setName($name){ $this->name=$name; } public function getName(){ return $this->name; } } Deve ridefinire tutti i metodi preservando la signature tranne per l'ambito che può essere ristretto.

14.Integrazione con il browser

PHP è un linguaggio di scripting che vive in un contenitore che si trova a sua volta all'interno di un server Web, l'ambiente con il quale questo deve comunicare per poter interagire con l'utente è pertanto il browser Web.

Il browser, infatti, si occupa della visualizzazione del codice HTML prodotto dagli script PHP che vengono eseguiti sul server ed è sempre lui che si occupa di spedire delle richieste personalizzate ad altri script PHP con l'obiettivo di ottenere elaborazioni complesse.

Si pensi per esempio a un form HTML: si tratta di un'interfaccia complessa che deve essere gestita completamente dal browser, il quale dovrà comporre la richiesta HTTP e a spedirla sul server allo script di destinazione passando anche tutti i parametri che a questo servono per completare il suo compito.

Vedremo quali sono le modalità classiche di interazione tra il linguaggio PHP e il browser che tratta richieste spedite al motore di scripting e le risposte che da questo vengono generate.

14_1.Scrittura sul browser

L'obiettivo principale di qualsiasi script PHP è la produzione dinamica di una pagina Web; pertanto deve essere possibile scrivere all'interno della response HTTP e, in funzione di condizioni applicative, decidere che cosa deve comparire nel codice HTML prodotto sul server e spedito al browser.

L'istruzione che si utilizza più spesso per scrivere all'interno di una pagina Web è la seguente:

print(stringa);

Utilizzando questa istruzione il parametro stringa può contenere senza alcun problema il codice HTML necessario a impaginare correttamente l'output, per esempio l'istruzione:

print("<b>Riga in grassetto</b><br>\n");

produce un output in carattere grassetto corredato anche dal tag <br> che permette di andare a capo nella pagina HTML. La presenza, invece, del carattere di new-line \n non ha impatti sull'impaginazione del codice prodotto, ma consente di scrivere un codice HTML più pulito perché permette di andare a capo e quindi di non produrre codice HTML tutto su una singola riga, ma di spezzettarlo su più righe.

Vediamo un esempio:

<html>
   <head>
      <title>Scrivere sul browser</title>
   </head>
   <body>
      <?php
         $a = getallheaders();
         print("<b>Variabili d'ambiente:</b><br><br>");
         foreach ($a as $k => $v)
         {
            print "<b>$k</b>: $v<br>";
         }
      ?>
   </body>
</html>

L'obiettivo di questo script, è di scrivere sul browser tutte le variabili d'ambiente della comunicazione HTTP che intercorre tra client e server.

A questo scopo si utilizza la peculiarità di PHP di trasformare l'intero set di variabili d'ambiente in un array interno del motore PHP stesso. Per accedere a questo array viene utilizzata la funzione getallheaders che restituisce, appunto, un array contenente il valore di tutte le variabili d'ambiente della comunicazione HTTP.

Nel caso in cui si intenda leggere il valore di una singola variabile si può interrogare direttamente l'array passandogli la chiave che rappresenta la variabile d'ambiente che ci interessa.

Per accedere, per esempio, alle informazioni su browser che effettua la chiamata HTTP si usa la sintassi $_SERVER['HTTP_USER_AGENT'].

Da qualsiasi script, infatti, è sufficiente utilizzare la sintassi $_SERVER[chiave].

Esistono anche altri array che sono messi a disposizione gratuitamente dal motore PHP, in particolare si tratta di GLOBALS, _GET, _POST, _COOKIE, _SERVER e _REQUEST.

14_2.Lettura delle informazioni dal browser

In ogni applicazione Web, come sappiamo, accade spesso di dover riempire dei form HTML per eseguire operazioni sul server.

Vediamo, in questo esempio, come si comporta il motore PHP quando deve leggere le informazioni inviate da un modulo HTML attraverso i metodi GET e POST.

Innanzitutto costruiamo un form HTML dedicato alla raccolta delle informazioni. Ecco lo script:

<html>
   <head>
      <title>Spedire le informazioni al browser</title>
   </head>
   <body>
      <form action="7-3.php">
      <h3>Inserisci il tuo nome
      <input type="text" name="nome" size="20" maxlength="20"></h3>
      <h3>Inserisci il tuo cognome
      <input type="text" name="cognome" size="20" maxlength="20"></h3>
      <h3>Data di nascita
      <input type="text" name="data" size="20" maxlength="20"></h3>
      <h3>Sesso: uomo <input type="radio" name="sesso" value="m">
      donna <input type="radio" name="sesso" value="f"></h3>
      <h3>Luogo di nascita
      <select name="luogo">
         <option value="Torino" SELECTED>Torino</option>
         <option value="Milano">Milano</option>
         <option value="Genova">Genova</option>
         <option value="Palermo">Palermo</option>
         <option value="Padova">Padova</option>
      </select></h3>
      <input type="submit" value="Invia">
      </form>
   </body>
</html>

Il risultato di questo script, è un semplice form HTML pronto per spedire le informazioni inserite dall'utente allo script indicato all'interno del parametro action.

Per raccogliere, da uno script PHP, le informazioni ricevute all'interno della request HTTP si utilizza un array che il motore PHP mette gratuitamente a disposizione: l'array $_REQUEST.

Proviamo a creare uno script che cicli all'interno di tutto l'array $_REQUEST e che stampi il contenuto delle chiavi e dei valori associati:

<html>
   <head>
      <title>Leggere le informazioni HTTP</title>
   </head>
   <body>
      <?php
         $a = $_REQUEST;
         print("<b>Parametri del form HTML:</b><br><br>");
         foreach ($a as $k => $v)
         {
            print "<b>$k</b>: $v<br>";
         }
      ?>
   </body>
</html>

L'output di questo script dimostra come nell'array $_REQUEST siano presenti tutti e solo i valori dei parametri che sono stati passati dalla pagina precedente attraverso la request.

Normalmente però i parametri della request vengono utilizzati attraverso la loro chiave per ottenerne il valore; vediamo quindi un esempio di script che, conoscendo i nomi delle chiavi, recupera dalla request i valori associati e li utilizza.

<html>
   <head>
      <title>Recuperare i valori in un form</title>
   </head>
   <body>
      <?php
         print("<b>Scheda cliente</b><br><br>");
         print("Nome: " . $_REQUEST['nome'] . " Cognome " . $_REQUEST['nome'] . "<br>");
         print("Sesso: " . ($_REQUEST['sesso']=="m"?"Maschio":"Femmina") . "<br>");
         print("Nato il: " . $_REQUEST['data'] . " a " . $_REQUEST['luogo'] );
      ?>
   </body>
</html>

Come si può vedere dall'output generato dall'esecuzione di questo script, è possibile recuperare i valori dei parametri passati da un form, sia attraverso il metodo GET sia attraverso il metodo POST, utilizzando la sintassi:

$_REQUEST['nomeparametro']

Questa forma restituisce il valore del parametro il cui nome è nomeparametro e consente quindi di recuperare dalla request tutte le informazioni che servono.

14_3.Passaggio di array come parametri

Negli esempi precedenti abbiamo visto come sia possibile passare semplici valori come parametri di un form HTML e recuperarli attraverso uno script PHP.

Bene, il motore PHP permette anche di recuperare dalla request HTTP anche interi array. Vediamo il seguente esempio:

<html>
   <head>
      <title>Recuperare un array dalla request</title>
   </head>
   <body>
   <?php
   if(isset($_REQUEST['colori']))
   {
      print("<h3>I colori dell'ultima maglia sono:</h3>\n");
      print("<ul>\n");
      foreach($_REQUEST['colori'] as $colori)
      {
         print("<li>$colori</li>\n");
      }
      print("</ul>\n");
   }

   $colori = array(
      "Rosso", "Giallo",
      "Verde", "Blu",
      "Arancio", "Nero");

   print("<h3>Colora la tua maglia!</h3>\n");
   print("<form method=\"POST\" action=\"{$_SERVER['PHP_SELF']}\">\n");

   foreach($colori as $c)
   {
      print("<input type=\"checkbox\" " .
            "name=\"colori[]\" value=\"$c\">" . "$c\n");
   }
   print("<br>");
   ?>
   <input type="submit" value="Colora!">
   </form>
   </body>
</html>

Questo è uno script un po' particolare, poiché è composto sia dal form HTML che raccoglie le informazioni sia dalla pagina dei risultati.

La discriminante è data dall'esistenza in memoria dell'oggetto $_REQUEST['colori'].

Vediamone in dettaglio il comportamento.

Alla prima esecuzione, l'istruzione condizionale if(isset($_REQUEST['colori'])) restituirà il valore FALSE in quanto in quel punto non esiste in memoria l'oggetto $_REQUEST['colori']. L'esistenza di questo oggetto si ha soltanto se a questo script si è arrivati attraverso un form che abbia passato nella request un valore per la chiave 'colori', ma essendo la prima volta che lo script viene chiamato è chiaro che questo oggetto non esiste.

La ricerca di un oggetto, o di una variabile, in memoria viene effettuata attraverso l'utilizzo della funzione isset che, come è facile intuire, restituisce TRUE se l'oggetto esiste e FALSE in caso contrario.

Poiché siamo nel caso di non esistenza dell'oggetto in memoria, quindi, la porzione di script racchiusa tra le parentesi graffe viene saltata e si passa immediatamente alla porzione successiva.

Da qui in poi lo script definisce un array contenente i nomi di alcuni colori e li utilizza per creare dinamicamente gli elementi di un form HTML, in particolare vengono creati dei checkbox il cui valore è il nome del colore contenuto nell'array per quella posizione del ciclo, mentre il nome è uguale per tutti: si tratta di colori[].

È interessante notare che il nome dell'elemento di input non identifica una singola stringa, ma si tratta questa volta di un array di stringhe. Questo significa che quando lo script verrà eseguito verrà passato allo script di destinazione un array colori[] contenente tutti e solo i valori che l'utente avrà selezionato nel form.

Il parametro action del form HTML viene valorizzato in questo modo: $_SERVER['PHP_SELF'].

L'oggetto $_SERVER è un array interno di PHP che consente in ogni momento di avere varie informazioni sulla request e sulla response HTTP, comprese informazioni legate alla comunicazione tra client e server. Pertanto, la sintassi che abbiamo visto restituisce il nome della pagina che ha effettuato la request HTTP, cioè il nome stesso dello script. In questo caso, quindi, lo script richiama se stesso ogni volta che si effettua un submit del form.

Che cosa otteniamo se selezioniamo alcuni colori e premiamo il pulsante di submit?

Ecco quello che accade: lo script richiama se stesso passando come parametri nella request tutti gli elementi del form, e quindi anche l'array colori[] con tutti e solo i valori che sono stati selezionati dall'utente. Pertanto, quando si arriva alla porzione di codice vincolata dalla condizione if(isset($_REQUEST['colori'])) che abbiamo visto in precedenza, il risultato della condizione sarà TRUE e quindi la porzione di codice verrà eseguita scandendo l'array e stampandone tutto il contenuto un elemento alla volta.

Quello che verrà stampato, nel caso specifico, è l'elenco di tutti i colori che l'utente ha selezionato per la sua maglietta.

Successivamente lo script proseguirà ricreando il form di selezione dei colori visto in precedenza.

Come si può vedere quello che abbiamo portato dal form di inserimento dati allo script di esecuzione è un completo array che può essere scandito con i metodi classici che il motore PHP mette a disposizione.

Un altro elemento che possiamo notare di questo script è l'utilizzo, all'interno del form HTML, del metodo POST al posto del metodo GET. Questo è stato fatto solo per far vedere che, indipendentemente dal metodo utilizzato, la trasmissione di parametri sulla request HTTP funziona sempre allo stesso modo.

14_4.Accedere ai file

Il motore PHP gestisce nativamente tutta la struttura applicativa per permettere all'utente la funzionalità di upload di file sul server via HTTP.

La prima operazione da eseguire, quando si vuole effettuare un upload via HTTP, è creare un form di inserimento dati che permetta di selezionare il file sul file system della macchina client. Vediamo questo form HTML:

<html>
   <head>
      <title>Selezionare un file da spedire al server</title>
   </head>
   <body>
   <h3>Seleziona il file da spedire al server</h3>
   <form enctype="multipart/form-data"
      action="7-8.php" method="post">
      <input type="hidden" name="MAX_FILE_SIZE" value="1024000">
      <input name="file_name" type="file">
      <input type="submit" value="Spedisci"">
   </form>
   </body>
</html>

Si tratta, come vedete, di una semplice pagina HTML con un form che contiene un campo di input che ha l'attributo type settato sul valore "file".

Il form stesso non può essere un form qualsiasi, ma il metodo deve essere obbligatoriamente post ed è necessario specificare sempre l'attributo:

enctype="multipart/form-data"

che identifica il metodo di codifica dell'informazione, cioè dei parametri che vengono passati tra client e server.

Come si può vedere, un campo con l'attributo type="file" causa la comparsa di un pulsante "Sfoglia…" o "Browse…" accanto al campo di testo che conterrà il nome del file. Questo pulsante aggiuntivo permette di selezionare, sul file system del client, un file da spedire al server.

Una volta selezionato il file si può premere il pulsante di submit: si otterrà il trasferimento del file allo script chiamante, che però dovrà essere in grado di riceverlo e di trattarlo. Vediamone un esempio:

<html>
   <head>
      <title>Memorizzazione del file sul server</title>
   </head>
   <body>
   <h3>Informazioni sul file trasferito:</h3>
   <?php
      if($_FILES['file_name']['error'] != UPLOAD_ERR_OK)
      {
         print("C'è stato un errore nel trasferimento<br>\n");
      }
      else
      {
         copy($_FILES['file_name']['tmp_name'],
              ".\\".$_FILES['file_name']['name']);
         unlink($_FILES['file_name']['tmp_name']);
         print("Nome del file temporaneo: " .
            $_FILES['file_name']['tmp_name'] . "<br>\n");
         print("Nome del file trasferito: " .
            $_FILES['file_name']['name'] . "<br>\n");
         print("Dimensione: " .
            $_FILES['file_name']['size'] . "<br>\n");
         print("Tipo: " .
            $_FILES['file_name']['type'] . "<br>\n");
         print("<hr>\n");
      }
   ?>
   </body>
</html>
</html>

Il motore PHP memorizza in un array multidimensionale tutte le informazioni sui file trasferiti.

L'array si chiama $_FILES e si usa in questo modo:

$_FILES[nome_file_trasferito][parametro]

dove nome_file_trasferito è il valore del campo di input nel form HTML che ha spedito il file sul server e parametro può essere uno dei seguenti valori:

  • name: il nome fisico del file che stiamo trasferendo;
  • type: il tipo mime del file;
  • tmp_name: il nome, completo di percorso, del file temporaneo che è stato generato sul server;
  • size: la dimensione in byte del file trasferito;
  • error: valore booleano che vale TRUE se c'è stato un errore nel trasferimento.

La prima operazione da compiere, quindi, in uno script che voglia ricevere un file per memorizzarlo sul server, è un test sulla condizione di errore per verificare che il file sia stato trasferito. Successivamente, se non ci sono stati errori, lo script si ritroverà già il file nella posizione temporanea e non dovrà fare altro che spostarlo in una posizione a lui più utile.

In particolare, il nostro script effettua una copia, con la funzione copy, del file nella stessa posizione dove si trova egli stesso e successivamente elimina il file temporaneo utilizzando la funzione unlink.

A questo punto vengono stampate le informazioni sul file che sono presenti nell'array $_FILES.

L'output prodotto da questo script dopo il trasferimento del file logo.gif. Come si può vedere non ci sono stati errori e sono presenti tutte le informazioni sul file che è stato trasferito.

14_5.Controllo delle sessioni

Il protocollo HTTP è stateless, cioè non supporta il controllo dello stato tra una chiamata e la successiva: per ovviare a questa limitazione si utilizzano le sessioni.

Quando un client HTTP (per esempio un browser) effettua una chiamata a un server, questa vive di vita propria e il server non è in grado di sapere se esiste una qualche relazione tra questa ed eventuali chiamate precedenti o successive.

Nel contesto di siti Web statici questo potrebbe non essere un problema: il client effettua una request e il server risponde con una response, nel pieno rispetto delle specifiche del protocollo.

Nel momento in cui, però, sia necessario collegare due request differenti, allora iniziano a sorgere i problemi.

Pensiamo per esempio a un sito di commercio elettronico, dove il contesto vede l'utente effettuare il login, riempire il carrello, inserire i dati della carta di credito e confermare l'ordine.

In un caso come questo è essenziale che il server HTTP sia in grado di capire che la request effettuata da un certo client è associabile a un'altra, cioè che si tratta esattamente dello stesso client.

In altre parole il server deve essere in grado di capire che i dati della carta di credito che sono stati inviati con una request si riferiscono a una certa lista di prodotti che è stata selezionata e inviata in precedenza.

Il punto d'unione tra le request è ovviamente il client, cioè il browser.

Altre discriminanti, alcune già presenti come proprietà della request stessa, come il tipo del browser, l'indirizzo IP del chiamante ecc. non sono sufficienti a distinguere univocamente il client, pertanto è necessario utilizzare strumenti diversi.

L'utilizzo delle sessioni permette di associare, lato server, chiamate che arrivino da uno stesso singolo client, legandole addirittura al pid del processo del client sulla macchina che lo ospita: questo garantisce unicità e innumerevoli possibilità.

La gestione delle sessioni è stata aggiunta nel motore PHP a partire dalla versione 4, ma è soltanto nella versione 5 che si può parlare di un totale e completo controllo della sessione.

Il motore PHP memorizza tutti i dati presenti nella sessione in un array che viene riportato tra una request e la successiva. Il meccanismo che permette di associare l'array al singolo client è basato sul principio della doppia memorizzazione.

Dal punto di vista del server viene generato un numero univoco che permette di indicizzare l'array: questo numero viene utilizzato per generare una chiave di accesso alle informazioni dell'array stesso e questa coppia chiave-array viene memorizzata sul file system del server.

Dal punto di vista del client, una volta ottenuto dal server il numero univoco identificativo, è tenuto a memorizzarlo in un cookie per poterlo rendere al server alla request successiva, in modo che il server possa avere una chiave di collegamento tra il client e le informazioni di sessione.

Nel caso in cui il client non sia abilitato a ricevere cookies, il motore PHP è in grado di accorgersene e di cambiare strategia di comunicazione consentendo al client di comunicare il numero univoco appendendolo alla querystring oppure aggiungendolo ai parametri passati nei form.

Vediamo un esempio di script che utilizza il controllo della sessione:

<?php
session_start();

if(isset($_REQUEST['inputName']))
{
$_SESSION['Name'] = $_REQUEST['inputName'];
}
?>
<html>
   <head>
      <title>Controllo della sessione</title>
   </head>
   <body>
<?php
if(isset($_SESSION['Name']))
{
print("<h3>Benvenuto, {$_SESSION['Name']}!</h3>\n");
}
      else
{
print("<h3>Benvenuto, inserisci il tuo nome!</h3>\n");
    print("<form action=\"{$_SERVER['PHP_SELF']}\" " .
"method=\"post\"> <input type=\"text\" name=\"inputName\" " .
"value=\"\">  <input type=\"submit\" value=\"salva\"><br>\n" .
"</form>");
}

    print("<h3>Informazioni sulla sessione:</h3>\n");

print("Name: " . session_name() . "<br>\n");
print("ID: " . session_id() . "<br>\n");
print("Module Name: " . session_module_name() . "<br>\n");
print("Save Path: " . session_save_path() . "<br>\n");
print("Encoded Session:" . session_encode() . "<br>\n");

?>
   </body>
</html>
</html>

Analizziamo con attenzione questo script PHP, che cosa accade quando eseguiamo per la prima volta questo script.

La prima operazione che viene eseguita è la chiamata alla funzione session_start() che si occupa di inizializzare la sessione sul server e sul client.

Successivamente viene testata la presenza dell'oggetto $_REQUEST['inputName'] nell'array della request. La prima volta che questo script viene eseguito naturalmente la condizione restituisce FALSE, quindi il codice contenuto nel blocco non viene eseguito.

Successivamente viene testata la presenza dell'oggetto $_SESSION['Name'] nell'array che contiene tutte le informazioni di sessione, poiché questa informazione non è ancora stata inserita nella sessione viene eseguito il codice corrispondente al blocco else, pertanto viene richiesto l'inserimento del nome dell'utente.

Nella seconda parte dello script vengono lette e stampate tutte le informazioni della sessione, e tra queste il nome della sessione, recuperato attraverso la funzione session_name(), l'identificativo numerico, recuperato attraverso la funzione session_id(), e il valore completo di tutti i contenuti della sessione serializzati, ottenuti attraverso la funzione session_encode().

A questo punto possiamo inserire un nome all'interno del campo di testo e premere il pulsante di submit.

Visto che abbiamo passato il parametro corretto sulla request, questo viene preso e inserito all'interno della sessione e da qui non riusciremmo più a eliminarlo per tanto che tentassimo di ricaricare la pagina Web.

L'informazione è stata inserita all'interno della sessione attraverso l'istruzione:

$_SESSION['Name'] = $_REQUEST['inputName'];

e questa sarà utilizzata da tutte le pagine Web che arrivano da quel server dotato di quel preciso motore PHP e che sono visualizzate in quella specifica istanza del browser.

Tra i vari elementi di discriminazione, infatti, c'è anche l'istanza reale del browser, al cui proposito possiamo tentare un esperimento.

Se apriamo una nuova finestra del browser (premendo per esempio la combinazione di tasti CTRL+n ) otterremo una nuova finestra, legata alla stessa istanza del browser, che condividerà le informazioni di sessione con la finestra precedente.

Se, invece, apriamo una nuova istanza del browser, questa non condividerà le informazioni di sessione con la precedente in quanto si tratta a tutti gli effetti di un nuovo processo. Pertanto, le informazioni pubblicate in una finestra del browser possono essere molto diverse da quelle pubblicate in un'altra, anche se si tratta dello stesso identico url.

Vediamo a questo punto un altro esempio che ci mostri come sia possibile leggere e scrivere informazioni all'interno della sessione:

<?php
session_start();

if(!isset($_SESSION['contatore']))
{
$_SESSION['contatore'] = 1;
}
    else
{
$_SESSION['contatore'] += 1;
}
    
?>
<html>
   <head>
      <title>Leggere e scrivere nella sessione</title>
   </head>
   <body>
<?php
    print("<h3>Un contatore memorizzato in sessione:</h3>\n");

print("Il contatore vale: " . $_SESSION['contatore'] . "<br>\n");

?>
   </body>
</html>
</html>

Questo piccolo esempio, esegue un test per verificare che non esista il valore del contatore in sessione. Se è così il contatore viene inizializzato, in caso contrario viene semplicemente incrementato.

A questo punto, nella parte centrale dello script, è possibile leggere questa informazione e utilizzarla a piacimento, per esempio per stamparla.

La caratteristica interessante di questo script è che utilizzando la funzione di aggiornamento del browser (tipicamente si ottiene premendo il tasto F5), il numero viene costantemente incrementato proprio perché il suo valore si trova all'interno della sessione, una zona di memorizzazione dedicata a contenere tutte le informazioni che si ritiene possano essere utili da condividere tra una request HTTP e l'altra. Per le considerazioni che facevamo prima su istanze e finestre, è chiaro che aprendo una nuova istanza del browser il valore del contatore sarà inizializzato e il contatore stesso riprenderà da capo.

14_6.Include e require

In qualsiasi applicazione Web, all'aumentare della complessità, diventa essenziale organizzare le parti al meglio e prevedere librerie di funzioni e componenti riutilizzabili.

Il motore PHP contiene due funzioni create appositamente per l'inclusione di file, qualunque cosa questi contengano, all'interno di uno script.

Le due funzioni in questione sono include e require. La differenza tra le due funzioni è sostanziale e merita di essere compresa bene per evitare sorprese: la funzione require, la prima volta che viene eseguita, viene sostituita completamente con il file che viene incluso nello script, mentre la funzione include, ogni volta che viene eseguita, richiama il file che intende includere.

Questi due comportamenti hanno delle sottili differenze in quanto, per esempio, se si vuole includere un file all'interno di un ciclo, l'utilizzo della funzione include permette di includere al limite anche file diversi per ogni iterazione, mentre la funzione require, visto che viene sostituita dal file che include, permette l'inclusione di un singolo file.

Vediamo un esempio dell'utilizzo di queste funzioni.

Supponiamo di avere una libreria di funzioni contenuta nel file utils.php e dal seguente contenuto:

<?php
function moltiplicazione($a, $b)
{
   return ($a * $b);
}
?>

A questo punto creiamo un nuovo script che utilizzi questa libreria di funzioni includendo il file della libreria all'interno del codice dello script. Un esempio potrebbe essere il seguente:

<?php
include("utils.php");
?>
<html>
   <head>
      <title>Includere un file</title>
   </head>
   <body>
      <?php
       $valore1 = 10;
       $valore2 = 48;
       print("valore1 = $valore1<br>");
       print("valore2 = $valore2<br>");
       $prodotto = moltiplicazione($valore1, $valore2);
       print("prodotto = $prodotto<br>");
      ?>
   </body>
</html>

Come si può vedere l'utilizzo della funzione include permette di includere, appunto, un file in uno script e, se si tratta di codice PHP, permetterne l'esecuzione.

Un altro utilizzo di queste funzioni di inclusioni consiste nella possibilità di comporre una pagina Web in sezioni e poterle riutilizzare semplicemente includendole negli script principali: questo permette, per esempio, di realizzare portali complessi senza bisogno di riscrivere tutto il codice di componenti comuni come la classica testata o il menu.

Vediamo un esempio di quanto esposto. Supponiamo di avere un file contenente la testata del nostro portale memorizzato nel file testata.php e il motore di generazione del menu nel file menu.php.

A questo punto possiamo pensare di costruire una qualsiasi pagina del nostro portale attraverso l'utilizzo di uno script di questo tipo:

<?php
include("utils.php");
?>
<html>
   <head>
      <title>Comporre un Portale</title>
   </head>
   <body>
   <table border="1" width="100%">
   <tr>
      <td align="center" colspan="2">
         <?php
         include("testata.php");
         ?>
      </td>
   </tr>
   <tr>
      <td height="200" width="150" >
         <?php
         include("menu.php");
         ?>
      </td>
      <td>
      Corpo principale dello script
      </td>
   </tr>
   </table>
   </body>
</html>

In questo caso, si può notare che l'implementazione della testata e del menu è contenuta in un file separato, mentre nella parte principale dello script c'è soltanto quello che riguarda la pagina corrente.

Abbiamo quindi separato l'implementazione delle varie componenti del portale delegandone la produzione a file separati che vengono inclusi dinamicamente in fase di esecuzione.