Le sessioni in PHP

Cosa Sono le Sessioni PHP?

Le sessioni PHP forniscono un modo per memorizzare informazioni tra più richieste in un’applicazione web.
In altri termini le sessioni PHP rendono Stateful una successione di connessioni HTTP che è un protocollo di comunicazione per sua natura Stateless. Una applicazione / protocollo di comunicazione è stateless (senza stato) quando non salva i dati generati in una sessione / connessione per utilizzarli nel corso di quelle successive. Una applicazione / protocollo di comunicazione è stateful quando conserva i dati generati in una sessione / connessione per utilizzarli nel corso di quelle successive.

I dati di sessione in PHP di default vengono memorizzati lato server, e al fine di mantenere un legame / un file conduttore nella sequenza di transazioni HTTP. Solo un ID di sessione viene condiviso con il client, tipicamente tramite un cookie o piu raramente con un parametro URL.

Come Funzionano le Sessioni PHP

  1. Quando una sessione inizia, PHP genera un ID di sessione univoco.
  2. L’ID di sessione viene inviato al client, in un cookie chiamato di default PHPSESSID.
  3. di default PHP memorizza i dati di sessione lato server in un file con il nome dell’ID di sessione, a meno che non venga usato un altro meccanismo di archiviazione. Di conseguenza quando la session inizia PHP crea il file di sessione.
  4. Alle richieste successive, il client invia l’ID di sessione al server, consentendo a PHP di recuperare i dati della sessione.

Uso Base delle Sessioni PHP

<?php
// Avvia la sessione
session_start();

// Memorizza un valore nella sessione
$_SESSION['username'] = 'JohnDoe';

// Recupera e visualizza il valore
if (isset($_SESSION['username'])) {
    echo 'Ciao, ' . $_SESSION['username'];
}

// Distrugge la sessione
session_destroy();
?>

la funzione session_start() di php

La funzione session_start() di PHP avvia una nuova sessione o riprende una sessione esistente per l’utente corrente. È una funzione fondamentale per gestire le sessioni nel contesto delle applicazioni web.

Funzionamento

  1. Inizializza o riprende una sessione: Se non esiste già una sessione, session_start() ne crea una nuova e genera un identificativo univoco di sessione (session ID). Se invece esiste una sessione attiva per l’utente (identificata da un cookie o tramite URL), PHP riprende quella sessione.
  2. Gestione dell’identificativo di sessione (Session ID):
    • Quando si avvia una nuova sessione, PHP genera un identificativo unico (ad esempio abc123) che viene utilizzato per identificare i dati della sessione associati a quell’utente.
    • L’ID della sessione viene inviato al client tramite un cookie (di default chiamato PHPSESSID).
  3. Associazione a dati utente: Dopo aver avviato una sessione, PHP utilizza l’ID della sessione per associare i dati dell’utente ad un array superglobale chiamato $_SESSION.

Esempio di utilizzo

php

// Avvia o riprende una sessione
session_start();
// Aggiunge un valore alla sessione
$_SESSION['username'] = 'utente123';
$_SESSION['role'] = 'admin';
// Recupera i dati della sessione
echo 'Benvenuto, ' . $_SESSION['username']; // Output: Benvenuto, utente123
?>

Dettagli tecnici

  • Archiviazione dei dati di sessione: I dati di sessione sono memorizzati sul server, di default nel filesystem (in una directory come /tmp), e il browser conserva solo l’ID della sessione.
  • Cookie di sessione: Quando viene chiamata session_start(), PHP invia un cookie al client con il nome predefinito PHPSESSID che contiene l’ID della sessione.

    Esempio di intestazione HTTP inviata al client:

    http
    Copia codice
    Set-Cookie: PHPSESSID=abc123; path=/; HttpOnly
  • Ripristino di una sessione esistente: Se il browser invia il cookie con l’ID della sessione, session_start() recupera i dati associati a quell’ID.

Quando usare session_start()

  1. Prima di accedere a $_SESSION: Deve essere sempre chiamata prima di leggere o scrivere dati nell’array $_SESSION.
  2. All’inizio di uno script: Per garantire che la sessione sia inizializzata correttamente prima di accedere ai dati dell’utente.

