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

PsySH: La shell interattiva per il debug delle applicazioni PHP


Introduzione a PsySH

PsySH è una shell interattiva pensata per facilitare
il debug e l’esplorazione di codice PHP. Si tratta di uno strumento potente e versatile, ideale per sviluppatori che
vogliono testare snippet di codice in un ambiente sicuro e interattivo. È particolarmente utile in fase di sviluppo,
consentendo di eseguire comandi e ottenere risultati immediati.

Caratteristiche principali

  • Esecuzione interattiva di codice PHP.
  • Supporto per il caricamento di file e classi personalizzate.
  • Funzioni di autocompletamento per variabili, metodi e classi.
  • Integrazione con debug_backtrace e stack traces per una migliore diagnostica degli errori.
  • Possibilità di esplorare il contesto delle applicazioni in runtime.

Installazione

PsySH è distribuito tramite Composer.

Puo essere installato localmente tra i vendor di un progetto php, ad esempio con il seguente comando:

                
composer require psy/psysh --dev
                
            

Oppure puo essere installato globalmente a livello di sistema con il comando:

                
composer global require psy/psysh
                
            

Una volta installato, PsySH può essere avviato eseguendo il comando:

                
vendor/bin/psysh
                
            

PsySH apre una shell interattiva, da utilizzare un po come la console degli strumenti di sviluppo del browser.

                
Psy Shell v0.12.4 (PHP 8.3.14 — cli) by Justin Hileman
?> 
                
            

Esempio pratico

PsySH puo esere usata come la shell interattiva di php che si avvia con il comando php -a. Supponiamo di voler testare una funzione PHP per calcolare la somma di numeri. Apri PsySH e inserisci il seguente codice:

                
function somma($a, $b) {
    return $a + $b;
}
somma(5, 7); // Output atteso: 12
                
            

Dopo aver premuto Invio, PsySH restituirà l’output della funzione, confermando il corretto funzionamento del codice.
Tuttavia notiamo che PsySH genera un output ad ogni istruzione inserita, infatti PsySH è un Read-Eval-Print Loop (o REPL).

Debug di un’applicazione

PsySH può essere utilizzato anche per esaminare variabili e oggetti durante l’esecuzione di un’applicazione. Per fare ciò, dopo avere importato il file autoload dei vendor, aggiungere il seguente comando nel codice dove inserire il “breackpoint” ed interrompere l’esecuzione per il debug:

                
eval(\Psy\sh());
                
            

Quando l’applicazione raggiunge questa riga, entrerà automaticamente nella shell interattiva, permettendoti di esplorare il contesto in tempo reale.

Il comando help ti darà la lista di comandi di PsySH e le loro spiegazioni:

>>> help

– help Show a list of commands. Type `help [foo]` for information about [foo]. Aliases: ?

– ls List local, instance or class variables, methods and constants. Aliases: list, dir

– dump Dump an object or primitive.

– doc Read the documentation for an object, class, constant, method or property. Aliases: rtfm, man

– show Show the code for an object, class, constant, method or property.

– wtf Show the backtrace of the most recent exception. Aliases: last-exception, wtf?

– trace Show the current call stack.

– buffer Show (or clear) the contents of the code input buffer. Aliases: buf

– clear Clear the Psy Shell screen.

– history Show the Psy Shell history.

– exit End the current session and return to caller. Aliases: quit, q

Il comando ls in PsySH

Il comando ls in PsySH elenca tutti gli elementi disponibili nel contesto corrente, inclusi:

  • Variabili: Mostra tutte le variabili attualmente definite.
  • Classi: Elenca tutte le classi dichiarate, comprese quelle caricate tramite autoload.
  • Metodi: Elenca i metodi di una classe o di un oggetto specificato.
  • Costanti: Elenca tutte le costanti disponibili.

Utilizzo di base

Per utilizzare il comando ls, basta digitare il seguente comando nella shell PsySH:

                
ls
                
            

Questo restituirà un elenco completo delle variabili, classi e altri elementi nel contesto corrente.

Esempi pratici

Elenco delle variabili nel contesto corrente

Supponiamo di avere le seguenti variabili definite:

                
$nome = "Mario";
$eta = 30;
                
            

