Krótko: Przestrzenie nazw są w PHP czymś w rodzaju folderów dla nazw klas: App\Rechnung\Pdf i Bibliothek\Pdf mogą istnieć obok siebie, nie przeszkadzając sobie. Autoloading sprawia, że PHP ładuje klasę dopiero wtedy, gdy jest po raz pierwszy potrzebna. Standard PSR-4 określa, jak z nazwy klasy powstaje ścieżka pliku; Composer generuje potem odpowiedni autoloader. Ponieważ ten autoloader działa przy każdym żądaniu, po incydencie bezpieczeństwa należy do pierwszych rzeczy, które się sprawdza.
Dlaczego PHP potrzebuje przestrzeni nazw
Przestrzenie nazw istnieją od PHP 5.3.0. Wcześniej wszystkie klasy i funkcje dzieliły jedną globalną przestrzeń nazw. Jeśli dwie biblioteki nazywały się Logger, dochodziło do kolizji. Programiści radzili sobie długimi nazwami, takimi jak Firma_Modul_Unterbereich_Logger.
Podręcznik PHP wymienia dwa problemy, które rozwiązują przestrzenie nazw: kolizje nazw między własnym kodem, klasami wewnętrznymi PHP i bibliotekami podmiotów trzecich oraz zbyt długie nazwy, które powstały tylko po to, by te kolizje obejść. Dla osób podejmujących decyzje oznacza to, że dopiero przestrzenie nazw umożliwiły w praktyce łączenie w jednym projekcie dziesiątek obcych pakietów, jak to jest dziś zwyczajem z Composerem.
Przestrzenie nazw w praktyce: namespace, use, aliasy
Plik deklaruje swoją przestrzeń nazw w pierwszej instrukcji. Za pomocą use sprowadza się klasy z innych przestrzeni nazw, w razie potrzeby pod krótszą nazwą.
<?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);
}
}
Pełna nazwa tej klasy brzmi App\Rechnung\PdfErzeuger. Wiodący ukośnik odwrotny przy \sprintf jednoznacznie wskazuje globalną funkcję PHP.
Autoloading: ładowanie klas w razie potrzeby
Bez autoloadingu każdy skrypt musiałby dołączać wszystkie potrzebne pliki za pomocą require. Zamiast tego za pomocą spl_autoload_register() rejestruje się funkcję, którą PHP wywołuje, gdy tylko użyta zostanie nieznana klasa, interfejs, trait lub enum. Otrzymuje ona nazwę klasy i ładuje odpowiedni plik.
<?php
spl_autoload_register(function (string $klasse): void {
$praefix = 'App\\';
$basis = __DIR__ . '/src/';
if (!str_starts_with($klasse, $praefix)) {
return; // niewłaściwy autoloader, kolej na następny
}
$datei = $basis . str_replace('\\', '/', substr($klasse, strlen($praefix))) . '.php';
if (is_file($datei)) {
require $datei;
}
});
spl_autoload_register() można wywoływać wielokrotnie. PHP odpytuje wtedy zarejestrowane autoloadery po kolei, aż któryś załaduje klasę. W praktyce duże aplikacje mają ich często kilka: autoloader Composera dla bibliotek, własny autoloader CMS i kolejne od wtyczek lub modułów. Dla administratorów oznacza to, że to, jaki kod zostanie faktycznie załadowany przy żądaniu, wynika ze współdziałania wszystkich tych autoloaderów, a nie wyłącznie ze struktury katalogów.
Podręcznik stanowczo odradza zgłaszanie wyjątków w autoloaderze; PSR-4 wyraźnie tego zabrania. Starsza funkcja __autoload() została oznaczona jako przestarzała w PHP 7.2.0 i usunięta w PHP 8.0.0.
PSR-4: od nazwy klasy do ścieżki pliku
PSR-4 to standard PHP-FIG. Pełna nazwa klasy zaczyna się od przestrzeni nazw producenta. Prefiks przestrzeni nazw jest przypisany do katalogu bazowego, reszta nazwy daje podkatalogi, a plik kończy się na .php. Wielkość liter musi się dokładnie zgadzać. Tak wygląda przypisanie w composer.json:
{
"autoload": {
"psr-4": { "App\\": "src/" }
}
}
| Nazwa klasy | Plik |
|---|---|
App\Kunde\Kunde |
src/Kunde/Kunde.php |
App\Rechnung\PdfErzeuger |
src/Rechnung/PdfErzeuger.php |
App\Rechnung\Pdf\Vorlage |
src/Rechnung/Pdf/Vorlage.php |
Klasyczną pułapką jest wielkość liter. Na komputerach deweloperskich z Windows lub macOS system plików często nie rozróżnia Kunde.php i kunde.php, a na serwerze Linux tak. Aplikacja, która działa lokalnie, nie znajduje wtedy klasy na serwerze. Reguła z PSR-4, aby używać nazw dokładnie w poprawnej pisowni, zapobiega takim niespodziankom przy wdrożeniu.
Obok psr-4 Composer zna classmap dla kodu bez standardowej struktury oraz files dla plików ładowanych przy każdym żądaniu, na przykład funkcji pomocniczych. Dalsze standardy PHP-FIG opisuje strona Standardy PSR.
Optymalizacja autoloadera w produkcji
W produkcji autoloader najlepiej nie szuka już w systemie plików, lecz zagląda do gotowej listy. Composer oferuje w tym celu dwa stopnie:
composer install --no-dev --optimize-autoloaderzamienia reguły PSR-4 na listę klas. Według dokumentacji Composera jest to zalecane dla produkcji.--classmap-authoritativeidzie dalej: klasy są ładowane wyłącznie z listy. Nieuwzględnionych plików autoloader wtedy w ogóle nie ładuje.- Przy każdym wdrożeniu lista jest generowana od nowa, aby nowe klasy zostały znalezione.
Autoloading a bezpieczeństwo po PHP2Shell
PHP2Shell dotyczy interpretera PHP, a nie autoloadera. Obowiązkowa pozostaje aktualizacja do PHP 8.5.12, 8.4.27, 8.3.36 lub 8.2.35 i restart wszystkich workerów. Po możliwym incydencie autoloader zasługuje jednak na szczególną uwagę: vendor/autoload.php i wszystkie pliki wpisane w files są wykonywane przy każdym żądaniu. Wprowadzony tam wiersz działa więc na całej stronie, bez rzucającego się w oczy nowego pliku w katalogu z przesłanymi plikami.
Proszę zatem sprawdzić, czy vendor i pliki autoloadera zgadzają się ze sprawdzonym stanem z systemu kontroli wersji. Pomocne są artykuły Wyszukiwanie ostatnio zmienionych plików oraz Porównywanie plików rdzenia CMS z oryginałem. W razie wątpliwości świeżo wygenerowany vendor z pliku lock jest bezpieczniejszy niż jakakolwiek pojedyncza naprawa.
Jak konkretnie pomaga PHP SOS?
- Porównujemy
vendor, autoloader i pliki wejściowe ze sprawdzonym stanem i oznaczamy każde odstępstwo. - Odtwarzamy zestaw pakietów z pliku lock i konfigurujemy zoptymalizowany autoloader.
- Sprawdzamy, czy w webrootcie leży tylko katalog publiczny, a kod źródłowy nie jest bezpośrednio dostępny przez URL.
- W razie infekcji przejmujemy oczyszczanie w ramach usuwania malware i dokumentujemy wynik.
Częste pytania
Od kiedy w PHP istnieją przestrzenie nazw?
Od PHP 5.3.0. Według podręcznika rozwiązują dwa problemy: kolizje nazw między własnym kodem, PHP i bibliotekami podmiotów trzecich oraz zbyt długie nazwy klas, które powstawały tylko po to, by takim kolizjom zapobiec.
Jaka jest różnica między PSR-0 a PSR-4?
Oba odwzorowują nazwy klas na ścieżki plików. W PSR-4 prefiks przestrzeni nazw nie jest powtarzany w ścieżce pliku, co daje płytsze katalogi. PSR-0 jest w PHP-FIG uznany za przestarzały, a PSR-4 jest zalecanym standardem.
Czy __autoload() jeszcze działa?
Nie. __autoload() oznaczono jako przestarzałe w PHP 7.2.0 i usunięto w PHP 8.0.0. Dziś autoloadery rejestruje się za pomocą spl_autoload_register(), w praktyce zwykle przez autoloader generowany przez Composera.
Czy po dodaniu nowej klasy muszę ponownie wygenerować autoloader?
Przy samych regułach PSR-4 nie, według dokumentacji Composera jest to właśnie zaleta PSR-4. Kto używa w produkcji zoptymalizowanej listy klas, musi ją generować od nowa przy każdym wdrożeniu.