Esempio errato (causerà un errore):


<?php
$_SESSION['username'] = 'utente123'; // Errore: session_start() non è stato chiamato
?>

Esempio corretto:


<?php
session_start(); // Avvia la sessione
$_SESSION['username'] = 'utente123'; // Ora funziona correttamente
?>

Errori comuni

  1. Sessione già avviata: Se session_start() viene chiamata dopo che l’intestazione HTTP è già stata inviata, genera un errore. Errore tipico:
    css
    Copia codice
    Warning: session_start(): Cannot send session cookie - headers already sent

    Soluzione: Assicurarsi che session_start() sia chiamata all’inizio dello script, prima di qualsiasi output.

  2. Conflitto di sessioni: Utilizzare un nome specifico per la sessione con session_name() per evitare conflitti in applicazioni condivise.

Parametri opzionali

A partire da PHP 7.0, session_start() accetta un array opzionale di opzioni:


session_start([
    
'cookie_lifetime' => 3600, // Durata del cookie in secondi
    
'read_and_close' => true,  // Chiude la sessione subito dopo la lettura
]);

In sintesi la funzione session_start():

  • Crea una nuova sessione o riprende una esistente.
  • Gestisce l’identificativo di sessione (Session ID).
  • Permette di associare dati persistenti all’utente attraverso l’array superglobale $_SESSION.

Opzioni di Configurazione di PHP per le sessioni

Le sessioni PHP sono configurabili tramite il file php.ini o ini_set(). Ecco alcune opzioni comuni:

  • session.save_path: Percorso in cui sono memorizzati i file di sessione.
  • session.gc_maxlifetime: Durata massima dei dati di sessione in secondi.
  • session.cookie_secure: Garantisce che i cookie vengano inviati solo tramite HTTPS.
  • session.use_strict_mode: Impone una gestione rigorosa degli ID di sessione per migliorare la sicurezza.

PHP offre diverse configurazioni relative alla gestione delle sessioni, tutte personalizzabili tramite il file php.ini, direttive ini_set() o configurazioni specifiche in runtime. Di seguito sono descritte le principali impostazioni per la gestione delle sessioni.

1. session.save_path

Specifica la directory in cui vengono salvati i file di sessione lato server (ad esempio, file temporanei contenenti i dati delle sessioni).

  • Valore predefinito: Sistema operativo specifico, ad esempio /tmp su Linux.
  • Esempio:
    
    session.save_path = "/var/lib/php/sessions"

2. session.name

Specifica il nome del cookie che contiene l’ID della sessione. Il valore predefinito è PHPSESSID.

  • Esempio:
    
    session.name = "MYSESSIONID"

3. session.gc_maxlifetime

Determina il tempo (in secondi) dopo il quale i dati di sessione inutilizzati vengono eliminati dal server durante il garbage collection.

  • Valore predefinito: 1440 (24 minuti).
  • Esempio:
    
    session.gc_maxlifetime = 3600

4. session.gc_probability e session.gc_divisor

Definiscono la probabilità che il garbage collection delle sessioni venga eseguito.

  • Formula: gc_probability / gc_divisor determina la probabilità di esecuzione.
  • Valore predefinito: 1 (probabilità) su 100 (divisore), cioè 1%.
  • Esempio:
    
    session.gc_probability = 1
    session.gc_divisor = 100

5. session.cookie_lifetime

Imposta la durata del cookie di sessione sul client (browser). Se è 0, il cookie scade quando il browser viene chiuso.

  • Valore predefinito: 0.
  • Esempio:
    
    session.cookie_lifetime = 86400  ; 24 ore

6. session.cookie_path

Specifica il percorso in cui il cookie di sessione è valido sul dominio.

  • Valore predefinito: / (valido per tutto il sito).
  • Esempio:
    
    session.cookie_path = "/"

7. session.cookie_domain

Definisce il dominio per cui il cookie di sessione è valido.

  • Valore predefinito: Vuoto (cookie valido solo per il dominio corrente).
  • Esempio:
    
    session.cookie_domain = "example.com"

8. session.cookie_secure