Digitando ls, vedrai un output simile al seguente:

                
Variables:
  $nome
  $eta
                
            

Esplorare i metodi di una classe o oggetto

Se hai un oggetto, come ad esempio una nuova istanza della classe DateTime, puoi utilizzare
ls per elencare i suoi metodi:

                
$data = new DateTime();
ls $data
                
            

Questo mostrerà tutti i metodi disponibili nella classe DateTime, ad esempio:

                
Methods:
  __construct
  format
  getTimestamp
  setDate
  ... (altri metodi)
                
            

Esplorare classi definite

Se vuoi esplorare tutte le classi attualmente dichiarate nel contesto, usa:

                
ls --classes
                
            

Questo mostrerà un elenco di tutte le classi disponibili, comprese le classi PHP standard e quelle definite dall’utente.

Opzioni aggiuntive

Il comando ls supporta diverse opzioni per filtrare i risultati:

  • --vars: Mostra solo le variabili.
  • --constants: Mostra solo le costanti.
  • --methods: Mostra solo i metodi di una classe o di un oggetto.
  • --classes: Mostra solo le classi definite.

Il comando show in PsySH

Il comando show consente di visualizzare informazioni dettagliate su:

  • Variabili: Mostra il tipo e il valore di una variabile.
  • Classi: Visualizza la definizione di una classe, inclusi metodi e proprietà.
  • Funzioni: Mostra la definizione e i dettagli di una funzione.
  • Costanti: Elenca e descrive le costanti disponibili.

Utilizzo di base

Per utilizzare il comando show, basta specificare il nome dell’elemento da ispezionare. Ad esempio:

                
show $variabile
                
            

Oppure:

                
show NomeClasse
                
            

Esempi pratici

Ispezionare una variabile

Supponiamo di avere la seguente variabile:

                
$nome = "PsySH";
                
            

Utilizzando il comando show:

                
show $nome
                
            

L’output sarà simile al seguente:

                
Name: $nome
Type: string
Value: "PsySH"
                
            

Ispezionare una classe

Supponiamo di avere la seguente classe:

                
class Esempio {
    public $proprieta = "valore";
    public function metodo() {
        return "ciao";
    }
}
                
            

Per visualizzarne la definizione, utilizza:

                
show Esempio
                
            

L’output includerà i dettagli delle proprietà e dei metodi della classe:

                
Class: Esempio
Properties:
  public $proprieta
Methods:
  public metodo()
                
            

Ispezionare una funzione

Puoi anche ispezionare una funzione PHP definita nel contesto attuale o una funzione nativa:

                
show strlen
                
            

L’output mostrerà la firma e una descrizione della funzione:

                
Function: strlen(string $string): int
Description: Get the length of a string
                
            

Riferimenti

PHP 8.4: le novità della nuova versione


PHP ha visto una notevole evoluzione dalla sua nascita, con ogni versione che introduce miglioramenti per renderlo più potente.
Gli aggiornamenti di versione minor, come PHP 8.4, portano un mix di nuove funzionalità.
In questo articolo esploreremo le aggiunte più importanti di PHP 8.4.

Property Hooks

I Property Hooks rappresentano una delle novità principali introdotte in PHP 8.4. Questa funzionalità consente agli sviluppatori di definire comportamenti personalizzati per la lettura e la scrittura delle proprietà di una classe, eliminando la necessità di usare metodi getter e setter tradizionali o metodi magici come __get() e __set().

Come funzionano i Property Hooks?

Con i Property Hooks, è possibile aggiungere logica personalizzata direttamente sulle proprietà usando le parole chiave get e set. Di seguito un esempio pratico:


class Locale {
    public string $languageCode;

    public string $countryCode {
        set (string $countryCode) {
            $this->countryCode = strtoupper($countryCode);
        }
    }

    public string $combinedCode {
        get => sprintf("%s_%s", $this->languageCode, $this->countryCode);
        set (string $value) {
            [$this->languageCode, $this->countryCode] = explode('_', $value, 2);
        }
    }
}

$brazilianPortuguese = new Locale('pt', 'br');
echo $brazilianPortuguese->countryCode; // Output: BR
echo $brazilianPortuguese->combinedCode; // Output: pt_BR

