Routing

OxPHP kieruje przychodzące żądania HTTP w jednym z trzech trybów, sterowanych pojedynczą zmienną środowiskową. Każdy tryb odzwierciedla znaną konfigurację try_files z nginx, więc możesz dokładnie przewidzieć, co stanie się z dowolnym URL-em.

Jak to działa

Każde żądanie przechodzi przez wspólny pipeline, zanim uruchomi się logika specyficzna dla danego trybu:

  1. Filtr ścieżek z kropką — ścieżki zawierające ukryte segmenty (.git, .env) są blokowane, z wyjątkiem /.well-known/* (RFC 8615)
  2. Odczyt z cache routingu — niedawno rozwiązane URI są zwracane z cache LRU (10 000 wpisów)
  3. Dekodowanie procentowe + sanityzacja — zakodowane sekwencje takie jak %2e%2e są dekodowane, a segmenty trawersowania (.., ., puste) są usuwane
  4. Blokada well-known PHP — obrona w głąb: skrypty .php wewnątrz /.well-known/ nigdy się nie wykonują
  5. Klasyfikacja URI — zsanityzowana ścieżka jest jednorazowo klasyfikowana jako NoExtension, Php lub OtherExtension
  6. Rozdział na tryby — każdy tryb obsługuje trzy rodzaje URI według własnych reguł
  7. Walidacja dowiązań symbolicznych — każda rozwiązana ścieżka w systemie plików musi po kanonizacji mieścić się wewnątrz katalogu głównego dokumentów

Krokiem kluczowym dla wydajności jest klasyfikacja: sprawdzenie na dysku zasobów statycznych (/style.css, /logo.png) wykonywane jest raz we wspólnej warstwie dla URI typu OtherExtension, dzięki czemu wszystkie trzy tryby ponoszą ten sam koszt.

Konfiguracja

Zmienna Domyślnie Opis
DOCUMENT_ROOT /var/www/html/public Katalog główny do serwowania plików i skryptów PHP
ENTRY_FILE (nieustawiona) Pojedynczy kanoniczny skrypt wejściowy. Nieustawiona = Tradycyjny. *.php = Framework. Nie-.php = SPA. Z WORKER_MODE_ENABLED=true = Worker. Przyjmuje ścieżkę bezwzględną lub względną wobec DOCUMENT_ROOT (dozwolone ..); rozwiązana ścieżka musi istnieć
WORKER_MODE_ENABLED false Włącza trwały tryb worker. Wymaga, aby ENTRY_FILE wskazywał na skrypt .php

Przestarzałe zmienne INDEX_FILE i WORKER_FILE są nadal parsowane (z ostrzeżeniem WARN przy starcie) i mapowane na nowy model. Zobacz Konfiguracja → Przestarzałe.

Tryby routingu

Tryby Tradycyjny, Framework i SPA wybierane są przez ENTRY_FILE, gdy WORKER_MODE_ENABLED=false, a każdy z nich mapuje się na równoważną konfigurację try_files w nginx.

Aktywny, gdy ENTRY_FILE nie jest ustawiony (lub jest pusty) oraz WORKER_MODE_ENABLED=false. Równoważna konfiguracja nginx:

nginx
location / { try_files $uri $uri/ /index.php /index.html =404; } location ~ \.php$ { try_files $uri =404; # PATH_INFO splitting enabled }

Kolejność rozwiązywania:

  1. $uri — dokładny plik na dysku → serwuj (lub wykonaj, jeśli .php)
  2. $uri/ — katalog → poszukaj index.php, a następnie index.html w jego wnętrzu
  3. Podział PATH_INFO — gdy URI zawiera .php/, prefiks skryptu jest dopasowywany na dysku, a reszta staje się PATH_INFO (np. /api.php/users/42 → skrypt api.php, PATH_INFO=/users/42)
  4. /index.php — fallback do kontrolera wejściowego w katalogu głównym
  5. /index.html — fallback do statycznego indeksu w katalogu głównym
  6. 404

Przykłady:

Żądanie Wynik
/about.php Wykonaj about.php
/style.css Serwuj style.css
/blog/ (z blog/index.php) Wykonaj blog/index.php
/api.php/users/42 Wykonaj api.php z PATH_INFO=/users/42
/missing.txt Fallback do /index.php
/some/route Fallback do /index.php

Podział PATH_INFO jest zawsze włączony w trybie Tradycyjnym. Nie ma przełącznika środowiskowego — poprzednia flaga SPLIT_PATH_INFO_ENABLED została usunięta.

Tryb Worker

Tryb worker aktywuje się, gdy WORKER_MODE_ENABLED=true, a ENTRY_FILE wskazuje na skrypt .php. Router serwuje zasoby statyczne z dysku i kieruje każde inne żądanie do workera ENTRY_FILE — to worker jest jedynym kontrolerem wejściowym.

Rodzaj URI Zachowanie
Zasoby statyczne (.css, .png, … — dowolne rozszerzenie inne niż .php) Serwowane bezpośrednio z dysku, jeśli są obecne; brakujący zasób przechodzi dalej do workera ENTRY_FILE (nie twarde 404)
Wszystko inne (URI .php, ścieżki bez rozszerzenia, /) Kierowane do workera ENTRY_FILE

Dowolne pliki .php w katalogu głównym dokumentów nigdy nie są wykonywane bezpośrednio w trybie worker — żądanie do /about.php dociera do callbacku workera jak każda inna trasa, nawet jeśli about.php istnieje na dysku. Nie ma tu również wyszukiwania indeksu katalogu ani fallbacku do głównego index.php; worker sam obsługuje te żądania.

Dwa wyjątki, oba będące zabezpieczeniami na poziomie serwera, które działają przed rozdziałem na tryby: ścieżki z segmentem-kropką (/.git/config, /.env, samo /.well-known) są odrzucane przez blokowanie ścieżek z kropką, a URI .php pod /.well-known/ są odrzucane jako obrona w głąb. Oba zwracają 404 i nigdy nie docierają do workera.

Walidacja przy starcie odrzuca dwie kombinacje:

  • WORKER_MODE_ENABLED=true bez ENTRY_FILEWORKER_MODE_ENABLED=true requires ENTRY_FILE to be set.
  • WORKER_MODE_ENABLED=true z ENTRY_FILE innym niż .phpWORKER_MODE_ENABLED=true requires a .php ENTRY_FILE.

Zobacz Tryb Worker, aby poznać pełne szczegóły konfiguracji.

Zachowanie PATH_INFO

$_SERVER['PATH_INFO'] jest wypełniane różnie w zależności od trybu:

Tryb Kiedy ustawiane Wartość
Traditional Tylko gdy URI zawiera .php/ (podział PATH_INFO) Końcówka za segmentem skryptu, np. /users/42
Framework Tylko dla jawnego żądania /index.php/extra Końcówka za plikiem wejściowym, np. /news
SPA Nigdy (PHP jest wywoływane tylko dla dokładnych plików .php; brak PATH_INFO)

PATH_INFO podąża za semantyką CGI: jest obecne tylko wtedy, gdy SCRIPT_NAME (wykonany skrypt) stanowi dosłowny prefiks ścieżki żądania. Przepisanie do kontrolera wejściowego, którego URL nie nazywa — trasa aplikacji, indeks katalogu, fallback po nietrafionym zasobie statycznym — nie niesie żadnego PATH_INFO; zamiast tego odczytaj REQUEST_URI. W trybie Tradycyjnym poprzednia zmienna środowiskowa SPLIT_PATH_INFO_ENABLED została usunięta.

Bezpieczeństwo ścieżek

OxPHP stosuje wiele warstw ochrony, aby zapobiec trawersowaniu katalogów, ujawnianiu ukrytych plików i atakom polegającym na ucieczce przez dowiązania symboliczne:

  • Dekodowanie procentowe działa przed sanityzacją, więc zakodowane próby trawersowania, takie jak /%2e%2e/etc/passwd, są wychwytywane
  • Filtrowanie segmentów usuwa segmenty .., . oraz puste z rozwiązanej ścieżki
  • Walidacja dowiązań symbolicznych kanonizuje każdą rozwiązaną ścieżkę i weryfikuje, że pozostaje ona wewnątrz katalogu głównego dokumentów. Dowiązania symboliczne wskazujące poza serwowany katalog są blokowane
  • Blokowanie ścieżek z kropką blokuje każdy segment ścieżki zaczynający się od . (np. /.git/config, /.env), z wyjątkiem /.well-known/* zgodnie z RFC 8615
  • Blokada well-known PHP — nawet przy wyjątku dla ścieżek z kropką, skrypty .php pod /.well-known/ nigdy nie są wykonywane (obrona w głąb)
  • Lista blokowania wykonywania PHP — w trybach bezpośredniego mapowania (Tradycyjny i SPA) PHP_DENY_PATHS blokuje wykonywanie .php dla skonfigurowanych wzorców glob (np. /uploads/** lub pojedynczy plik jak /admin/legacy.php) przed jakimkolwiek I/O dyskowym. Zobacz Lista blokowania wykonywania PHP
Note

Jeśli katalog główny dokumentów nie istnieje przy starcie, serwer kończy działanie z błędem krytycznym. Ochrona przed ucieczką przez dowiązania symboliczne wymaga prawidłowej, rozwiązywalnej ścieżki katalogu głównego dokumentów.

Rozwiązywanie problemów

Wszystkie żądania zwracają 404 w trybie Tradycyjnym

Sprawdź, czy index.php lub index.html istnieje w katalogu głównym dokumentów. Łańcuch try_files w trybie Tradycyjnym korzysta z nich jako fallbacku — jeśli obu brakuje i żaden plik nie pasuje do URL-a, otrzymasz 404.

bash
docker exec <container> ls /var/www/html/public
Brakujący zasób statyczny zwraca 404 zamiast powłoki SPA

W trybie SPA jest to zamierzone: brakujący /style.css daje twarde 404, a nie ciche przejście do index.html, co pozwala wcześnie wychwycić zepsute odwołania do zasobów. W trybach Framework i Tradycyjnym brakujący plik statyczny trafia do kontrolera wejściowego (/index.php), więc router Twojej aplikacji renderuje stronę 404. Użyj trybu SPA, jeśli chcesz twardych 404 dla brakujących zasobów.

Bezpośredni /index.php nie zwraca już 404

W trybie Framework bezpośredni dostęp do kontrolera wejściowego jest teraz dozwolony (przepisanie na /index.php jest idempotentne). Jeśli wcześniej polegałeś na 404, aby wykrywać bezpośrednie odwołania, przejdź na sprawdzanie REQUEST_URI z wnętrza kontrolera.

PATH_INFO jest puste w trybie Framework

Jest to oczekiwane dla tras aplikacji. Tryb Framework podąża za semantyką CGI: PATH_INFO jest ustawiane tylko wtedy, gdy żądanie jawnie wskazuje plik wejściowy z końcowym segmentem (/index.php/news/news). Dla zwykłej trasy aplikacji, takiej jak /users/42, kontroler wejściowy jest osiągany przez wewnętrzne przepisanie, którego trasa nie nazywa, więc PATH_INFO jest nieobecne — odczytaj ścieżkę z $_SERVER['REQUEST_URI']. (Jeśli ENTRY_FILE nie kończy się na .php, OxPHP wybiera tryb SPA, który nigdy nie wypełnia PATH_INFO.)

Dowiązanie symboliczne wewnątrz katalogu głównego dokumentów zwraca 404

Dowiązania symboliczne wskazujące poza katalog główny dokumentów są blokowane z założenia. Przenieś docelową zawartość do wnętrza katalogu głównego dokumentów lub podmontuj ją jako katalog pod właściwą ścieżką.

Przykład Docker

compose.yaml
services: app: image: ghcr.io/oxphp/oxphp:0.10.0 ports: - "8080:80" volumes: - ./src:/var/www/html environment: - DOCUMENT_ROOT=/var/www/html/public - ENTRY_FILE=index.php

Zobacz też