Imposta se il cookie di sessione deve essere trasmesso solo su connessioni HTTPS.

  • Valore predefinito: Off.
  • Esempio:
    
    session.cookie_secure = On

9. session.cookie_httponly

Impedisce l’accesso al cookie di sessione tramite JavaScript (protezione contro attacchi XSS).

  • Valore predefinito: Off.
  • Esempio:
    
    session.cookie_httponly = On

10. session.use_cookies

Determina se PHP utilizza i cookie per memorizzare l’ID della sessione.

  • Valore predefinito: 1 (Attivo).
  • Esempio:
    
    session.use_cookies = 1

11. session.use_only_cookies

Forza PHP a utilizzare solo cookie per l’ID di sessione, disabilitando il passaggio di ID tramite URL (per evitare attacchi session fixation).

  • Valore predefinito: 1 (Attivo).
  • Esempio:
    
    session.use_only_cookies = 1

12. session.use_trans_sid

Permette di aggiungere automaticamente l’ID della sessione agli URL, se i cookie sono disabilitati. Questa impostazione è deprecata per motivi di sicurezza.

  • Valore predefinito: 0 (Disabilitato).
  • Esempio:
    
    session.use_trans_sid = 0

13. session.sid_length

Specifica la lunghezza del session ID (da 22 a 256 caratteri).

  • Valore predefinito: 32.
  • Esempio:
    
    session.sid_length = 48

14. session.sid_bits_per_character

Definisce il numero di bit usati per ogni carattere nel session ID (da 4 a 6).

  • Valore predefinito: 4.
  • Esempio:
    
    session.sid_bits_per_character = 6

Modifica delle configurazioni a runtime

È possibile modificare alcune delle configurazioni di sessione direttamente nello script PHP usando la funzione ini_set().

Esempio:


<?php
ini_set('session.gc_maxlifetime', 3600); // Sessione valida per 1 ora
ini_set('session.cookie_secure', 1);     // Forza HTTPS
ini_set('session.cookie_httponly', 1);   // Blocca accesso JavaScript ai cookie
session_start();
?>

Controllo delle configurazioni correnti

Puoi visualizzare le impostazioni attuali relative alle sessioni utilizzando la funzione phpinfo() o ini_get().

Esempio:


<?php
echo ini_get('session.gc_maxlifetime'); // Visualizza il valore attuale
phpinfo(); // Mostra tutte le configurazioni correnti
?>

Come funziona la gestione delle sessioni nel web server integrato

  1. Gestione standard:
    • Il server incorporato rispetta le impostazioni di sessione definite nel file php.ini o quelle modificate tramite ini_set() nello script.
    • I file di sessione vengono salvati nella directory specificata da session.save_path.
  2. Persistenza delle sessioni:
    • Le sessioni vengono mantenute tra le richieste HTTP, a patto che il server sia in esecuzione e il client invii il cookie con l’ID di sessione (ad esempio, il cookie PHPSESSID).
  3. Nessuna configurazione aggiuntiva:
    • Il built-in server di PHP è progettato per eseguire applicazioni PHP in modo semplice e gestisce automaticamente le funzionalità di base come le sessioni.

Esempio di utilizzo delle sessioni con il built-in server

Script PHP con gestione della sessione

Crea un file chiamato session_example.php:


<?php
// Avvia la sessione
session_start();
// Incrementa un contatore nella sessione
if (isset($_SESSION['counter'])) {
    
$_SESSION['counter']++;
} else {
    
$_SESSION['counter'] = 1;
}
// Mostra il contatore
echo "Hai visitato questa pagina {$_SESSION['counter']} volte.";
// Link per resettare la sessione
echo '<br><a href="session_example.php?reset=true">Resetta sessione</a>';
// Gestisce il reset della sessione
if (isset($_GET['reset']) && $_GET['reset'] === 'true') {
    
session_destroy();
    
echo '<br>Sessione resettata.';
}
?>

Avvia il web server integrato

Esegui il server nella directory in cui si trova il file:


php -S localhost:8000

Poi visita l’indirizzo: http://localhost:8000/session_example.php.

  • Ogni volta che ricarichi la pagina, il contatore ($_SESSION['counter']) aumenta.
  • Quando clicchi sul link “Resetta sessione”, la sessione viene distrutta.

