Zmienne superglobalne
OxPHP wypełnia każdą standardową zmienną superglobalną PHP przed uruchomieniem Twojego skryptu, odwzorowując zachowanie, jakiego programiści PHP oczekują od tradycyjnej konfiguracji serwera. Wartości są dostępne od pierwszej linii kodu, bez potrzeby jakiejkolwiek inicjalizacji.
$_SERVER
OxPHP buduje $_SERVER na podstawie przychodzącego żądania HTTP zgodnie ze specyfikacją CGI/1.1. Najpierw importowane są zmienne środowiskowe procesu, a dopiero potem ustawiane są zmienne CGI, dzięki czemu wartości specyficzne dla żądania zawsze nadpisują kolidujące z nimi klucze środowiskowe.
Zmienne standardowe
| Zmienna | Opis | Przykład |
|---|---|---|
SCRIPT_FILENAME |
Bezwzględna ścieżka w systemie plików do wykonywanego skryptu PHP | /var/www/html/public/index.php |
DOCUMENT_ROOT |
Katalog główny serwera WWW skonfigurowany zmienną środowiskową DOCUMENT_ROOT |
/var/www/html/public |
SERVER_SOFTWARE |
Identyfikator serwera (zawiera wersję działającego OxPHP) | OxPHP/0.11.0 |
SERVER_PROTOCOL |
Wynegocjowana wersja protokołu HTTP | HTTP/2 |
REQUEST_METHOD |
Metoda HTTP | GET |
REQUEST_URI |
Pełny URI wraz z ciągiem zapytania | /app?page=2 |
SCRIPT_NAME |
Ścieżka wykonywanego skryptu względem DOCUMENT_ROOT — front controller w trybie Framework, a nie URI żądania |
/index.php |
DOCUMENT_URI |
Alias dla SCRIPT_NAME, dla zgodności z nginx/PHP-FPM |
/index.php |
PHP_SELF |
SCRIPT_NAME wraz z PATH_INFO, gdy jest obecne, w przeciwnym razie równe SCRIPT_NAME |
/index.php/user/42 |
QUERY_STRING |
Część zapytania URI (pusty ciąg znaków, gdy nie występuje) | page=2 |
SERVER_NAME |
Nazwa hosta z nagłówka Host |
example.com |
SERVER_PORT |
Port z nagłówka Host |
8080 |
REMOTE_ADDR |
Adres IP klienta | 172.17.0.1 |
REMOTE_PORT |
Numer portu klienta | 54321 |
HTTPS |
Ustawione na "on", gdy połączenie korzysta z TLS; w przeciwnym razie nieobecne |
on |
REQUEST_SCHEME |
"https" dla połączeń TLS, w przeciwnym razie "http" |
https |
CONTENT_TYPE |
Wartość nagłówka Content-Type (bez prefiksu HTTP_) |
application/json |
CONTENT_LENGTH |
Wartość nagłówka Content-Length (bez prefiksu HTTP_) |
128 |
REQUEST_TIME |
Znacznik czasu Unix (liczba całkowita) rozpoczęcia żądania | 1738800000 |
REQUEST_TIME_FLOAT |
Znacznik czasu Unix z dokładnością do mikrosekund | 1738800000.123456 |
GATEWAY_INTERFACE |
Ciąg znaków z wersją CGI | CGI/1.1 |
Gdy nagłówek Host jest nieobecny, SERVER_NAME przyjmuje domyślnie wartość localhost, a SERVER_PORT domyślnie 80 (lub 443 dla TLS).
Nagłówki żądania HTTP
Wszystkie nagłówki żądania HTTP są dodawane do $_SERVER z prefiksem HTTP_. Nazwy nagłówków są zamieniane na wielkie litery, a myślniki na podkreślenia, zgodnie z konwencjami CGI/1.1:
Accept: text/html -> HTTP_ACCEPT
X-Forwarded-For: 1.2.3.4 -> HTTP_X_FORWARDED_FOR
Authorization: Bearer abc -> HTTP_AUTHORIZATION
Cookie: session=xyz -> HTTP_COOKIEContent-Type i Content-Length pojawiają się bez prefiksu HTTP_ — jako CONTENT_TYPE i CONTENT_LENGTH — zgodnie z wymaganiami specyfikacji CGI.
Za odwrotnym proxy
Gdy TRUSTED_PROXIES jest skonfigurowane, a peer żądania należy do zaufanego zbioru, OxPHP przepisuje następujące klucze $_SERVER na podstawie nagłówków przekazujących (X-Forwarded-* lub Forwarded z RFC 7239):
| Zmienna | Wartość, gdy peer jest zaufany | Wartość w przeciwnym razie |
|---|---|---|
REMOTE_ADDR |
Skrajnie prawy niezaufany adres z X-Forwarded-For / Forwarded |
Adres IP bezpośredniego peera |
REMOTE_PORT |
Port źródłowy klienta z Forwarded: for=ip:port, w przeciwnym razie 0 |
Port bezpośredniego peera |
HTTPS |
"on", gdy X-Forwarded-Proto: https |
Ustawione tylko wtedy, gdy połączenie z peerem korzysta z TLS |
REQUEST_SCHEME |
"https" / "http" z X-Forwarded-Proto |
Na podstawie rzeczywistego stanu TLS |
SERVER_NAME |
Część z hostem z X-Forwarded-Host |
Część z hostem z nagłówka Host |
SERVER_PORT |
X-Forwarded-Port, w przeciwnym razie część z portem z X-Forwarded-Host, w przeciwnym razie 443/80 zależnie od schematu |
Część z portem z Host lub 443/80 |
Surowe klucze HTTP_X_FORWARDED_FOR, HTTP_X_FORWARDED_PROTO, HTTP_X_FORWARDED_HOST, HTTP_X_FORWARDED_PORT oraz HTTP_FORWARDED pozostają w $_SERVER bez zmian — dostępne są zarówno wartości przepisane, jak i oryginalne nagłówki.
REMOTE_PORT ma wartość "0" za zaufanym proxy, chyba że proxy wysyła Forwarded: for=ip:port zgodne z RFC 7239 — ani X-Forwarded-For, ani wybór skrajnie prawego niezaufanego adresu nie przenoszą portu źródłowego klienta, więc syntetyczna wartość jest zerowana, a nie zgadywana.
Gdy TRUSTED_PROXIES nie jest ustawione, przepisywanie nie następuje, a REMOTE_ADDR jest zawsze bezpośrednim peerem — zazwyczaj Twoim load balancerem, a nie końcowym klientem. Ręczne parsowanie X-Forwarded-For jest podatne na błędy (skrajnie lewy kontra skrajnie prawy, brak sprawdzania zaufania na podstawie CIDR); lepiej skonfigurować TRUSTED_PROXIES. Zobacz Zaufane proxy, aby poznać algorytm zaufania i składnię konfiguracji.
Zmienne kontekstu śledzenia
Gdy śledzenie rozproszone jest włączone, OxPHP dodaje do $_SERVER zmienne kontekstu śledzenia:
| Zmienna | Opis | Przykład |
|---|---|---|
OXPHP_TRACE_ID |
Identyfikator śledzenia W3C dla bieżącego żądania | 4bf92f3577b34da6a3ce929d0e0e4736 |
OXPHP_SPAN_ID |
Identyfikator spanu dla spanu serwera OxPHP | 00f067aa0ba902b7 |
OXPHP_PARENT_SPAN_ID |
Identyfikator spanu nadrzędnego z usługi nadrzędnej (pusty, jeśli jest korzeniem) | b9c7c989f97918e1 |
Zmienne te są obecne tylko wtedy, gdy przychodzi prawidłowy nagłówek traceparent lub gdy OxPHP generuje nowy ślad. Jeśli śledzenie nie jest skonfigurowane, klucze te są nieobecne.
Różnice względem PHP-FPM
Następujące zmienne zachowują się inaczej niż w standardowej konfiguracji PHP-FPM:
| Zmienna | Zachowanie |
|---|---|
SERVER_ADDR |
Nieustawione. OxPHP nie wypełnia lokalnego adresu IP serwera. |
PATH_INFO |
Ustawiane automatycznie — zobacz Zachowanie PATH_INFO poniżej. |
PATH_TRANSLATED |
Nieustawione. |
PHP_AUTH_USER / PHP_AUTH_PW / AUTH_TYPE |
Nie są wyodrębniane z nagłówka Authorization. Odczytaj $_SERVER['HTTP_AUTHORIZATION'] bezpośrednio. |
REDIRECT_STATUS |
Nieustawione. OxPHP nie korzysta z wewnętrznego mechanizmu przekierowań. |
Przykład
<?php
$method = $_SERVER['REQUEST_METHOD'];
$uri = $_SERVER['REQUEST_URI'];
$ip = $_SERVER['REMOTE_ADDR'];
$host = $_SERVER['SERVER_NAME'];
$scheme = $_SERVER['REQUEST_SCHEME']; // "http" or "https"
// Read a custom header
$token = $_SERVER['HTTP_AUTHORIZATION'] ?? '';
// REMOTE_ADDR is already the real client IP when TRUSTED_PROXIES is configured.
// Without it, REMOTE_ADDR is the direct peer (usually a load balancer).
$clientIp = $_SERVER['REMOTE_ADDR'];
// Check TLS without checking the port
if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on') {
// Secure connection
}Zachowanie PATH_INFO
$_SERVER['PATH_INFO'] jest wypełniane automatycznie na podstawie aktywnego trybu routingu. Nie ma żadnej flagi funkcji — wcześniejsza zmienna środowiskowa SPLIT_PATH_INFO_ENABLED została usunięta.
| Tryb routingu | Kiedy ustawiane | Wartość |
|---|---|---|
Traditional (ENTRY_FILE nieustawione) |
Tylko gdy URI zawiera .php/ i prefiks skryptu istnieje na dysku |
Końcówka po segmencie skryptu |
Framework (ENTRY_FILE=index.php) |
Tylko gdy żądanie jawnie wskazuje plik wejściowy z końcowym segmentem (/index.php/extra) |
Końcówka po pliku wejściowym, np. /news |
SPA (ENTRY_FILE=index.html) |
Nigdy — PHP działa wyłącznie dla dokładnych plików .php, bez PATH_INFO |
— |
SCRIPT_NAME zawsze wskazuje wykonywany skrypt (rozwiązany plik względem katalogu głównego dokumentów), więc przy zwykłym routingu PATH_INFO jest obecne tylko wtedy, gdy SCRIPT_NAME jest dosłownym prefiksem ścieżki żądania. Gdy żądanie zostaje przepisane na front controller, którego nie wskazuje (trasa aplikacji, indeks katalogu, awaryjny wariant przy nietrafieniu w plik statyczny), PATH_INFO jest nieobecne, a oryginalna ścieżka znajduje się w REQUEST_URI. (Wariant awaryjny PHP_DENY_PATHS to celowy wyjątek: ustawia PATH_INFO na oryginalny, oczyszczony URI, aby skrypt awaryjny mógł na jego podstawie routować.)
Przykłady trybu Traditional
OxPHP skanuje URI od lewej do prawej w poszukiwaniu pierwszego segmentu .php, któremu odpowiada rzeczywisty plik na dysku. Wszystko, co następuje po nim, staje się PATH_INFO:
| URI żądania | Plik na dysku | SCRIPT_NAME |
PATH_INFO |
PHP_SELF |
|---|---|---|---|---|
/app.php/user/42 |
app.php istnieje |
/app.php |
/user/42 |
/app.php/user/42 |
/index.php/api/v2/users |
index.php istnieje |
/index.php |
/api/v2/users |
/index.php/api/v2/users |
/app.php |
app.php istnieje |
/app.php |
(nieobecne) | /app.php |
/missing.php/foo |
plik nie znaleziony | przechodzi awaryjnie do /index.php |
— | zależy od wariantu awaryjnego |
Przykłady trybu Framework
Każde niestatyczne żądanie jest przepisywane na index.php. PATH_INFO jest ustawiane tylko wtedy, gdy żądanie jawnie wskazuje plik wejściowy z końcowym segmentem; w przypadku tras aplikacji oryginalna ścieżka jest odczytywana z REQUEST_URI.
| URI żądania | SCRIPT_NAME |
PATH_INFO |
|---|---|---|
/api/users |
/index.php |
(nieobecne) |
/about.php |
/index.php |
(nieobecne) |
/index.php/news/local |
/index.php |
/news/local |
/index.php |
/index.php |
(nieobecne) |
PATH_TRANSLATED nie jest wypełniane. W praktyce jest rzadko używane i domyślnie nie jest ustawiane ani przez nginx, ani przez PHP-FPM.
$_GET
Parametry ciągu zapytania są parsowane automatycznie z URI żądania.
<?php
// Request: GET /search?q=oxphp&page=2
$query = $_GET['q']; // "oxphp"
$page = $_GET['page']; // "2"Składnia tablicowa działa zgodnie z oczekiwaniami:
<?php
// Request: GET /filter?tags[]=php&tags[]=async
$tags = $_GET['tags']; // ["php", "async"]filter_input(), filter_input_array() i filter_has_var() nie czytają $_GET, $_POST ani $_COOKIE. Rozszerzenie filter trzyma własną kopię sparsowanego wejścia, a w trwałym workerze tę kopię wypełnia każde żądanie — więc każde żądanie musi ją też oddać, inaczej wartości zapytania, ciasteczka sesji i pola ciała jednego klienta pozostałyby czytelne dla każdego żądania, które ten worker obsłuży później. OxPHP oddaje ją na początku każdego żądania, więc te trzy funkcje odpowiadają dla żądania, które pyta, i dla żadnego innego.
Tryb worker dodaje jedno ograniczenie. Jeśli Twoje żądanie się zatrzyma (sleep(), await, podpięte wywołanie), a w tym oknie na tym workerze uruchomi się inne żądanie, magazyn staje się magazynem tamtego żądania. Odczyty po wznowieniu odpowiadają null, a nie cudzym wejściem, a kopia, którą miało Twoje żądanie, już wtedy nie istnieje. Wystarczy, że tamto żądanie tam się rozpocznie albo wznowi — nie musi się zakończyć. $_GET, $_POST i $_COOKIE podróżują z żądaniem przez zawieszenie i pozostają nietknięte, więc czytaj je — albo czytaj funkcje filter przed zawieszeniem.
Jedno wywołanie w ogóle nie może się zatrzymać. filter_input_array() z tablicą definicji per pole czyta magazyn raz na pole, więc FILTER_CALLBACK wykonujący I/O działa z workerem zajętym na cały czas swojego trwania, zamiast oddać go innemu żądaniu, a jawne Fiber::suspend() wewnątrz takiego callbacka rzuca wyjątek. Oczekiwanie i tak następuje — czeka wątek workera, a nie żądanie — więc wolny callback objawia się jako opóźnienie dla wszystkiego, co stoi za nim w kolejce, a po QUEUE_WAIT_TIMEOUT_MS te zakolejkowane żądania są zrzucane. Jak długo może to trwać, wyznacza to, z czym rozmawia callback, a nie serwer: wrapper strumienia poddaje się po default_socket_timeout (fabrycznie 60 sekund), a mysqlnd czeka mysqlnd.net_read_timeout (fabrycznie dobę). Daj takiemu wywołaniu jawny limit czasu — albo, lepiej, uruchamiaj walidację sięgającą do bazy danych czy cache'a na wartości, którą filter_input_array() już zwróciło.
$_POST
OxPHP obsługuje dwa standardowe typy zawartości dla przesyłanych formularzy:
application/x-www-form-urlencoded— standardowe dane formularza HTMLmultipart/form-data— przesyłanie plików w połączeniu z polami formularza
<?php
// Request: POST /login
// Content-Type: application/x-www-form-urlencoded
// Body: username=admin&password=secret
$username = $_POST['username']; // "admin"
$password = $_POST['password']; // "secret"W przypadku JSON lub innych typów zawartości użyj zamiast tego php://input:
<?php
// Request: POST /api/users
// Content-Type: application/json
// Body: {"name":"Alice","email":"[email protected]"}
$data = json_decode(file_get_contents('php://input'), true);
$name = $data['name']; // "Alice"
$email = $data['email']; // "[email protected]"filter_input(INPUT_POST, …) czyta własną kopię ciała trzymaną przez rozszerzenie filter, a nie $_POST — czym jest ta kopia i jak długo żyje, opisuje notka pod $_GET.
$_COOKIE
Ciasteczka są parsowane z nagłówka żądania Cookie.
<?php
// Request with: Cookie: session=abc123; theme=dark
$session = $_COOKIE['session']; // "abc123"
$theme = $_COOKIE['theme']; // "dark"Ciasteczka z prefiksem __oxp_ są zarezerwowane dla wewnętrznych wtyczek OxPHP. Są usuwane z nagłówka Cookie, zanim dotrze on do PHP, i nie pojawią się w $_COOKIE.
filter_input(INPUT_COOKIE, …) czyta własną kopię ciasteczek trzymaną przez rozszerzenie filter, a nie $_COOKIE — czym jest ta kopia i jak długo żyje, opisuje notka pod $_GET.
$_FILES
Pliki przesłane przez multipart/form-data wypełniają tablicę $_FILES standardową strukturą PHP:
<?php
// $_FILES['avatar'] structure:
// [
// 'name' => 'photo.jpg', // Original filename sent by the client
// 'type' => 'image/jpeg', // MIME type declared by the client
// 'tmp_name' => '/tmp/phpAb12Cd', // Temporary file path on the server
// 'error' => 0, // UPLOAD_ERR_OK (0 means no error)
// 'size' => 204800, // File size in bytes
// ]
if ($_FILES['avatar']['error'] === UPLOAD_ERR_OK) {
$tmp = $_FILES['avatar']['tmp_name'];
$name = basename($_FILES['avatar']['name']);
move_uploaded_file($tmp, "/uploads/$name");
}$_REQUEST
$_REQUEST to scalona tablica $_GET, $_POST i opcjonalnie $_COOKIE, budowana przez PHP zgodnie z dyrektywą INI request_order (domyślnie: "GP" — GET, a następnie POST). Samo scalanie przebiega według reguł PHP bez zmian.
<?php
// GET /form?action=preview with POST body: action=submit
$action = $_REQUEST['action']; // "submit" (POST overrides GET with default order)$_REQUEST jest przebudowywane dla każdego żądania. PHP normalnie buduje je leniwie, raz, gdy pierwszy raz ładowany jest skrypt, który o nim wspomina — co w trwałym workerze oznaczałoby, że każde późniejsze żądanie czyta parametry pierwszego. OxPHP wymusza zamiast tego przebudowę, więc scalona tablica zawsze opisuje obsługiwane właśnie żądanie.
$_ENV
$_ENV przechowuje środowisko procesu. W trybie tradycyjnym zachowuje się dokładnie tak, jak w PHP-FPM: PHP wypełnia je ponownie ze środowiska przy każdym żądaniu, zgodnie z variables_order.
Tryb worker je przypina. Worker inicjalizuje się raz, a loadery .env (vlucas/phpdotenv, symfony/dotenv, laravelowy Env) zapisują swoje wartości wprost do $_ENV, nie dotykając środowiska procesu. Wypełnianie $_ENV na nowo przy każdym żądaniu wymazywałoby więc konfigurację aplikacji od jej drugiego żądania, dlatego w trybie worker tablica — raz utworzona — jest zachowywana na cały czas życia workera: zapisy wykonane tam przez Twoją inicjalizację pozostają widoczne dla każdego żądania obsługiwanego przez tego workera.
<?php
// bootstrap, before oxphp_worker()
Dotenv\Dotenv::createImmutable(__DIR__)->load(); // writes into $_ENV
oxphp_worker(function () {
echo $_ENV['DATABASE_URL']; // still there on request 10_000
});filter_input(INPUT_ENV, …), filter_input_array(INPUT_ENV) i filter_has_var(INPUT_ENV, …) czytają w trybie worker środowisko procesu. Nie czytają $_ENV, więc wartości zapisanych tam przez Twoją inicjalizację wśród nich nie ma. Tak PHP zachowuje się wszędzie, ponieważ zapis do $_ENV daje tablicy jej własną kopię, a rozszerzenie filter dalej czyta migawkę środowiska zrobioną przez silnik. Tryb worker dodaje jeden niuans: migawką jest ta zrobiona, gdy $_ENV zostało po raz pierwszy zbudowane na workerze, więc późniejszego wywołania putenv() też w niej nie ma. getenv() czyta żywe środowisko i pozostaje nietknięte; $_ENV przechowuje wartości procesu plus to, co dodała Twoja inicjalizacja. Wszystko to dotyczy wyłącznie INPUT_ENV: INPUT_GET, INPUT_POST i INPUT_COOKIE czytają własny magazyn wewnątrz rozszerzenia filter, opisany w notce pod $_GET powyżej.
Przypinanie opiera się na domyślnym auto_globals_jit=1 w PHP. Przy auto_globals_jit=0 PHP wypełnia $_ENV ze środowiska procesu na nowo przy każdym żądaniu, zanim jakiekolwiek rozszerzenie zdąży zainterweniować, i wartości loadera .env nie przetrwają w trybie worker.
php://input
Surowe ciało żądania jest dostępne przez strumień php://input. To standardowy sposób odczytu ładunków JSON, XML lub dowolnego typu zawartości innego niż przesyłane formularze.
<?php
$body = file_get_contents('php://input');
$data = json_decode($body, true);php://input można przewijać i odczytywać wielokrotnie w obrębie tego samego żądania.
php://input jest puste dla żądań multipart/form-data. W ich przypadku użyj $_POST i $_FILES.
Wyłączanie zmiennych superglobalnych
Ustaw SUPERGLOBALS_ENABLED=false, aby wyłączyć wypełnianie $_GET, $_POST, $_COOKIE, $_FILES i $_SERVER. Po wyłączeniu tablice te są puste. Aby uzyskać dostęp do danych żądania, użyj zamiast tego API żądań HTTP (oxphp_http_request()).
SUPERGLOBALS_ENABLED=false # superglobals are empty arraysNastępujące elementy pozostają dostępne niezależnie od tego ustawienia:
| Co | Dlaczego |
|---|---|
$_SESSION |
Zarządzane przez moduł sesji PHP, a nie przez SAPI |
php://input |
Strumień, a nie zmienna superglobalna |
header(), headers_list() itp. |
Funkcje SAPI, a nie zmienne superglobalne |
session_start() i inne funkcje session_*() |
Natywne funkcje PHP |
oxphp_http_request() |
Zawsze dostępna — zalecana alternatywa |
Bieżące ustawienie można sprawdzić w czasie działania:
if (!oxphp_superglobals_enabled()) {
$request = oxphp_http_request();
$page = $request->query('page', 1);
}Zobacz też
- API żądań HTTP -- typowany, leniwie ładowany obiekt żądania jako alternatywa dla zmiennych superglobalnych
- Funkcje PHP --
oxphp_request_id(),oxphp_worker_id()i inne funkcje rozszerzenia - Tryb worker -- jak zmienne superglobalne są odświeżane między żądaniami workera
- Dokumentacja konfiguracji --
DOCUMENT_ROOTi inne zmienne konfiguracyjne serwera