In breve: in PHP i namespace sono una sorta di cartelle per i nomi delle classi: App\Rechnung\Pdf e Bibliothek\Pdf possono coesistere senza darsi fastidio. L'autoloading fa sì che PHP carichi una classe solo quando serve per la prima volta. Lo standard PSR-4 stabilisce come da un nome di classe si ricavi un percorso di file; Composer genera poi l'autoloader adatto. Poiché questo autoloader viene eseguito a ogni richiesta, dopo un incidente di sicurezza è tra le prime cose da controllare.
Perché PHP ha bisogno dei namespace
I namespace esistono dalla versione PHP 5.3.0. Prima, tutte le classi e le funzioni condividevano un unico spazio dei nomi globale. Se due librerie si chiamavano Logger, si andava a sbattere. Gli sviluppatori si aiutavano con nomi lunghi come Firma_Modul_Unterbereich_Logger.
Il manuale PHP indica due problemi che i namespace risolvono: le collisioni di nomi tra il codice proprio, le classi interne di PHP e le librerie di terze parti, e i nomi eccessivamente lunghi nati solo per aggirare tali collisioni. Per chi decide significa: solo i namespace hanno reso praticabile combinare decine di pacchetti esterni in un progetto, come oggi è consueto con Composer.
I namespace nella pratica: namespace, use, alias
Un file dichiara il proprio namespace nella prima istruzione. Con use si importano classi da altri namespace, se si vuole con un nome più breve.
<?php
declare(strict_types=1);
namespace App\Rechnung;
use App\Kunde\Kunde;
use Psr\Log\LoggerInterface as Log;
final class PdfErzeuger
{
public function __construct(private Log $log) {}
public function erzeugen(Kunde $kunde): string
{
$this->log->info('PDF erzeugt', ['kunde' => $kunde->id]);
return \sprintf('Rechnung für %s', $kunde->name);
}
}
Il nome completo di questa classe è App\Rechnung\PdfErzeuger. Il backslash iniziale in \sprintf rimanda esplicitamente alla funzione globale di PHP.
Autoloading: caricare le classi quando servono
Senza autoloading ogni script dovrebbe includere con require tutti i file necessari. Con spl_autoload_register() si registra invece una funzione che PHP richiama non appena viene usata una classe, un'interfaccia, un trait o un enum sconosciuti. Riceve il nome della classe e carica il file corrispondente.
<?php
spl_autoload_register(function (string $klasse): void {
$praefix = 'App\\';
$basis = __DIR__ . '/src/';
if (!str_starts_with($klasse, $praefix)) {
return; // non di competenza, tocca al prossimo autoloader
}
$datei = $basis . str_replace('\\', '/', substr($klasse, strlen($praefix))) . '.php';
if (is_file($datei)) {
require $datei;
}
});
spl_autoload_register() si può richiamare più volte. PHP interroga allora gli autoloader registrati uno dopo l'altro, finché uno ha caricato la classe. Nella pratica le grandi applicazioni ne hanno spesso più d'uno: l'autoloader di Composer per le librerie, uno proprio del CMS e altri di plugin o moduli. Per gli amministratori significa: quale codice venga effettivamente caricato in una richiesta risulta dall'interazione di tutti questi autoloader e non dalla sola struttura delle cartelle.
Il manuale sconsiglia vivamente di lanciare eccezioni in un autoloader; PSR-4 lo vieta espressamente. La più vecchia funzione __autoload() è stata dichiarata deprecata con PHP 7.2.0 e rimossa con PHP 8.0.0.
PSR-4: dal nome della classe al percorso del file
PSR-4 è uno standard di PHP-FIG. Un nome di classe completo inizia con un namespace del produttore. Un prefisso del namespace viene associato a una directory di base, il resto del nome genera le sottocartelle, e il file termina con .php. Maiuscole e minuscole devono corrispondere esattamente. Così appare l'associazione nel composer.json:
{
"autoload": {
"psr-4": { "App\\": "src/" }
}
}
| Nome della classe | File |
|---|---|
App\Kunde\Kunde |
src/Kunde/Kunde.php |
App\Rechnung\PdfErzeuger |
src/Rechnung/PdfErzeuger.php |
App\Rechnung\Pdf\Vorlage |
src/Rechnung/Pdf/Vorlage.php |
Un classico inciampo è la distinzione tra maiuscole e minuscole. Sui computer di sviluppo con Windows o macOS il file system spesso non distingue tra Kunde.php e kunde.php, su un server Linux sì. Un'applicazione che gira in locale non trova allora una classe sul server. La regola di PSR-4 di usare i nomi esattamente con la grafia corretta evita queste sorprese al momento del rilascio.
Oltre a psr-4, Composer conosce classmap per il codice senza struttura standard e files per i file che vengono caricati a ogni richiesta, ad esempio funzioni di utilità. Altri standard di PHP-FIG sono descritti nella pagina Standard PSR.
Ottimizzare l'autoloader in produzione
In produzione l'autoloader è meglio che non cerchi più nel file system, ma consulti un elenco già pronto. Composer offre a questo scopo due livelli:
composer install --no-dev --optimize-autoloadertrasforma le regole PSR-4 in un elenco di classi. Secondo la documentazione di Composer è raccomandato per la produzione.--classmap-authoritativeva oltre: le classi vengono caricate esclusivamente dall'elenco. I file non elencati l'autoloader non li carica affatto.- A ogni rilascio l'elenco viene rigenerato, in modo che le nuove classi vengano trovate.
Autoloading e sicurezza dopo PHP2Shell
PHP2Shell riguarda l'interprete PHP, non l'autoloader. Restano obbligatori l'aggiornamento a PHP 8.5.12, 8.4.27, 8.3.36 o 8.2.35 e il riavvio di tutti i worker. Dopo un possibile incidente, però, l'autoloader merita particolare attenzione: vendor/autoload.php e tutti i file registrati sotto files vengono eseguiti a ogni richiesta. Una riga lì inserita agisce quindi su tutto il sito web, senza che nella cartella di upload ci sia un nuovo file appariscente.
Verifichi perciò se vendor e i file di autoload corrispondono allo stato controllato presente nel controllo di versione. Sono utili gli articoli Trovare i file modificati di recente e Confrontare i file del nucleo del CMS con l'originale. Nel dubbio, un vendor rigenerato da zero a partire dal file di lock è più sicuro di qualsiasi riparazione puntuale.
Come aiuta concretamente PHP SOS?
- Confrontiamo
vendor, autoloader e file di ingresso con lo stato controllato e contrassegniamo ogni scostamento. - Ripristiniamo l'insieme dei pacchetti dal file di lock e configuriamo un autoloader ottimizzato.
- Verifichiamo che nel webroot si trovi solo la cartella pubblica e che il codice sorgente non sia raggiungibile direttamente tramite URL.
- In caso di compromissione ci occupiamo della bonifica nell'ambito della rimozione del malware e documentiamo il risultato.
Domande frequenti
Da quando esistono i namespace in PHP?
Da PHP 5.3.0. Secondo il manuale risolvono due problemi: le collisioni di nomi tra il codice proprio, PHP e le librerie di terze parti, e i nomi di classe eccessivamente lunghi nati solo per evitare tali collisioni.
Qual è la differenza tra PSR-0 e PSR-4?
Entrambi associano i nomi delle classi ai percorsi dei file. Con PSR-4 il prefisso del namespace non viene ripetuto nel percorso del file, il che dà directory meno profonde. PSR-0 è classificato come deprecato da PHP-FIG, PSR-4 è lo standard raccomandato.
__autoload() funziona ancora?
No. __autoload() è stato dichiarato deprecato con PHP 7.2.0 e rimosso con PHP 8.0.0. Oggi gli autoloader si registrano con spl_autoload_register(), nella pratica di solito tramite l'autoloader generato da Composer.
Devo rigenerare l'autoloader dopo aver creato una nuova classe?
Con sole regole PSR-4 no; secondo la documentazione di Composer è proprio questo un vantaggio di PSR-4. Chi in produzione usa un elenco ottimizzato delle classi deve rigenerarlo a ogni rilascio.