Limitazioni del PHP Built-in Web Server

  1. Uso per sviluppo:
    • Il web server integrato è progettato esclusivamente per lo sviluppo e il testing, non per ambienti di produzione. Non è ottimizzato per gestire molte richieste simultanee o per garantire la sicurezza necessaria in produzione.
  2. Garbage collection delle sessioni:
    • Il built-in server si comporta come gli altri ambienti PHP per il garbage collection delle sessioni, basandosi sulle configurazioni di session.gc_probability e session.gc_divisor.
  3. Concorrenza:
    • Se hai più richieste concorrenti (ad esempio tramite AJAX), la gestione della sessione può causare blocchi temporanei poiché PHP blocca il file di sessione fino a quando la richiesta corrente non è terminata.

Dove vengono salvati i file di sessione?

Con la configurazione di default i file di sessione vengono salvati nella directory specificata dalla direttiva session.save_path. Se non è configurata, PHP utilizza la directory predefinita del sistema (ad esempio, /tmp su Unix/Linux).

Puoi verificare la directory con il seguente codice:


<?php
echo session_save_path();
?>

Gestori Personalizzati di Sessione

PHP consente agli sviluppatori di definire gestori personalizzati per memorizzare i dati di sessione in archivi alternativi come database o cookie.

Atri metodi di memorizzare le sessioni: le sessioni salvate in cookie criptati,  le sessioni salvate su database

PHP Core è in grado di gestire metodi alternativi per memorizzare le sessioni, come salvarle in cookie criptati o in un database, senza bisogno di librerie esterne. Tuttavia, ciò richiede la personalizzazione delle funzionalità di gestione delle sessioni fornite da PHP, utilizzando le funzioni integrate per modificare il comportamento predefinito.


1. Gestione delle sessioni in cookie criptati

PHP permette di gestire le sessioni salvandole direttamente nei cookie del browser anziché nel filesystem del server. Per farlo, puoi usare il gestore di sessione personalizzato tramite la funzione session_set_save_handler().

Esempio di sessioni salvate nei cookie criptati


<?php
session_set_save_handler(
    
'open',
    
'close',
    
'read',
    
'write',
    
'destroy',
    
'gc'
);
// Funzione per criptare i dati
function encrypt_data($data) {
    
$key = 'my-secret-key'; // Usa una chiave sicura
    
return base64_encode(openssl_encrypt($data, 'aes-256-cbc', $key, 0, str_repeat('0', 16)));
}
// Funzione per decriptare i dati
function decrypt_data($data) {
    
$key = 'my-secret-key';
    
return openssl_decrypt(base64_decode($data), 'aes-256-cbc', $key, 0, str_repeat('0', 16));
}
// Funzione per leggere la sessione
function read($session_id) {
    
if (isset($_COOKIE[$session_id])) {
        
return decrypt_data($_COOKIE[$session_id]);
    
}
    
return '';
}
// Funzione per scrivere nella sessione
function write($session_id, $data) {
    
$encrypted_data = encrypt_data($data);
    
setcookie($session_id, $encrypted_data, time() + 3600, "/");
    
return true;
}
// Altre funzioni richieste (open, close, destroy, gc)
function open() {
    
return true;
}
function close() {
    
return true;
}
function destroy($session_id) {
    
setcookie($session_id, '', time() - 3600, "/");
    
return true;
}
function gc($maxlifetime) {
    
return true; // Nessuna gestione necessaria per i cookie
}
// Avvio della sessione
session_start();
$_SESSION['username'] = 'utente123';
echo 'Benvenuto, ' . $_SESSION['username'];
?>

Cosa fa il codice:

  • I dati di sessione vengono salvati nel cookie del browser, criptati con AES-256.
  • I cookie sono sicuri e non leggibili dall’utente.
  • Non è richiesto l’uso di file lato server per memorizzare le sessioni.

2. Gestione delle sessioni salvate in un database

Anche per memorizzare le sessioni in un database, puoi utilizzare session_set_save_handler() per definire un gestore di sessioni personalizzato. In questo caso, i dati di sessione sono salvati in una tabella del database.

