Introduzione
Quando un progetto PHP cresce, non vengono aggiunte soltanto classi concrete. Diventano importanti anche interfacce, trait ed enum. Questi elementi permettono di definire contratti, riutilizzare comportamenti e rappresentare valori limitati in modo sicuro.
Un errore comune consiste nel caricare automaticamente solo le classi principali e lasciare interfacce, trait o enum in file inclusi manualmente con require. Questa soluzione può funzionare all’inizio, ma rende il codice fragile e difficile da mantenere.
In questo tutorial costruiremo un piccolo sistema di notifiche. L’esempio mostrerà come organizzare i file con i namespace e come configurare l’autoloading PSR-4 affinché PHP trovi automaticamente tutti i componenti necessari, inclusi interfacce, trait ed enum.
Codice completo
La struttura del progetto sarà la seguente:
notifier/
├── composer.json
├── public/
│ └── index.php
└── src/
├── Contract/
│ └── NotifierInterface.php
├── Enum/
│ └── NotificationChannel.php
├── Support/
│ └── LogsMessages.php
└── Service/
└── NotificationService.php Configuriamo Composer nel file composer.json:
{
"name": "demo/notifier",
"description": "Esempio di namespace e autoloading",
"require": {
"php": "^8.1"
},
"autoload": {
"psr-4": {
"DemoNotifier": "src/"
}
}
} L’interfaccia definisce il comportamento che ogni sistema di notifica deve rispettare:
<?php
namespace DemoNotifierContract;
use DemoNotifierEnumNotificationChannel;
interface NotifierInterface
{
public function send(
string $recipient,
string $message,
NotificationChannel $channel
): void;
} L’enum limita i canali disponibili, evitando stringhe arbitrarie:
<?php
namespace DemoNotifierEnum;
enum NotificationChannel: string
{
case EMAIL = ´email´;
case SMS = ´sms´;
case PUSH = ´push´;
} Il trait contiene una funzionalità riutilizzabile per registrare i messaggi inviati:
<?php
namespace DemoNotifierSupport;
trait LogsMessages
{
private array $logs = [];
protected function logMessage(string $message): void
{
$this->logs[] = sprintf(
´[%s] %s´,
date(´Y-m-d H:i:s´),
$message
);
}
public function getLogs(): array
{
return $this->logs;
}
} La classe concreta implementa l’interfaccia e utilizza il trait:
<?php
namespace DemoNotifierService;
use DemoNotifierContractNotifierInterface;
use DemoNotifierEnumNotificationChannel;
use DemoNotifierSupportLogsMessages;
final class NotificationService implements NotifierInterface
{
use LogsMessages;
public function send(
string $recipient,
string $message,
NotificationChannel $channel
): void {
$log = sprintf(
´Invio tramite %s a %s: %s´,
$channel->value,
$recipient,
$message
);
$this->logMessage($log);
}
} Infine, il punto di ingresso del programma utilizza soltanto l’autoloader di Composer:
<?php
require dirname(__DIR__) . ´/vendor/autoload.php´;
use DemoNotifierEnumNotificationChannel;
use DemoNotifierServiceNotificationService;
$service = new NotificationService();
$service->send(
´[email protected]´,
´Il tuo ordine è stato spedito´,
NotificationChannel::EMAIL
);
$service->send(
´+391234567890´,
´Codice di verifica: 4821´,
NotificationChannel::SMS
);
foreach ($service->getLogs() as $log) {
echo $log . PHP_EOL;
} Per installare e generare l’autoloader esegui:
composer install
composer dump-autoload Spiegazione
La direttiva:
"DemoNotifier": "src/" indica che ogni namespace che inizia con DemoNotifier deve essere cercato nella directory src. Per esempio, il namespace:
DemoNotifierEnum corrisponde alla directory:
src/Enum/ La classe NotificationChannel si trova quindi in src/Enum/NotificationChannel.php. Lo stesso principio vale per interfacce e trait: non esiste una differenza speciale nel meccanismo di autoloading. Ciò che conta è la corrispondenza tra namespace, percorso e nome del file.
Quando PHP incontra il tipo NotificationChannel, Composer verifica la mappa PSR-4 e carica il file corretto. Questo avviene anche quando il tipo viene usato in una dichiarazione di parametro, come in:
public function send(
string $recipient,
string $message,
NotificationChannel $channel
): void Il costruttore dell’enum viene gestito automaticamente da PHP, mentre il file dell’enum viene caricato dall’autoloader.
La parola chiave use usata dentro una classe per un trait non è la stessa cosa dell’istruzione use usata per importare un namespace. In questo caso:
use LogsMessages; inserisce i metodi del trait nella classe. Il trait viene comunque risolto tramite il suo namespace e, se necessario, caricato automaticamente.
Best practice
- Usa un namespace coerente: il prefisso configurato in Composer deve corrispondere alla struttura logica del progetto.
- Mantieni un elemento principale per file: salva ogni interfaccia, trait, enum e classe in un file dedicato.
- Preferisci enum ai valori testuali liberi: riducono errori come
´e-mail´,´email´e´Email´. - Usa interfacce per definire contratti: una classe dipendente dall’interfaccia è più semplice da sostituire e testare.
- Limita i trait: sono utili per comportamenti trasversali, ma non dovrebbero diventare contenitori di logica complessa.
- Rigenera l’autoloader dopo aver modificato la configurazione: esegui
composer dump-autoload. - Verifica la versione di PHP: gli enum richiedono PHP 8.1 o versioni successive.
- Non usare require manuali per ogni file: includi una sola volta
vendor/autoload.php. - Controlla i nomi con precisione: su sistemi case-sensitive,
NotificationService.phpenotificationservice.phpnon sono equivalenti.
Riepilogo
Namespace e autoloading non servono soltanto a caricare classi concrete. Lo stesso sistema gestisce in modo uniforme interfacce, trait ed enum, purché la struttura delle cartelle rispetti la configurazione PSR-4.
Nel progetto dell’esempio, l’interfaccia definisce il contratto, l’enum rappresenta i canali validi, il trait riutilizza la logica di registrazione e la classe concreta coordina il comportamento. Grazie all’autoloader di Composer, il file principale non deve conoscere i percorsi fisici dei singoli componenti.
Approfondisci con risorse ufficiali
- PHP Namespaces: https://www.php.net/manual/en/language.namespaces.php
- PHP Autoloading: https://www.php.net/manual/en/language.oop5.autoload.php
- Enumerations in PHP: https://www.php.net/manual/en/language.enumerations.php
- Composer Autoloading: https://getcomposer.org/doc/01-basic-usage.md#autoloading
- PSR-4 Autoloader Specification: https://www.php-fig.org/psr/psr-4/
