API żądania HTTP

OxPHP udostępnia obiektowe API do uzyskiwania dostępu do danych żądania HTTP. Zamiast czytać $_GET, $_POST, $_COOKIE, $_FILES i $_SERVER, wywołujesz metody na obiekcie Request, który zwraca dokładnie to, o co prosisz — nic więcej i nic mniej.

Spis treści

Przegląd

oxphp_http_request() zwraca proxy tylko do odczytu do danych żądania HTTP przechowywanych w bieżącym wątku worker. Dane są pobierane leniwie. Pojedyncze wywołanie metody, takie jak $request->header('Accept'), trafia bezpośrednio do struktury danych po stronie Rust i zwraca tylko tę wartość. Spośród wywołań zwracających całą tablicę query() i payload() zachowują sparsowany wynik w obiekcie, na którym zostały wywołane; headers(), cookies() i files() budują swój wynik od nowa przy każdym wywołaniu. Nic nie jest przechowywane na poziomie żądania: każde wywołanie oxphp_http_request() zwraca nowy obiekt z nowym zestawem tych buforów.

Dlaczego używać tego zamiast superglobalnych?

  • Parsowanie ciała JSON jest wbudowane. $request->payload() zwraca zdekodowane ciało application/json niezależnie od metody żądania, a także pola ciała formularza POST (application/x-www-form-urlencoded lub multipart/form-data) bez dodatkowego kodu. Dokładne zwracane wartości opisuje sekcja Sparsowane ciało.
  • Bez literówek w kluczach tablicy. $request->method() trudniej pomylić przy pisaniu niż $_SERVER['REQUEST_METHOD'].
  • Wykrywanie typu przesyłanych plików. $request->file('avatar')->type() zwraca typ MIME ustalony na podstawie faktycznej zawartości pliku, a nie wartości podanej przez klienta.
  • Testowalność. Ponieważ zachowanie jest zdefiniowane przez interfejsy, w testach jednostkowych możesz wstrzykiwać atrapy implementacji.
  • Superglobalne pozostają dostępne. Ustawienie SUPERGLOBALS_ENABLED=false jest opcjonalne. Obiektowe API działa niezależnie.

Uzyskiwanie obiektu Request

php
<?php $request = oxphp_http_request();

Wywołaj oxphp_http_request() w dowolnym miejscu skryptu wykonywanego wewnątrz aktywnego żądania HTTP, w tym wewnątrz wywołania zwrotnego oxphp_worker():