Esempio di sessioni salvate su database

Tabella MySQL per memorizzare le sessioni:


CREATE TABLE sessions (
    
session_id VARCHAR(255) NOT NULL PRIMARY KEY,
    
session_data TEXT NOT NULL,
    
session_expiry INT NOT NULL
);

Script PHP per gestire le sessioni:


<?php
session_set_save_handler(
    
'open',
    
'close',
    
'read',
    
'write',
    
'destroy',
    
'gc'
);
function open() {
    
$GLOBALS['db'] = new PDO('mysql:host=localhost;dbname=test', 'root', '');
    
return true;
}
function close() {
    
$GLOBALS['db'] = null;
    
return true;
}
function read($session_id) {
    
$stmt = $GLOBALS['db']->prepare('SELECT session_data FROM sessions WHERE session_id = :session_id AND session_expiry > :time');
    
$stmt->execute([
        
':session_id' => $session_id,
        
':time' => time()
    
]);
    
return $stmt->fetchColumn() ?: '';
}
function write($session_id, $data) {
    
$expiry = time() + ini_get('session.gc_maxlifetime');
    
$stmt = $GLOBALS['db']->prepare('REPLACE INTO sessions (session_id, session_data, session_expiry) VALUES (:session_id, :session_data, :session_expiry)');
    
return $stmt->execute([
        
':session_id' => $session_id,
        
':session_data' => $data,
        
':session_expiry' => $expiry
    
]);
}
function destroy($session_id) {
    
$stmt = $GLOBALS['db']->prepare('DELETE FROM sessions WHERE session_id = :session_id');
    
return $stmt->execute([':session_id' => $session_id]);
}
function gc($maxlifetime) {
    
$stmt = $GLOBALS['db']->prepare('DELETE FROM sessions WHERE session_expiry < :time');
    
return $stmt->execute([':time' => time()]);
}
// Avvio della sessione
session_start();
$_SESSION['username'] = 'utente123';
echo 'Benvenuto, ' . $_SESSION['username'];
?>

Cosa fa il codice:

  • Salva i dati di sessione in un database MySQL, PostgreSQL o simile.
  • Garantisce la persistenza dei dati anche in caso di riavvio del server PHP.

Considerazioni

  1. Cookie criptati:
    • Vantaggi: Nessuna necessità di archiviazione lato server.
    • Svantaggi: Dimensione limitata (max 4 KB) e possibili problemi di sincronizzazione in browser con politiche sui cookie.
  2. Database:
    • Vantaggi: Scalabilità, persistenza dei dati su più server e capacità di analisi delle sessioni.
    • Svantaggi: Maggiore complessità e potenziale overhead sul database.
  3. Implementazione nativa:
    • PHP fornisce tutti gli strumenti per gestire questi approcci senza librerie esterne.
    • L’uso di funzioni come session_set_save_handler() permette di personalizzare completamente il comportamento delle sessioni.

Scrittura dei dati in Sessione

La scrittura dei dati in sessione non avviene immediatamente quando valorizzi la variabile superglobale $_SESSION, ma al termine dello script, nel momento in cui PHP chiude la sessione e salva i dati.


Quando avviene la scrittura in sessione

  1. Quando modifichi $_SESSION durante l’esecuzione di uno script:
    
    $_SESSION['key'] = 'value';
    • Il dato viene aggiornato solo nella copia locale di $_SESSION, che risiede in memoria (RAM) mentre lo script è in esecuzione.
  2. Salvataggio dei dati:
    • PHP salva i dati di sessione nel percorso configurato (es. filesystem, database, ecc.) alla fine dello script, durante la fase di shutdown, dopo che tutte le operazioni sono state completate.
    • Questo avviene automaticamente grazie alla funzione session_write_close(), che PHP chiama implicitamente quando lo script termina, a meno che non venga chiamata manualmente prima.

Esempio per chiarire

Codice PHP:


