Stručne: Menné priestory sú v PHP niečo ako priečinky pre názvy tried: App\Rechnung\Pdf a Bibliothek\Pdf môžu existovať vedľa seba bez toho, aby si prekážali. Autoloading zabezpečuje, že PHP načíta triedu až vtedy, keď ju prvýkrát potrebuje. Štandard PSR-4 určuje, ako sa z názvu triedy stane cesta k súboru; Composer potom vygeneruje zodpovedajúci autoloader. Pretože tento autoloader beží pri každej požiadavke, patrí po bezpečnostnom incidente medzi prvé veci, ktoré sa kontrolujú.
Prečo PHP potrebuje menné priestory
Menné priestory existujú od PHP 5.3.0. Predtým zdieľali všetky triedy a funkcie jeden jediný globálny menný priestor. Ak sa dve knižnice volali Logger, nastal problém. Vývojári si pomáhali dlhými názvami ako Firma_Modul_Unterbereich_Logger.
Príručka PHP uvádza dva problémy, ktoré menné priestory riešia: kolízie názvov medzi vlastným kódom, internými triedami PHP a knižnicami tretích strán, ako aj príliš dlhé názvy, ktoré vznikli len na obchádzanie týchto kolízií. Pre rozhodovateľov to znamená: až menné priestory umožnili prakticky kombinovať v jednom projekte desiatky cudzích balíkov, ako je to dnes s Composerom bežné.
Menné priestory v praxi: namespace, use, aliasy
Súbor deklaruje svoj menný priestor v prvom príkaze. Pomocou use sa do súboru prinášajú triedy z iných menných priestorov, na požiadanie pod kratším názvom.
<?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);
}
}
Úplný názov tejto triedy je App\Rechnung\PdfErzeuger. Úvodný spätný lomka pri \sprintf výslovne odkazuje na globálnu funkciu PHP.
Autoloading: načítanie tried podľa potreby
Bez autoloadingu by každý skript musel všetky potrebné súbory vkladať pomocou require. Pomocou spl_autoload_register() sa namiesto toho zaregistruje funkcia, ktorú PHP zavolá, len čo sa použije neznáma trieda, rozhranie, trait alebo enum. Dostane názov triedy a načíta príslušný súbor.
<?php
spl_autoload_register(function (string $klasse): void {
$praefix = 'App\\';
$basis = __DIR__ . '/src/';
if (!str_starts_with($klasse, $praefix)) {
return; // nie je príslušný, na rade je ďalší autoloader
}
$datei = $basis . str_replace('\\', '/', substr($klasse, strlen($praefix))) . '.php';
if (is_file($datei)) {
require $datei;
}
});
spl_autoload_register() sa dá volať viackrát. PHP potom pýta zaregistrované autoloadery jeden po druhom, kým niektorý triedu nenačíta. V praxi majú veľké aplikácie často viacero z nich: autoloader Composeru pre knižnice, vlastný autoloader CMS a ďalšie od pluginov alebo modulov. Pre administrátorov to znamená: aký kód sa pri požiadavke skutočne načíta, vyplýva z súhry všetkých týchto autoloaderov, a nie len zo štruktúry priečinkov.
Príručka dôrazne odrádza od vyhadzovania výnimiek v autoloaderi; PSR-4 to výslovne zakazuje. Staršia funkcia __autoload() bola v PHP 7.2.0 označená ako zastaraná a v PHP 8.0.0 odstránená.
PSR-4: od názvu triedy k ceste súboru
PSR-4 je štandard PHP-FIG. Úplný názov triedy začína menným priestorom výrobcu. Prefix mena priestoru sa priradí základnému adresáru, zvyšok názvu dáva podpriečinky a súbor končí na .php. Veľké a malé písmená sa musia presne zhodovať. Takto vyzerá priradenie v composer.json:
{
"autoload": {
"psr-4": { "App\\": "src/" }
}
}
| Názov triedy | Súbor |
|---|---|
App\Kunde\Kunde |
src/Kunde/Kunde.php |
App\Rechnung\PdfErzeuger |
src/Rechnung/PdfErzeuger.php |
App\Rechnung\Pdf\Vorlage |
src/Rechnung/Pdf/Vorlage.php |
Klasickou pascou sú veľké a malé písmená. Na vývojárskych počítačoch s Windows alebo macOS súborový systém často nerozlišuje medzi Kunde.php a kunde.php, na serveri s Linuxom áno. Aplikácia, ktorá lokálne beží, potom na serveri triedu nenájde. Pravidlo z PSR-4 používať názvy presne v správnom písaní takýmto prekvapeniam pri nasadení predchádza.
Okrem psr-4 pozná Composer classmap pre kód bez štandardnej štruktúry a files pre súbory, ktoré sa načítavajú pri každej požiadavke, napríklad pomocné funkcie. Ďalšie štandardy PHP-FIG opisuje stránka Štandardy PSR.
Optimalizácia autoloadera v produkcii
V produkcii je najlepšie, ak autoloader už nehľadá v súborovom systéme, ale vyhľadáva v hotovom zozname. Composer na to ponúka dva stupne:
composer install --no-dev --optimize-autoloaderpremení pravidlá PSR-4 na zoznam tried. Podľa dokumentácie Composeru sa to pre produkciu odporúča.--classmap-authoritativeide ďalej: triedy sa načítavajú výlučne zo zoznamu. Súbory, ktoré tam nie sú uvedené, autoloader vôbec nenačíta.- Pri každom nasadení sa zoznam vygeneruje znova, aby sa našli nové triedy.
Autoloading a bezpečnosť po PHP2Shell
PHP2Shell sa týka interpretera PHP, nie autoloadera. Povinná zostáva aktualizácia na PHP 8.5.12, 8.4.27, 8.3.36 alebo 8.2.35 a reštart všetkých workerov. Po možnom incidente si však autoloader zaslúži osobitnú pozornosť: vendor/autoload.php a všetky súbory uvedené v files sa vykonávajú pri každej požiadavke. Riadok, ktorý tam bol votknutý, teda pôsobí na celý web, aj keď v priečinku na nahrávanie neleží žiadny nápadný nový súbor.
Skontrolujte preto, či vendor a súbory autoloadu zodpovedajú overenému stavu zo správy verzií. Užitočné sú články Hľadanie nedávno zmenených súborov a Porovnanie jadrových súborov CMS s originálom. V prípade pochybností je novo vygenerovaný vendor zo súboru lock bezpečnejší než akákoľvek jednotlivá oprava.
Ako PHP SOS konkrétne pomáha?
- Porovnávame
vendor, autoloader a vstupné súbory s overeným stavom a označujeme každú odchýlku. - Znovu vytvárame stav balíkov zo súboru lock a nastavujeme optimalizovaný autoloader.
- Kontrolujeme, či je vo webroote iba verejný priečinok a či zdrojový kód nie je dostupný priamo cez URL.
- Pri napadnutí preberáme vyčistenie v rámci odstránenia malvéru a dokumentujeme výsledok.
Časté otázky
Odkedy existujú menné priestory v PHP?
Od PHP 5.3.0. Podľa príručky riešia dva problémy: kolízie názvov medzi vlastným kódom, PHP a knižnicami tretích strán a príliš dlhé názvy tried, ktoré vznikli len preto, aby sa takým kolíziám predišlo.
Aký je rozdiel medzi PSR-0 a PSR-4?
Obe priraďujú názvy tried k cestám súborov. Pri PSR-4 sa prefix mena priestoru v ceste k súboru neopakuje, čo dáva plytšie adresáre. PSR-0 je u PHP-FIG zaradený ako zastaraný, PSR-4 je odporúčaný štandard.
Funguje __autoload() ešte?
Nie. __autoload() bola v PHP 7.2.0 označená ako zastaraná a v PHP 8.0.0 odstránená. Dnes sa autoloadery registrujú pomocou spl_autoload_register(), v praxi zvyčajne cez autoloader generovaný Composerom.
Musím po vytvorení novej triedy autoloader vygenerovať znova?
Pri čisto PSR-4 pravidlách nie, podľa dokumentácie Composeru je to práve výhoda PSR-4. Kto v produkcii používa optimalizovaný zoznam tried, musí ho pri každom nasadení vygenerovať znova.