Kort sagt: Namespaces er i PHP noget i retning af mapper til klassenavne: App\Rechnung\Pdf og Bibliothek\Pdf kan eksistere side om side uden at genere hinanden. Autoloading sørger for, at PHP først indlæser en klasse, når den bruges for første gang. Standarden PSR-4 fastlægger, hvordan et klassenavn bliver til en filsti; Composer genererer derefter den passende autoloader. Fordi denne autoloader kører ved hver forespørgsel, hører den efter en sikkerhedshændelse til de første ting, der kontrolleres.
Hvorfor PHP har brug for namespaces
Namespaces findes siden PHP 5.3.0. Før delte alle klasser og funktioner ét enkelt globalt navnerum. Hed to biblioteker Logger, gik det galt. Udviklere hjalp sig med lange navne som Firma_Modul_Unterbereich_Logger.
PHP-manualen nævner to problemer, som namespaces løser: navnekollisioner mellem egen kode, PHP's interne klasser og tredjepartsbiblioteker samt alt for lange navne, der kun er opstået for at omgå disse kollisioner. For beslutningstagere betyder det: Først namespaces har gjort det praktikabelt at kombinere dusinvis af fremmede pakker i ét projekt, som det i dag er sædvanligt med Composer.
Namespaces i praksis: namespace, use, aliasser
En fil deklarerer sit namespace i den første sætning. Med use henter man klasser ind fra andre namespaces, om ønsket under et kortere navn.
<?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);
}
}
Det fulde navn på denne klasse er App\Rechnung\PdfErzeuger. Den foranstillede backslash ved \sprintf henviser udtrykkeligt til den globale PHP-funktion.
Autoloading: indlæs klasser ved behov
Uden autoloading skulle ethvert script inkludere alle nødvendige filer med require. Med spl_autoload_register() registrerer man i stedet en funktion, som PHP kalder, så snart en ukendt klasse, et interface, en trait eller en enum bruges. Den får klassenavnet og indlæser den passende fil.
<?php
spl_autoload_register(function (string $klasse): void {
$praefix = 'App\\';
$basis = __DIR__ . '/src/';
if (!str_starts_with($klasse, $praefix)) {
return; // ikke ansvarlig, den næste autoloader er på
}
$datei = $basis . str_replace('\\', '/', substr($klasse, strlen($praefix))) . '.php';
if (is_file($datei)) {
require $datei;
}
});
spl_autoload_register() kan kaldes flere gange. PHP spørger så de registrerede autoloadere efter hinanden, indtil en har indlæst klassen. I praksis har store applikationer ofte flere af dem: Composer-autoloaderen til biblioteker, en egen fra CMS'et og flere fra plugins eller moduler. For administratorer betyder det: Hvilken kode der faktisk indlæses ved en forespørgsel, fremgår af samspillet mellem alle disse autoloadere og ikke alene af mappestrukturen.
Manualen fraråder kraftigt at kaste exceptions i en autoloader; PSR-4 forbyder det udtrykkeligt. Den ældre funktion __autoload() blev markeret som forældet med PHP 7.2.0 og fjernet med PHP 8.0.0.
PSR-4: fra klassenavn til filsti
PSR-4 er en standard fra PHP-FIG. Et fuldt klassenavn begynder med et producent-namespace. Et præfiks af namespace'et knyttes til en basismappe, resten af navnet giver undermapper, og filen ender på .php. Store og små bogstaver skal stemme nøjagtigt overens. Sådan ser tilknytningen ud i composer.json:
{
"autoload": {
"psr-4": { "App\\": "src/" }
}
}
| Klassenavn | Fil |
|---|---|
App\Kunde\Kunde |
src/Kunde/Kunde.php |
App\Rechnung\PdfErzeuger |
src/Rechnung/PdfErzeuger.php |
App\Rechnung\Pdf\Vorlage |
src/Rechnung/Pdf/Vorlage.php |
En klassisk faldgrube er brugen af store og små bogstaver. På udviklingsmaskiner med Windows eller macOS skelner filsystemet ofte ikke mellem Kunde.php og kunde.php, på en Linux-server gør det. En applikation, der kører lokalt, finder så ikke en klasse på serveren. Reglen fra PSR-4 om at bruge navne i nøjagtigt den rette stavemåde forhindrer sådanne overraskelser ved leveringen.
Ud over psr-4 kender Composer classmap til kode uden standardstruktur og files til filer, der indlæses ved hver forespørgsel, f.eks. hjælpefunktioner. Flere standarder fra PHP-FIG beskriver siden PSR-standarder.
Optimér autoloaderen i produktion
I produktion søger autoloaderen bedst ikke længere i filsystemet, men slår op i en færdig liste. Composer tilbyder hertil to trin:
composer install --no-dev --optimize-autoloaderomdanner PSR-4-reglerne til en klasseliste. Det er ifølge Composer-dokumentationen anbefalet til produktion.--classmap-authoritativegår videre: Klasser indlæses udelukkende fra listen. Filer, der ikke er opført, indlæser autoloaderen så slet ikke.- Ved hver levering genereres listen på ny, så nye klasser findes.
Autoloading og sikkerhed efter PHP2Shell
PHP2Shell angår PHP-fortolkeren, ikke autoloaderen. Et must er fortsat opdateringen til PHP 8.5.12, 8.4.27, 8.3.36 eller 8.2.35 og genstart af alle workers. Efter en mulig hændelse fortjener autoloaderen dog særlig opmærksomhed: vendor/autoload.php og alle filer, der er opført under files, udføres ved hver forespørgsel. En linje, der er smuglet ind dér, virker altså på hele websitet, uden at en iøjnefaldende ny fil ligger i upload-mappen.
Kontrollér derfor, om vendor og autoload-filerne passer til den kontrollerede tilstand i versionsstyringen. Hjælpsomme er indlæggene Find nyligt ændrede filer og Sammenlign CMS-kernefiler med originalen. I tvivlstilfælde er en nygenereret vendor ud fra lock-filen sikrere end enhver enkeltreparation.
Hvordan hjælper PHP SOS konkret?
- Vi sammenligner
vendor, autoloader og indgangsfiler med den kontrollerede tilstand og markerer enhver afvigelse. - Vi genskaber pakkebeholdningen ud fra lock-filen og opsætter en optimeret autoloader.
- Vi kontrollerer, om kun den offentlige mappe ligger i webroot, og om kildekode ikke kan nås direkte via URL.
- Ved angreb overtager vi oprydningen som led i malwarefjernelsen og dokumenterer resultatet.
Ofte stillede spørgsmål
Siden hvornår findes der namespaces i PHP?
Siden PHP 5.3.0. Ifølge manualen løser de to problemer: navnekollisioner mellem egen kode, PHP og tredjepartsbiblioteker samt alt for lange klassenavne, der kun opstod for at undgå sådanne kollisioner.
Hvad er forskellen på PSR-0 og PSR-4?
Begge afbilder klassenavne på filstier. Ved PSR-4 gentages namespace-præfikset ikke i filstien, hvilket giver fladere mappestrukturer. PSR-0 er hos PHP-FIG klassificeret som forældet, PSR-4 er den anbefalede standard.
Virker __autoload() stadig?
Nej. __autoload() blev markeret som forældet med PHP 7.2.0 og fjernet med PHP 8.0.0. I dag registrerer man autoloadere med spl_autoload_register(), i praksis oftest via den autoloader, Composer genererer.
Skal jeg generere autoloaderen igen, når jeg har oprettet en ny klasse?
Ved rene PSR-4-regler ikke; ifølge Composer-dokumentationen er det netop en fordel ved PSR-4. Den, der bruger en optimeret klasseliste i produktion, skal generere den igen ved hver levering.