<?php
session_start();
// Modifica il valore in $_SESSION
$_SESSION['username'] = 'utente123';
// Legge subito il valore modificato
echo "Username in sessione: " . $_SESSION['username'];
// Simula un'operazione manuale di salvataggio e chiusura
session_write_close(); 
// Prova a modificare la sessione dopo averla chiusa
$_SESSION['username'] = 'altro_utente';
?>
  1. Quando esegui:
    
    $_SESSION['username'] = 'utente123';
    • Il dato è aggiornato in $_SESSION (memoria locale).
  2. La sessione viene salvata in modo definitivo quando PHP chiama session_write_close() (o implicitamente al termine dello script).
  3. Se provi a modificare $_SESSION dopo aver chiuso la sessione con session_write_close(), le modifiche non saranno salvate, perché il salvataggio avviene solo fino al momento della chiusura.

Funzioni correlate

  1. session_write_close():
    • Forza il salvataggio dei dati di sessione e chiude la sessione, rendendo i dati disponibili ad altre richieste.
    • Esempio:
      
      session_write_close();
  2. session_abort():
    • Annulla le modifiche fatte a $_SESSION durante lo script, lasciando i dati della sessione invariati rispetto allo stato iniziale.
  3. Comportamento al termine dello script:
    • PHP chiama automaticamente session_write_close() alla fine dello script, garantendo che tutte le modifiche siano salvate.

Il dato in $_SESSION viene salvato in sessione (persistenza) al termine dello script o quando viene chiamata esplicitamente la funzione session_write_close(). La semplice assegnazione di un valore a $_SESSION aggiorna la variabile superglobale solo in memoria fino a quel momento.

Trappole Comuni e Best Practices

  • Chiama sempre session_start() prima di modificare $_SESSION.
  • Usa session_regenerate_id() per prevenire attacchi di session fixation.
  • Proteggi le tue sessioni abilitando session.cookie_secure e session.cookie_httponly.

Observer Design Pattern

Observer Design Pattern

Un approccio per implementare la reattività tra oggetti in modo decoupled.

Introduzione

L’Observer Design Pattern è un design pattern comportamentale per la gestione degli eventi che definisce una relazione uno-a-molti tra oggetti.
L’ Observer Design Pattern definisce una relazione uno-a-molti tra oggetti: quando l’oggetto osservato (detto Subject) cambia stato, notifica automaticamente tutti i suoi osservatori (detti Observers).

Il pattern è composto da:

  • Subject: l’oggetto osservato, che mantiene una lista degli osservatori e li notifica di eventuali modifiche.
  • Observer: un’interfaccia o classe che deve essere implementata da chi desidera osservare il Subject.

Diagramma UML

Il seguente diagramma mostra la struttura del pattern:

Diagramma UML del pattern Observer

Nota: L’immagine rappresenta le relazioni tra le principali classi e interfacce:
Subject, ConcreteSubject, Observer e ConcreteObserver.

Esempio pratico in PHP

Implementiamo un esempio in PHP che simula un sistema di notifiche con due tipi di eventi: user registration
e order placement, ciascuno con relativi listener.

Codice PHP



// Interfaccia Observer
interface Observer {
    public function update(string $event, mixed $data): void;
}

// Classe Subject
class Subject {
    private array $observers = [];

    public function attach(string $event, Observer $observer): void {
        $this->observers[$event][] = $observer;
    }

    public function detach(string $event, Observer $observer): void {
        if (!isset($this->observers[$event])) return;

        $this->observers[$event] = array_filter(
            $this->observers[$event],
            fn($o) => $o !== $observer
        );
    }

    public function notify(string $event, mixed $data): void {
        if (!isset($this->observers[$event])) return;

        foreach ($this->observers[$event] as $observer) {
            $observer->update($event, $data);
        }
    }
}

// Listener per la registrazione utenti
class UserRegistrationListener implements Observer {
    public function update(string $event, mixed $data): void {
        echo "Notifica ricevuta per evento '$event': Nuovo utente registrato con email {$data['email']}.\\n";
    }
}

// Listener per l'invio di ordini
class OrderPlacementListener implements Observer {
    public function update(string $event, mixed $data): void {
        echo "Notifica ricevuta per evento '$event': Ordine effettuato con ID {$data['orderId']}.\\n";
    }
}

// Simulazione
$subject = new Subject();