php
<?php oxphp_worker(function () { $request = oxphp_http_request(); $method = $request->method(); // ... });

Metody RequestInterface

URI i metoda

php
$request->method(): string

Zwraca metodę HTTP wielkimi literami: "GET", "POST", "PUT", "PATCH", "DELETE" itd.

php
$request->isMethod(string $method): bool

Sprawdzenie metody bez rozróżniania wielkości liter.

php
$request->path(): string

Ścieżka URI bez ciągu zapytania: "/users/42".

php
$request->fullUri(): string

Kompletny URI zawierający schemat, host, opcjonalny niestandardowy port, ścieżkę i ciąg zapytania: "https://example.com:8080/users/42?page=2". Standardowe porty (80 dla HTTP, 443 dla HTTPS) są pomijane.

php
$request->scheme(): string

"https" lub "http".

php
$request->isSecure(): bool

true, gdy schematem jest "https".

php
$request->host(): string

Nazwa hosta z nagłówka Host. Zwraca pusty ciąg znaków, gdy nagłówek jest nieobecny (żądania HTTP/1.0 bez nagłówka Host).

php
$request->port(): int

Port z nagłówka Host. Gdy nie jest wprost obecny, zwraca wartość domyślną dla schematu: 80 dla HTTP, 443 dla HTTPS.

Za odwrotnym proxy

scheme(), isSecure(), host() i port() uwzględniają X-Forwarded-Proto oraz X-Forwarded-Host, gdy TRUSTED_PROXIES obejmuje peera. Bez zaufanych proxy odzwierciedlają one bezpośrednie połączenie.

php
$request->queryString(): ?string

Surowy ciąg zapytania bez wiodącego ?. Zwraca null, gdy nie ma ciągu zapytania.

Protokół

php
$request->httpProtocol(): string

Pełny ciąg protokołu: "HTTP/1.1" lub "HTTP/2".

php
$request->httpProtocolVersion(): string

Sam numer wersji: "1.1" lub "2".

Parametry zapytania

php
$request->query(?string $key = null, mixed $default = null): mixed

Dostęp do parametrów ciągu zapytania.

Wywołanie Zwraca
$request->query() Wszystkie parametry jako płaską tablicę par nazwa-wartość
$request->query('page') Wartość page lub null, jeśli nieobecna
$request->query('page', 1) Wartość page lub 1, jeśli nieobecna
Znana luka

Notacja z nawiasami nie jest jeszcze rozwijana do tablic zagnieżdżonych. ?tags[]=php&tags[]=async daje obecnie płaski wpis o nazwie tags[], a nie tablicę tags, a powtórzona nazwa rozwiązuje się przez query('name') do swojego pierwszego wystąpienia, podczas gdy query() zachowuje ostatnie. Do czasu naprawy używaj $_GET dla parametrów tablicowych.

php
// Request: GET /search?q=oxphp&tags[]=php&tags[]=async $q = $request->query('q'); // "oxphp" $tags = $_GET['tags']; // ["php", "async"]

Znalezione wartości są zawsze ciągami znaków. $default jest zwracany bez zmian, gdy klucz jest nieobecny. Parametr wysłany bez wartości (?flag) i wysłany pusty (?flag=) oba odczytują się jako '' — tylko nieobecny parametr daje null.

Dekodowanie

Nazwy i wartości są dekodowane z zapisu procentowego, a + jest odczytywany jako spacja — te same reguły, które PHP stosuje przy budowaniu $_GET:

php
// Request: GET /search?q=%D0%9F%D1%80%D0%B8%D0%B2%D0%B5%D1%82&tag=a+b $request->query('q'); // "Привет" $request->query('tag'); // "a b"

Wartości są ciągami bajtów, więc escape kodujący coś, co nie jest poprawnym UTF-8, przetrwa nietknięty — podpisany token lub binarny identyfikator wciąż można zweryfikować względem bajtów w postaci, w jakiej zostały wysłane. queryString() pozostaje sposobem na odczytanie ciągu zapytania dokładnie tak, jak przyszedł, bez dekodowania.

Nazwy są raportowane tak, jak wysłał je klient, podczas gdy $_GET raportuje je tak, jak kataloguje je PHP. PHP przepisuje ' ', '.' i '[' w nazwach superglobalnych, więc ?a.b=1 ląduje w $_GET jako a_b:

php
// Request: GET /?a.b=1 $_GET['a_b']; // "1" $request->query('a.b'); // "1"

Nazwa pozostaje też cała tam, gdzie PHP by ją uciął. PHP obsługuje nazwy superglobalne jako ciągi C, więc ?a%00b=1 staje się $_GET['a'], podczas gdy query() zachowuje a\0b. Nie odczytuj tego jako powodu, by walidować przez jedno API, a czytać przez drugie: ucięcie pozwala jednemu parametrowi wylądować na kluczu innego, więc ?a=1&a%00x=2 daje $_GET['a'] === '2' tam, gdzie query('a') === '1', i oba API różnią się co do wartości, a nie tylko co do zbioru nazw.

Parsowanych jest co najwyżej 1000 parametrów. PHP stosuje własny limit, max_input_vars, który domyślnie wynosi te same 1000, ale jest konfigurowalny — podnieś go, a $_GET będzie zawierać parametry poza punktem, w którym query() się zatrzymuje.

Sparsowane ciało

php
$request->payload(?string $key = null, mixed $default = null): mixed

Zwraca sparsowane ciało żądania. Ciało jest parsowane zgodnie z nagłówkiem Content-Type:

Content-Type Zwraca
application/x-www-form-urlencoded Tablicę asocjacyjną wartości pól przy żądaniu POST; pustą tablicę przy każdej innej metodzie
multipart/form-data Tablicę asocjacyjną wartości pól tekstowych przy żądaniu POST; pustą tablicę przy każdej innej metodzie
application/json Zdekodowaną wartość: tablicę dla obiektu lub tablicy, w pozostałych przypadkach samą wartość skalarną (string, int, float, bool); null dla nieprawidłowego JSON-a oraz dla ciała będącego dosłownym null
Dowolna inna wartość null. Dopasowanie odbywa się po prefiksie, więc wszystko, co zaczyna się od application/json (na przykład application/json-patch+json), trafia do wiersza JSON powyżej, natomiast text/json i application/vnd.api+json lądują tutaj
Ciała formularzy tylko dla POST

Ciało JSON jest dekodowane niezależnie od metody żądania. Ciało formularza nie: oba wiersze formularzy odczytują to, na co PHP sparsował ciało, a PHP robi to wyłącznie dla POST, więc payload() dla PUT lub PATCH niosącego pola formularza zwraca pustą tablicę. Wysyłaj takie ciała jako JSON albo odczytaj je przez body() i sparsuj samodzielnie.

Dla obu typów formularzy payload() zwraca pola w postaci, w jakiej PHP sparsował je z ciała. Późniejszy zapis do $_POST zmienia tylko kopię tablicy należącą do skryptu i payload() go nie widzi.

Sparsowany wynik jest buforowany przy pierwszym wywołaniu i ponownie wykorzystywany przez kolejne wywołania na tym samym obiekcie. Każde wywołanie oxphp_http_request() zwraca osobny obiekt z własnym buforem, więc drugi obiekt buduje wynik od nowa.

Wywołanie Zwraca
$request->payload() Całe sparsowane ciało
$request->payload(null, []) Całe sparsowane ciało lub [], gdy go brak: puste ciało, Content-Type z ostatniego wiersza powyżej, nieprawidłowy JSON lub dosłowny JSON null
$request->payload('email') Wartość pojedynczego pola lub null, jeśli nieobecne
$request->payload('email', '') Wartość pojedynczego pola lub '', jeśli nieobecne

Wyszukiwanie po kluczu sięga tylko do payloadu będącego tablicą. W ciele JSON zdekodowanym do wartości skalarnej nie ma kluczy, więc payload('anything') zwraca wartość domyślną. Ciało dekodujące się do false lub 0 jest zwracane jako ta wartość; wartość domyślna jej nie zastępuje.

php
<?php // JSON request: POST /api/users // Content-Type: application/json // Body: {"name": "Alice", "role": "admin"} $name = $request->payload('name'); // "Alice" $role = $request->payload('role'); // "admin" $data = $request->payload(); // ["name" => "Alice", "role" => "admin"]

Nagłówki

php
$request->header(string $name, ?string $default = null): ?string

Zwraca surową wartość nagłówka. Nazwy nagłówków nie rozróżniają wielkości liter. Dla nagłówków wielowartościowych (Accept, X-Forwarded-For) cała linia nagłówka jest zwracana jako pojedynczy ciąg znaków. Parsowanie leży po Twojej stronie.

php
$request->hasHeader(string $name): bool

Zwraca true, jeśli wskazany nagłówek jest obecny.

php
$request->headers(): array

Zwraca wszystkie nagłówki jako tablicę asocjacyjną. Każdy klucz to nazwa nagłówka w otrzymanej postaci (bez normalizacji), a każda wartość to surowy ciąg nagłówka.

php
<?php $accept = $request->header('Accept'); // "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" if ($request->hasHeader('Authorization')) { $token = $request->header('Authorization'); } $all = $request->headers(); // ["Content-Type" => "application/json", "Accept" => "...", ...]

Ciasteczka

php
$request->cookie(string $name, ?string $default = null): ?string

Zwraca wartość pojedynczego ciasteczka lub $default, jeśli ciasteczko nie jest obecne. Ciasteczko wysłane puste (Cookie: a=) odczytuje się jako ''; tylko nieobecne ciasteczko daje $default.

php
$request->cookies(): array

Zwraca wszystkie ciasteczka jako tablicę asocjacyjną par nazwa-wartość.

Dekodowanie

Ciasteczka dekodują się inaczej niż parametry zapytania — zgodnie z tym, co robi PHP przy budowaniu $_COOKIE: wartości są dekodowane z zapisu procentowego, ale + pozostaje dosłownym +, a nazwy nie są dekodowane wcale.

php
// Cookie: token=a%20b; sep=a+b; %D0%BA=1 $request->cookie('token'); // "a b" — escape'y procentowe zdekodowane $request->cookie('sep'); // "a+b" — inaczej niż w query(), "+" nie jest spacją $request->cookie('%D0%BA'); // "1" — wyszukiwanie po nazwie w postaci wysłanej

Nazwy również tutaj są raportowane w postaci wysłanej. PHP przepisuje ' ', '.' i '[' w każdej nazwie superglobalnej, ciasteczka nie są wyjątkiem, więc Cookie: a.b=1 ląduje w $_COOKIE jako a_b, podczas gdy cookie('a.b') i cookies()['a.b'] używają nazwy wysłanej przez klienta.

Wartości są ciągami bajtów, więc podpisane ciasteczko lub setcookie('sid', random_bytes(16)) wraca jako dokładnie te bajty, którymi zostało ustawione.

php
<?php $theme = $request->cookie('theme', 'light'); // "dark" or "light" $session = $request->cookie('session'); // null if absent $all = $request->cookies(); // ["theme" => "dark", ...]

Surowe ciało

php
$request->body(): string

Zwraca surowe bajty ciała żądania. To odpowiednik file_get_contents('php://input') w OxPHP. W przeciwieństwie do payload(), body() nie jest buforowany. Każde wywołanie trafia do bazowej struktury danych.

php
$request->contentType(): ?string

Zwraca wartość nagłówka Content-Type lub null, jeśli jest nieobecny.

php
<?php // Read raw body for signature verification $raw = $request->body(); $signature = $request->header('X-Hub-Signature-256'); $valid = hash_hmac('sha256', $raw, $secret) === $signature;

body() i payload() są niezależne. Możesz wywołać oba w tym samym żądaniu.

Przesyłane pliki

php
$request->file(string $name): ?UploadedFileInterface

Zwraca przesłany plik dla podanej nazwy pola lub null, jeśli pole nie jest obecne. Dla pól tablicowych (name="photos[]") zwraca pierwszy plik.

php
$request->files(?string $name = null): array
Wywołanie Zwraca
$request->files() Wszystkie przesłane pliki jako płaską tablicę obiektów UploadedFileInterface
$request->files('photos') Wszystkie pliki dla pola photos (obsługuje name="photos[]")

Obie metody budują nowe obiekty UploadedFileInterface przy każdym wywołaniu. Dwa wywołania file('avatar') zwracają dwa obiekty dla tego samego przesłanego pliku, a typ, który jeden z nich wykrył przez type(), nie jest znany drugiemu. Jeśli potrzebujesz obiektu więcej niż raz, zachowaj ten, który dostałeś, tak jak robi to poniższy przykład.

php
<?php $avatar = $request->file('avatar'); if ($avatar && $avatar->isValid()) { $mime = $avatar->type(); // Detected from file contents, not the client claim $name = $avatar->name(); // Original filename $avatar->moveTo('/var/uploads/' . basename($name)); } // Multiple files $photos = $request->files('photos'); // UploadedFileInterface[] foreach ($photos as $photo) { if ($photo->isValid()) { $photo->moveTo('/var/uploads/' . basename($photo->name())); } }

Klient

php
$request->ip(): string

Zwraca adres IP klienta. Gdy TRUSTED_PROXIES jest skonfigurowany, a peer żądania należy do zaufanego zbioru, jest to skrajnie prawy niezaufany adres z X-Forwarded-For lub z Forwarded (RFC 7239). Gdy TRUSTED_PROXIES wymienia cloudflare, a łańcuch przekazywania pokazuje, że żądanie przyszło z węzła brzegowego Cloudflare, jest to zamiast tego adres z CF-Connecting-IP. W przeciwnym razie jest to IP bezpośredniego peera, zwykle Twojego load balancera, a nie klienta końcowego.

Surowy nagłówek X-Forwarded-For pozostaje dostępny przez $request->header('X-Forwarded-For') na potrzeby zaawansowanych przypadków, ale ręczne parsowanie go rzadko bywa poprawne (skrajnie lewy kontra skrajnie prawy, brak sprawdzenia zaufania na podstawie CIDR). Zamiast tego skonfiguruj TRUSTED_PROXIES. Zobacz Zaufane proxy.

Pomiar czasu

php
$request->startTime(bool $asFloat = false): int|float

Zwraca znacznik czasu Unix wskazujący, kiedy to żądanie zostało odebrane.

Wywołanie Zwraca
$request->startTime() Sekundy jako liczbę całkowitą: 1711234567
$request->startTime(true) Liczbę zmiennoprzecinkową z dokładnością poniżej sekundy: 1711234567.3412
php
<?php $elapsed = microtime(true) - $request->startTime(true); error_log(sprintf("Request took %.3fs so far", $elapsed));

Atrybuty

php
$request->attributes(): AttributesInterface

Zwraca modyfikowalny kontener atrybutów przechowywany przez ten obiekt Request. Używaj atrybutów, aby przekazywać dane z middleware do obsług tras i do każdego innego kodu, który otrzymuje ten sam obiekt, zamiast sięgać po zmienną globalną.

php
<?php // In authentication middleware $request->attributes()->set('user', $authenticatedUser); // In the route handler $user = $request->attributes()->get('user');
Kontener należy do obiektu, a nie do żądania

oxphp_http_request() zwraca nowy Request przy każdym wywołaniu, a kontener, który z niego pobierasz, jest nowy i pusty. Zapisu dokonanego przez jedno wywołanie nie da się odczytać przez inne: get() zwraca zamiast tego wartość domyślną i nic nie jest zgłaszane ani logowane.

php
<?php oxphp_http_request()->attributes()->set('tenant_id', $id); oxphp_http_request()->attributes()->get('tenant_id'); // null — a second Request, a second container

Jeśli przekazujesz obiekt dalej, tak jak przykład powyżej robi to z $request, cały kod, który go trzyma, czyta i zapisuje ten sam kontener, łącznie z Fiberem, któremu go przekażesz. Fiber, który sam wywołuje oxphp_http_request(), dostaje własny kontener, jak każdy inny wywołujący. Ponieważ Fibery PHP są kooperacyjne, żadne dwa z nich nie korzystają z kontenera w tym samym czasie.

Reset między żądaniami nie jest potrzebny. Kontener jest zwalniany razem z obiektem, który go przechowuje, a handler następnego żądania wywołuje oxphp_http_request() i dostaje własny.

Sesja

php
$request->session(): ?SessionInterface

Zwraca widok tylko do odczytu na $_SESSION. Zwraca null, jeśli session_start() nie zostało wywołane. Zarządzanie sesją (uruchamianie, zapisywanie, niszczenie, zapisywanie wartości) odbywa się za pomocą standardowych funkcji sesji PHP.

php
<?php session_start(); $session = $request->session(); $userId = $session->get('user_id'); $isAdmin = $session->get('is_admin', false); // Write session data using standard PHP functions $_SESSION['last_seen'] = time();

SessionInterface

SessionInterface to widok tylko do odczytu na aktywną sesję.

php
namespace OxPHP\Http; interface SessionInterface { public function id(): string; public function name(): string; public function get(string $key, mixed $default = null): mixed; public function has(string $key): bool; public function all(): array; }
Metoda Opis
id() ID sesji
name() Nazwa sesji (domyślnie: "PHPSESSID")
get(key, default) Pojedyncza wartość sesji lub $default, jeśli klucz jest nieobecny
has(key) true, jeśli klucz istnieje w $_SESSION
all() Wszystkie dane sesji jako tablica

Wartości sesji odzwierciedlają bieżący stan $_SESSION w momencie wywołania, a nie stan z chwili, gdy session() zostało wywołane po raz pierwszy.

UploadedFileInterface

UploadedFileInterface reprezentuje pojedynczy przesłany plik.

php
namespace OxPHP\Http; interface UploadedFileInterface { public function name(): string; public function clientType(): string; public function type(): string; public function size(): int; public function tmpPath(): string; public function error(): int; public function isValid(): bool; public function moveTo(string $destination): bool; }
Metoda Opis
name() Oryginalna nazwa pliku wysłana przez klienta
clientType() Typ MIME zadeklarowany przez klienta — nie ufaj tej wartości przy decyzjach dotyczących bezpieczeństwa
type() Typ MIME ustalony na podstawie faktycznej zawartości pliku za pomocą wykrywania magicznych bajtów (mime_content_type() z ext/fileinfo). Zwraca "application/octet-stream", gdy typu nie da się ustalić. Wykrywany przy pierwszym wywołaniu i zachowywany w tym obiekcie (zobacz Wykrywanie typu).
size() Rozmiar pliku w bajtach
tmpPath() Ścieżka do pliku tymczasowego na dysku
error() Jedna ze stałych UPLOAD_ERR_*
isValid() true, gdy error() ma wartość UPLOAD_ERR_OK
moveTo(path) Przenosi plik do $path za pomocą move_uploaded_file(), bez wykrywania jego typu. Zwraca false, jeśli plik jest nieprawidłowy lub przeniesienie się nie powiedzie. To, co type() odczytuje po przeniesieniu, opisuje Wykrywanie typu.

Zawsze sprawdzaj isValid() przed użyciem przesłanego pliku. Używaj type() zamiast clientType() przy podejmowaniu decyzji wrażliwych na bezpieczeństwo:

php
<?php $file = $request->file('document'); if (!$file || !$file->isValid()) { http_response_code(400); echo json_encode(['error' => 'Upload failed or missing']); return; } $detectedMime = $file->type(); $allowed = ['application/pdf', 'image/jpeg', 'image/png']; if (!in_array($detectedMime, $allowed, true)) { http_response_code(415); echo json_encode(['error' => "File type not allowed: $detectedMime"]); return; } $file->moveTo('/var/uploads/' . bin2hex(random_bytes(8)) . '.pdf');

Wykrywanie typu

Pierwsze wywołanie type() odczytuje plik i zachowuje wynik w obiekcie; kolejne wywołania na tym obiekcie zwracają go bez ponownego odczytu pliku. Dotyczy to także "application/octet-stream" po nieudanym wykryciu, również wtedy, gdy pierwsze wywołanie rzuciło wyjątek pod handlerem błędów zamieniającym ostrzeżenia w wyjątki: kolejne wywołania zwracają tę wartość bez ostrzeżenia. Obiekt z innego wywołania file() lub files() odczytuje plik ponownie przy własnym pierwszym wywołaniu.

Bez mime_content_type() (ext/fileinfo niezaładowane albo funkcja wymieniona w disable_functions) każde wywołanie, które miałoby odczytać plik, zgłasza ostrzeżenie i zwraca "application/octet-stream", nie zachowując tej wartości, albo rzuca wyjątek pod handlerem błędów zamieniającym ostrzeżenia w wyjątki. OxPHP loguje też o tym jedną linię na proces na poziomie warn, w logu serwera albo tam, gdzie wskazuje error_log, jeśli jest ustawione. Same ostrzeżenia są logowane jak każde inne ostrzeżenie PHP.

moveTo() nie wykrywa typu. Jeśli type() nie zostało wywołane na obiekcie przed przeniesieniem, jego pierwsze wywołanie po udanym przeniesieniu odczytuje plik w miejscu docelowym:

  • Ścieżka lokalna to przeniesiony plik, odnajdywany po ścieżce absolutnej, pod którą umieściło go przeniesienie. Późniejsza zmiana katalogu roboczego nie ma znaczenia, łącznie z tą, którą PHP wykonuje samo przed uruchomieniem funkcji zamykających i destruktorów.
  • URL wrappera strumienia jest otwierany ponownie przez swój wrapper, a typ jest wykrywany z tego, co ten zwróci. Tam, gdzie wrapper nie może otworzyć URL-a do odczytu (z ostrzeżeniem) albo nie może wykonać na nim stat (na przykład compress.zlib://; w PHP 8.4 z ostrzeżeniem), type() zwraca "application/octet-stream" albo rzuca wyjątek pod handlerem błędów zamieniającym ostrzeżenia w wyjątki.
  • URL data: nigdy nie jest dekodowany. move_uploaded_file() zmienia nazwę pliku na niego jak na lokalną nazwę pliku, którą type() odczytuje, albo, gdy to się nie uda, kopiuje do URL-a, który nie przyjmuje zapisów: przechodzi tylko pusty przesłany plik, bez zapisania czegokolwiek, a type() nie znajduje pliku.
  • Inny obiekt dla tego samego przesłanego pliku, na którym nie wywołano type() przed przeniesieniem (na przykład pobrany ponownie przez file()), nie znajduje pliku. mime_content_type() zgłasza ostrzeżenie o brakującym pliku, a type() tego obiektu zwraca "application/octet-stream" albo rzuca wyjątek pod handlerem błędów zamieniającym ostrzeżenia w wyjątki.
Tip

Wywołaj type() przed moveTo(), gdy miejscem docelowym jest wrapper strumienia albo gdy przeniesiony plik może zostać przeniesiony dalej, usunięty lub nadpisany, zanim zapytasz o jego typ. Obiekt zachowuje to, co wykryło to wywołanie.

AttributesInterface

AttributesInterface to jedyna modyfikowalna część obiektu żądania. Jest przeznaczony dla metadanych, które wyprowadza Twój własny kod (uwierzytelniony użytkownik, rozwiązane parametry trasy, ustawienia regionalne, flagi funkcji) i odczytuje ponownie dalej w tym samym łańcuchu wywołań. Te dane widzi tylko kod, który otrzymuje przechowujący je obiekt Request; zobacz Atrybuty.

php
namespace OxPHP\Http; interface AttributesInterface { public function get(string $key, mixed $default = null): mixed; public function set(string $key, mixed $value): void; public function has(string $key): bool; public function remove(string $key): void; public function all(): array; }
Metoda Opis
get(key, default) Zwraca wartość dla $key lub $default, jeśli nieobecna
set(key, value) Zapisuje wartość
has(key) true, jeśli klucz został ustawiony
remove(key) Usuwa klucz
all() Wszystkie atrybuty jako tablicę asocjacyjną

Wyjątki

Wywołanie oxphp_http_request() poza kontekstem aktywnego żądania rzuca wyjątek z przestrzeni nazw OxPHP\Http\Exception.

php
namespace OxPHP\Http\Exception; class NoActiveRequestException extends \RuntimeException {} class AsyncContextException extends NoActiveRequestException {} class WorkerIdleException extends NoActiveRequestException {}
Wyjątek Kiedy jest rzucany
NoActiveRequestException Brak aktywnego żądania HTTP: CLI, MINIT, po zamknięciu lub Fiber, który przeżył swoje żądanie
AsyncContextException Wewnątrz wywołania zwrotnego oxphp_async() — workery asynchroniczne działają na osobnych wątkach bez kontekstu żądania
WorkerIdleException Tryb worker, między żądaniami — worker czeka na kolejne żądanie

AsyncContextException i WorkerIdleException rozszerzają NoActiveRequestException, więc przechwycenie klasy bazowej obsługuje wszystkie przypadki.

php
<?php try { $request = oxphp_http_request(); } catch (\OxPHP\Http\Exception\AsyncContextException $e) { // Inside oxphp_async() — no request context here } catch (\OxPHP\Http\Exception\WorkerIdleException $e) { // Worker is between requests — do not call oxphp_http_request() here } catch (\OxPHP\Http\Exception\NoActiveRequestException $e) { // Any other case with no active request }

W normalnym kodzie obsługującym żądania to try/catch nie jest potrzebne. Zabezpieczenie wyjątkiem przydaje się w kodzie inicjalizacyjnym, który może działać poza kontekstem żądania.

SUPERGLOBALS_ENABLED

bash
SUPERGLOBALS_ENABLED=true # default — full backward compatibility SUPERGLOBALS_ENABLED=false # the $_SERVER request keys and $_GET are not built

Domyślnie OxPHP wypełnia $_GET, $_POST, $_COOKIE, $_FILES i $_SERVER jak zwykle. W tym trybie obiektowe API HTTP jest dostępne obok superglobalnych.

Ustawienie SUPERGLOBALS_ENABLED=false pomija pracę polegającą na opisaniu żądania dla PHP: zmienne CGI i HTTP_* nie są rejestrowane, a ciąg zapytania nie jest przekazywany PHP do sparsowania. Efekt jest węższy, niż sugeruje nazwa. Ciało żądania i nagłówek Cookie docierają do PHP tak czy inaczej, a PHP buduje wyprowadzone z nich tablice niezależnie od tego ustawienia:

Funkcja Zachowanie przy SUPERGLOBALS_ENABLED=false
oxphp_http_request() Dostępne; odpowiada każda metoda, łącznie z query() i payload()
php://input Dostępne (to strumień, a nie superglobalna)
$_SESSION Dostępne (zarządzane przez moduł sesji PHP)
header(), headers_list() Dostępne (funkcje wyjścia SAPI)
session_start(), session_*() Dostępne (natywne funkcje PHP)
$_POST, $_FILES, $_COOKIE Wypełniane jak zwykle. Są budowane z ciała i nagłówka Cookie, których to ustawienie nie dotyka
$_REQUEST Wypełniana. PHP scala ją jak zawsze (zobacz Zmienne superglobalne); brakuje tylko tego, co wniosłaby $_GET
$_GET Pusta
$_SERVER W trakcie żądania tylko cztery klucze: REQUEST_TIME, REQUEST_TIME_FLOAT, argc i argv, wszystkie zarejestrowane przez samo PHP, a nie przez serwer. REQUEST_METHOD, REQUEST_URI, klucze HTTP_* i środowisko procesu są nieobecne. Tryb worker startuje inaczej: kod powyżej oxphp_worker() działa przed jakimkolwiek żądaniem i widzi $_SERVER zbudowaną tak, jakby ustawienie było włączone, łącznie z całym środowiskiem procesu, plus zastępczy REQUEST_URI o wartości /

To ustawienie nie trzyma więc danych żądania z dala od globalnych PHP i nie powstrzymuje parsowania ciała formularza. Pomija jedynie składanie $_SERVER dla każdego żądania (zmienne CGI i HTTP_* plus środowisko procesu) oraz parsowanie ciągu zapytania do $_GET przez PHP.

Routing na podstawie REQUEST_URI

Kod, który routuje na podstawie $_SERVER['REQUEST_URI'], nie znajdzie tam niczego przy tym ustawieniu i musi zamiast tego odczytywać oxphp_http_request()->path(). W skrypcie wejściowym workera dotyczy to handlera. Bootstrap powyżej niego widzi zastępcze / opisane w tabeli, które również nie jest prawdziwą trasą.

Użyj oxphp_superglobals_enabled(), aby sprawdzić bieżące ustawienie w czasie działania:

php
<?php if (!oxphp_superglobals_enabled()) { $method = oxphp_http_request()->method(); } else { $method = $_SERVER['REQUEST_METHOD']; }

Tryb worker

W trybie worker oxphp_http_request() buduje nowy Request przy każdym wywołaniu, a nie raz na żądanie. Wywołuj ją wewnątrz handlera i przekazuj obiekt tam, gdzie jest potrzebny, tak jak robi to poniższy przykład.

worker.php
<?php // worker.php require __DIR__ . '/vendor/autoload.php'; $app = new MyApp\Application(); oxphp_worker(function () use ($app) { $request = oxphp_http_request(); $app->handle($request); });

Nie przechowuj obiektu dłużej niż handler, który go zbudował. Obiekt trzymany między żądaniami nie jest unieważniany i nigdy nie jest zgłaszany jako nieaktualny. method(), path(), header() i pozostałe niebuforowane metody odczytu zwracają dane z tego żądania, które wątek worker obsługuje w chwili wywołania. Wartości buforowane (tablica query(), sparsowany payload(), kontener atrybutów) przechowują to, co każda metoda przechwyciła przy pierwszym wywołaniu; metoda wywołana po raz pierwszy w trakcie bieżącego żądania przechwytuje dane tego żądania. Nic w obiekcie Request nie jest czyszczone, gdy rozpoczyna się kolejne żądanie. To, co obejmuje miękki reset między żądaniami, opisuje sekcja Co jest resetowane, a co zachowywane.

Wsparcie w IDE

Zainstaluj pakiet stubów, aby uzyskać autouzupełnianie i sprawdzanie typów w PhpStorm, VS Code lub dowolnym edytorze obsługującym LSP:

bash
composer require --dev oxphp/stubs

Pakiet stubów dostarcza:

text
oxphp-stubs/ ├── OxPHP/Http/ │ ├── RequestInterface.php │ ├── SessionInterface.php │ ├── UploadedFileInterface.php │ ├── AttributesInterface.php │ ├── Request.php │ ├── Session.php │ ├── UploadedFile.php │ ├── Attributes.php │ └── Exception/ │ ├── NoActiveRequestException.php │ ├── AsyncContextException.php │ └── WorkerIdleException.php └── functions.php

Nie dodaje żadnej zależności w czasie działania. Pakiet jest tylko dla require-dev.

Przykłady

Tryb tradycyjny

php
<?php $request = oxphp_http_request(); $method = $request->method(); // "GET" $path = $request->path(); // "/api/articles" $page = $request->query('page', 1); // "2" or 1 (default) // Authorization header if (!$request->hasHeader('Authorization')) { http_response_code(401); echo json_encode(['error' => 'Unauthorized']); exit; } $token = $request->header('Authorization'); // Structured logging with request metadata error_log(sprintf( '[%s] %s %s from %s', oxphp_request_id(), $method, $path, $request->ip() ));

POST z ciałem JSON

php
<?php $request = oxphp_http_request(); if (!$request->isMethod('POST')) { http_response_code(405); exit; } $email = $request->payload('email'); $password = $request->payload('password'); if (!$email || !$password) { http_response_code(400); echo json_encode(['error' => 'email and password are required']); exit; } // payload() handles JSON, form-urlencoded, and multipart // No manual json_decode() or $_POST check needed $user = authenticate($email, $password); header('Content-Type: application/json'); echo json_encode(['token' => $user->generateToken()]);

Atrybuty w middleware

php
<?php // auth-middleware.php function authenticate_request(\OxPHP\Http\RequestInterface $request): void { $token = $request->header('Authorization'); if (!$token) { http_response_code(401); exit; } $user = verify_token(str_replace('Bearer ', '', $token)); if (!$user) { http_response_code(403); exit; } $request->attributes()->set('user', $user); } // route-handler.php $request = oxphp_http_request(); authenticate_request($request); $user = $request->attributes()->get('user'); echo json_encode(['id' => $user->id, 'name' => $user->name]);

Tryb worker z sesją

worker.php
<?php // worker.php require __DIR__ . '/vendor/autoload.php'; oxphp_worker(function () { $request = oxphp_http_request(); if ($request->path() === '/login' && $request->isMethod('POST')) { $username = $request->payload('username'); $password = $request->payload('password'); if (verify_credentials($username, $password)) { session_start(); $_SESSION['user'] = $username; $_SESSION['authenticated'] = true; session_write_close(); header('Location: /dashboard'); } else { http_response_code(401); echo 'Invalid credentials'; } return; } if ($request->path() === '/dashboard') { session_start(); $session = $request->session(); if (!$session || !$session->get('authenticated')) { session_write_close(); header('Location: /login'); return; } echo 'Welcome, ' . htmlspecialchars($session->get('user')); session_write_close(); } });
Zamykaj sesję na każdej ścieżce wyjścia

Wywołanie session_write_close() na każdej ścieżce wyjścia umieszcza zapis — i zwolnienie wszystkiego, co zablokował handler zapisu sesji — w miejscu wybranym przez Twoją aplikację. Sesja pozostawiona otwarta i tak zostaje zapisana i zamknięta, gdy kończy się jej żądanie, o ile nie ma w niej żadnego innego żądania obsługiwanego przez workera. Sesja, w której jest inne żądanie, pozostaje otwarta, a handler zapisu sesji dalej trzyma wszystko, co zablokował, dopóki worker jej nie odda. Żądanie jest w sesji, jeśli ją otworzyło albo zostało przyjęte, gdy była otwarta, niezależnie od tego, czy kiedykolwiek wywoła session_start(). Długo żyjący strumień przyjęty w tym oknie utrzymuje więc sesję otwartą tak długo, jak działa, a żądania przyjęte w międzyczasie dostają ją nawet po tym, jak żądanie, które ją otworzyło, już się zakończyło.

Ani samodzielne zamknięcie, ani zamknięcie na końcu żądania nie rozdziela żądań, które się nakładają. Worker przyjmuje nowe żądania za każdym razem, gdy obsługiwane żądanie się zawiesza, a sesja wciąż otwarta w punkcie zawieszenia zostaje przekazana temu żądaniu, które zostanie przyjęte w tym oknie. Sesja zamknięta przez session_write_close() przed tym punktem zostawia ustawiony swój identyfikator, który żądanie przyjęte w tym oknie przejmuje własnym session_start(). Zobacz Tryb worker.

Prompty dla Twojego agenta

Te prompty zakładają agenta z podłączonym serwerem MCP dokumentacji OxPHP — sam przeczyta strony, których potrzebuje.

prompt
Przenieś mój kod z superglobalnych na API żądania OxPHP. Przeczytaj stronę `php/request-api` przez serwer MCP dokumentacji OxPHP. Znajdź w tym repozytorium miejsca, które czytają dane żądania bezpośrednio, i pokaż mi przepisanie tych, w których zmienia to zachowanie, a nie tylko styl.

Zobacz też

  • Superglobalne — jak OxPHP wypełnia $_SERVER, $_GET, $_POST, $_COOKIE i $_FILES
  • Funkcje PHP — pełne odniesienie do oxphp_http_request(), oxphp_superglobals_enabled() i wszystkich pozostałych wbudowanych funkcji
  • Tryb worker — trwałe workery PHP i cykl życia żądania
  • Odniesienie konfiguracji — SUPERGLOBALS_ENABLED i inne zmienne środowiskowe
Znalazłeś błąd? Zgłoś go →