Kort gezegd: Namespaces zijn in PHP iets als mappen voor klassenamen: App\Rechnung\Pdf en Bibliothek\Pdf kunnen naast elkaar bestaan zonder elkaar te storen. Autoloading zorgt ervoor dat PHP een klasse pas laadt wanneer ze voor het eerst nodig is. De standaard PSR-4 legt vast hoe uit een klassenaam een bestandspad ontstaat; Composer genereert vervolgens de bijpassende autoloader. Omdat deze autoloader bij elk verzoek draait, hoort hij na een beveiligingsincident tot de eerste dingen die worden gecontroleerd.
Waarom PHP namespaces nodig heeft
Namespaces bestaan sinds PHP 5.3.0. Daarvoor deelden alle klassen en functies één enkele globale naamruimte. Heetten twee bibliotheken Logger, dan ging het mis. Ontwikkelaars hielpen zich met lange namen zoals Firma_Modul_Unterbereich_Logger.
De PHP-handleiding noemt twee problemen die namespaces oplossen: naamconflicten tussen eigen code, interne PHP-klassen en bibliotheken van derden, en overlange namen die alleen zijn ontstaan om deze conflicten te omzeilen. Voor beslissers betekent dat: pas namespaces hebben het praktisch haalbaar gemaakt om tientallen externe pakketten in één project te combineren, zoals dat met Composer tegenwoordig gebruikelijk is.
Namespaces in de praktijk: namespace, use, aliassen
Een bestand declareert zijn namespace in de eerste instructie. Met use haalt men klassen uit andere namespaces binnen, desgewenst onder een kortere naam.
<?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);
}
}
De volledige naam van deze klasse luidt App\Rechnung\PdfErzeuger. De voorloop-backslash bij \sprintf verwijst uitdrukkelijk naar de globale PHP-functie.
Autoloading: klassen laden wanneer nodig
Zonder autoloading zou elk script alle benodigde bestanden met require moeten insluiten. Met spl_autoload_register() registreert men in plaats daarvan een functie die PHP aanroept zodra een onbekende klasse, interface, trait of enum wordt gebruikt. Ze krijgt de klassenaam en laadt het bijpassende bestand.
<?php
spl_autoload_register(function (string $klasse): void {
$praefix = 'App\\';
$basis = __DIR__ . '/src/';
if (!str_starts_with($klasse, $praefix)) {
return; // niet verantwoordelijk, de volgende autoloader is aan de beurt
}
$datei = $basis . str_replace('\\', '/', substr($klasse, strlen($praefix))) . '.php';
if (is_file($datei)) {
require $datei;
}
});
spl_autoload_register() kan meerdere keren worden aangeroepen. PHP vraagt de geregistreerde autoloaders dan een voor een, tot er een de klasse heeft geladen. In de praktijk hebben grote toepassingen er vaak meerdere: de Composer-autoloader voor bibliotheken, een eigen autoloader van het CMS en nog andere van plugins of modules. Voor beheerders betekent dat: welke code bij een verzoek daadwerkelijk wordt geladen, volgt uit het samenspel van al deze autoloaders en niet alleen uit de mappenstructuur.
De handleiding raadt sterk af om in een autoloader exceptions te werpen; PSR-4 verbiedt het uitdrukkelijk. De oudere functie __autoload() werd met PHP 7.2.0 als verouderd gemarkeerd en met PHP 8.0.0 verwijderd.
PSR-4: van klassenaam naar bestandspad
PSR-4 is een standaard van de PHP-FIG. Een volledige klassenaam begint met een leveranciers-namespace. Een voorvoegsel van de namespace wordt aan een basismap gekoppeld, de rest van de naam levert submappen op, en het bestand eindigt op .php. Hoofdletters en kleine letters moeten exact overeenkomen. Zo ziet de koppeling in de composer.json eruit:
{
"autoload": {
"psr-4": { "App\\": "src/" }
}
}
| Klassenaam | Bestand |
|---|---|
App\Kunde\Kunde |
src/Kunde/Kunde.php |
App\Rechnung\PdfErzeuger |
src/Rechnung/PdfErzeuger.php |
App\Rechnung\Pdf\Vorlage |
src/Rechnung/Pdf/Vorlage.php |
Een klassieke valkuil is het onderscheid tussen hoofdletters en kleine letters. Op ontwikkelcomputers met Windows of macOS maakt het bestandssysteem vaak geen onderscheid tussen Kunde.php en kunde.php, op een Linux-server wel. Een toepassing die lokaal werkt, vindt dan op de server een klasse niet. De regel uit PSR-4 om namen exact in de juiste schrijfwijze te gebruiken, voorkomt zulke verrassingen bij de oplevering.
Naast psr-4 kent Composer classmap voor code zonder standaardstructuur en files voor bestanden die bij elk verzoek worden geladen, zoals hulpfuncties. Verdere standaarden van de PHP-FIG beschrijft de pagina PSR-standaarden.
De autoloader in productie optimaliseren
In productie zoekt de autoloader bij voorkeur niet meer in het bestandssysteem, maar kijkt hij in een kant-en-klare lijst. Composer biedt daarvoor twee niveaus:
composer install --no-dev --optimize-autoloaderzet de PSR-4-regels om in een klassenlijst. Dat wordt volgens de Composer-documentatie voor productie aanbevolen.--classmap-authoritativegaat verder: klassen worden uitsluitend uit de lijst geladen. Bestanden die niet in de lijst staan, laadt de autoloader dan helemaal niet.- Bij elke oplevering wordt de lijst opnieuw gegenereerd, zodat nieuwe klassen worden gevonden.
Autoloading en beveiliging na PHP2Shell
PHP2Shell betreft de PHP-interpreter, niet de autoloader. Verplicht blijven de update naar PHP 8.5.12, 8.4.27, 8.3.36 of 8.2.35 en de herstart van alle workers. Na een mogelijk incident verdient de autoloader echter bijzondere aandacht: vendor/autoload.php en alle onder files opgenomen bestanden worden bij elk verzoek uitgevoerd. Een daar binnengesmokkelde regel werkt dus op de hele website, zonder dat er een opvallend nieuw bestand in de uploadmap staat.
Controleer daarom of vendor en de autoload-bestanden overeenkomen met de gecontroleerde stand uit de versiebeheer. Nuttig zijn de artikelen Onlangs gewijzigde bestanden vinden en CMS-kernbestanden met het origineel vergelijken. In geval van twijfel is een vers gegenereerde vendor uit het lock-bestand veiliger dan elke afzonderlijke reparatie.
Hoe helpt PHP SOS concreet?
- Wij vergelijken
vendor, autoloader en ingangsbestanden met de gecontroleerde stand en markeren elke afwijking. - Wij herstellen de pakketvoorraad vanuit het lock-bestand en richten een geoptimaliseerde autoloader in.
- Wij controleren of alleen de openbare map in de webroot staat en broncode niet rechtstreeks via een URL bereikbaar is.
- Bij besmetting nemen wij de opschoning over in het kader van de malwareverwijdering en documenteren het resultaat.
Veelgestelde vragen
Sinds wanneer zijn er namespaces in PHP?
Sinds PHP 5.3.0. Ze lossen volgens de handleiding twee problemen op: naamconflicten tussen eigen code, PHP en bibliotheken van derden, en overlange klassenamen die alleen ontstonden om zulke conflicten te vermijden.
Wat is het verschil tussen PSR-0 en PSR-4?
Beide beelden klassenamen af op bestandspaden. Bij PSR-4 wordt het namespace-voorvoegsel niet in het bestandspad herhaald, wat vlakkere mappenstructuren oplevert. PSR-0 is door de PHP-FIG als verouderd ingedeeld, PSR-4 is de aanbevolen standaard.
Werkt __autoload() nog?
Nee. __autoload() werd met PHP 7.2.0 als verouderd gemarkeerd en met PHP 8.0.0 verwijderd. Tegenwoordig registreert men autoloaders met spl_autoload_register(), in de praktijk meestal via de door Composer gegenereerde autoloader.
Moet ik na het aanmaken van een nieuwe klasse de autoloader opnieuw genereren?
Bij pure PSR-4-regels niet; volgens de Composer-documentatie is dat juist een voordeel van PSR-4. Wie in productie een geoptimaliseerde klassenlijst gebruikt, moet die bij elke oplevering opnieuw genereren.