// Creazione di listener
$userListener = new UserRegistrationListener();
$orderListener = new OrderPlacementListener();

// Registrazione ai rispettivi eventi
$subject->attach('user_registered', $userListener);
$subject->attach('order_placed', $orderListener);

// Generazione di eventi
$subject->notify('user_registered', ['email' => 'example@example.com']);
$subject->notify('order_placed', ['orderId' => '12345']);
                

Diagramma UML dell’implementazione

Diagramma UML dell'implementazione

Il diagramma sopra rappresenta le classi e le interazioni nel nostro esempio.

Conclusione

L’Observer Design Pattern è un approccio potente per implementare notifiche reattive tra oggetti,
consentendo una separazione delle responsabilità. È ampiamente utilizzato in sistemi di eventi, interfacce grafiche e molto altro.

Riferimenti

Il Decorator Design Pattern in PHP

Il Decorator Design Pattern è un pattern strutturale che permette di aggiungere nuove funzionalità a un oggetto esistente senza modificare il suo codice. Questo pattern è utile quando si desidera estendere la funzionalità di un oggetto in modo dinamico, permettendo così di evitare l’overloading del codice o l’uso di classi troppo complesse.

Invece di modificare il comportamento di una classe direttamente, il Decorator Pattern permette di “decorare” un oggetto con nuove responsabilità, creando una relazione di composizione tra oggetti, dove uno decoratore incapsula l’oggetto originario e ne estende le funzionalità.

Esempio di Implementazione in PHP

In questo esempio, implementeremo una classe base che rappresenta un oggetto semplice e successivamente aggiungeremo dei decoratori per estenderne le funzionalità.

Immaginiamo di avere una classe Pizza e di voler aggiungere vari ingredienti come decoratori (es. formaggio extra, pomodoro, ecc.).

1. Creazione dell’oggetto base

La classe base Pizza rappresenta un oggetto di base che può essere decorato da altri oggetti.


class Pizza {
    public function getDescription(): string {
        return "Pizza base";
    }

    public function cost(): float {
        return 5.00;
    }
}

2. Creazione del decoratore astratto

Il decoratore astratto estende la classe Pizza e rappresenta una base per i decoratori concreti. Ogni decoratore deve implementare i metodi getDescription e cost.


abstract class PizzaDecorator extends Pizza {
    protected $pizza;

    public function __construct(Pizza $pizza) {
        $this->pizza = $pizza;
    }

    abstract public function getDescription(): string;
    abstract public function cost(): float;
}

3. Creazione dei decoratori concreti

Ogni decoratore concreto aggiungerà una funzionalità specifica all’oggetto Pizza originale, come ad esempio aggiungere un ingrediente.


class CheeseDecorator extends PizzaDecorator {
    public function getDescription(): string {
        return $this->pizza->getDescription() . ", con formaggio extra";
    }

    public function cost(): float {
        return $this->pizza->cost() + 2.00;
    }
}

class TomatoDecorator extends PizzaDecorator {
    public function getDescription(): string {
        return $this->pizza->getDescription() . ", con pomodoro";
    }

    public function cost(): float {
        return $this->pizza->cost() + 1.50;
    }
}

4. Utilizzo dei decoratori

Ora possiamo combinare i vari decoratori per creare una pizza personalizzata, aggiungendo funzionalità all’oggetto Pizza originale in modo dinamico.


$pizza = new Pizza();
$pizzaConFormaggio = new CheeseDecorator($pizza);
$pizzaConPomodoro = new TomatoDecorator($pizzaConFormaggio);

echo $pizzaConPomodoro->getDescription();  // Output: Pizza base, con formaggio extra, con pomodoro
echo $pizzaConPomodoro->cost();  // Output: 8.50

Conclusioni

Il Decorator Pattern consente di aggiungere comportamenti o funzionalità a oggetti esistenti in modo flessibile e senza modificare la classe originale. Questo è particolarmente utile quando si vuole evitare una proliferazione di classi per combinare diverse funzionalità. In PHP, l’uso di decoratori consente di estendere facilmente un oggetto senza dover cambiare il suo codice, seguendo il principio di Open/Closed Principle della SOLID.

Riferimenti