Questo esempio mostra come personalizzare la scrittura di una proprietà per convertirne il valore in maiuscolo e come generare una proprietà virtuale combinando più valori.

Vantaggi rispetto alle versioni precedenti

Nelle versioni precedenti, per ottenere una funzionalità simile, era necessario ricorrere a metodi magici o a getter e setter separati. Ad esempio:


class User_OLD {
    private string $_name;

    public function __get(string $propName): mixed {
        return match ($propName) {
            'name' => $this->_name,
            default => throw new Error("Undefined property $propName"),
        };
    }

    public function __set(string $propName, $value): void {
        if ($propName === 'name' && strlen($value) === 0) {
            throw new ValueError("Name cannot be empty");
        }
        $this->_name = $value;
    }
}

Con i Property Hooks, la sintassi è più semplice e leggibile:


class User {
    public string $name {
        set {
            if (strlen($value) === 0) throw new ValueError("Name cannot be empty");
            $this->name = $value;
        }
    }
}

Possibili utilizzi

Alcuni casi d’uso pratici per i Property Hooks includono:

  • Validazione dei dati prima della scrittura
  • Proprietà virtuali calcolate
  • Conversione automatica di formati o unità

Visibilità asimmetrica delle proprietà

PHP 8.4 introduce il supporto per la visibilità asimmetrica delle proprietà, permettendo livelli di accesso differenti per la lettura (get) e la scrittura (set).
Questa funzionalità semplifica scenari in cui le proprietà necessitano di accesso pubblico in lettura ma limitato in scrittura.
Documentazione ufficiale.


class Esempio {
    public protected(set) string $nome;

    public function __construct(string $nome) {
        $this->nome = $nome;
    }
}

Attributo #[\Deprecated]

L’attributo #[\Deprecated] standardizza il modo in cui gli sviluppatori possono contrassegnare metodi, funzioni o costanti come deprecati.
Fornisce un messaggio strutturato per gli avvisi di deprecazione.
Documentazione ufficiale.


class Esempio {
    #[\Deprecated(message: "Usa il metodo newMethod() invece", since: "8.4")]
    public function oldMethod() {
        // Vecchia implementazione
    }

    public function newMethod() {
        // Nuova implementazione
    }
}

Miglioramenti al DOM API con supporto per HTML5

L’estensione DOM ora supporta la gestione di documenti HTML5 con nuove classi standardizzate nel namespace Dom.
Questo aggiornamento risolve problemi di conformità e semplifica la manipolazione degli HTML.
Documentazione ufficiale.


$html = '<main><article>Contenuto</article></main>';
$dom = Dom\HTMLDocument::createFromString($html);

$nodo = $dom->querySelector('main > article');
echo $nodo->textContent; // Output: "Contenuto"

API orientata agli oggetti per BcMath

Il nuovo oggetto BcMath\Number consente un approccio orientato agli oggetti per operazioni aritmetiche ad alta precisione.
Supporta operatori standard e implementa l’interfaccia Stringable per i contesti stringa.
Documentazione ufficiale.


use BcMath\Number;

$num1 = new Number('0.12345');
$num2 = new Number('2');
$risultato = $num1 + $num2;

echo $risultato; // Output: "2.12345"

Nuove funzioni per gli array

PHP 8.4 introduce nuove funzioni per la gestione degli array, tra cui:

  • array_find(): Trova il primo elemento corrispondente.
  • array_find_key(): Trova la chiave del primo elemento corrispondente.
  • array_any(): Verifica se un elemento corrisponde a una condizione.
  • array_all(): Verifica se tutti gli elementi corrispondono a una condizione.

Documentazione ufficiale.

Sottoclassi specifiche per driver PDO

Le sottoclassi specifiche per i driver PDO (ad esempio, Pdo\MySql, Pdo\Sqlite) permettono metodi specifici per i driver.
Documentazione ufficiale.

Chiamata ai metodi senza parentesi

PHP 8.4 permette la chiamata ai metodi senza parentesi se non richiedono argomenti obbligatori.
Documentazione ufficiale.


class Esempio {
    public function versione(): string {
        return "8.4";
    }
}

$esempio = new Esempio();
echo $esempio->versione; // Output: "8.4"