API de requête HTTP

OxPHP fournit une API orientée objet pour accéder aux données de requête HTTP. Au lieu de lire $_GET, $_POST, $_COOKIE, $_FILES et $_SERVER, vous appelez des méthodes sur un objet Request qui renvoie exactement ce que vous demandez, ni plus ni moins.

Table des matières

Vue d'ensemble

oxphp_http_request() renvoie un proxy en lecture seule vers les données de requête HTTP stockées dans le thread du worker courant. Les données sont récupérées de manière paresseuse. Un appel de méthode unique comme $request->header('Accept') accède directement à la structure de données côté Rust et renvoie uniquement cette valeur. Parmi les appels renvoyant le tableau complet, query() et payload() conservent leur résultat analysé sur l'objet sur lequel ils ont été appelés ; headers(), cookies() et files() reconstruisent le leur à chaque appel. Rien n'est conservé pour la requête : chaque appel à oxphp_http_request() renvoie un nouvel objet, avec un nouveau jeu de ces caches.

Pourquoi l'utiliser plutôt que les superglobales ?

  • L'analyse du corps JSON est intégrée. $request->payload() renvoie un corps application/json décodé quelle que soit la méthode de la requête, ainsi que les champs d'un corps de formulaire POST (application/x-www-form-urlencoded ou multipart/form-data), sans code supplémentaire. Les valeurs renvoyées exactes figurent dans Corps analysé.
  • Pas de fautes de frappe dans les clés de tableau. $request->method() est plus difficile à mal orthographier que $_SERVER['REQUEST_METHOD'].
  • Type des fichiers téléversés détecté. $request->file('avatar')->type() renvoie le type MIME déterminé à partir du contenu réel du fichier, et non la valeur fournie par le client.
  • Testable. Comme le comportement est défini par des interfaces, vous pouvez injecter des implémentations mock dans les tests unitaires.
  • Les superglobales restent disponibles. Définir SUPERGLOBALS_ENABLED=false est facultatif. L'API objet fonctionne dans tous les cas.

Obtenir l'objet Request

php
<?php $request = oxphp_http_request();

Appelez oxphp_http_request() n'importe où dans un script s'exécutant à l'intérieur d'une requête HTTP active, y compris dans le callback oxphp_worker() :

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

Méthodes de RequestInterface

URI et méthode

php
$request->method(): string

Renvoie la méthode HTTP en majuscules : "GET", "POST", "PUT", "PATCH", "DELETE", etc.

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

Vérification de méthode insensible à la casse.

php
$request->path(): string

Le chemin de l'URI sans la chaîne de requête : "/users/42".

php
$request->fullUri(): string

L'URI complète, comprenant le schéma, l'hôte, un port non standard optionnel, le chemin et la chaîne de requête : "https://example.com:8080/users/42?page=2". Les ports standard (80 pour HTTP, 443 pour HTTPS) sont omis.

php
$request->scheme(): string

"https" ou "http".

php
$request->isSecure(): bool

true lorsque le schéma est "https".

php
$request->host(): string

Nom d'hôte issu de l'en-tête Host. Renvoie une chaîne vide lorsque l'en-tête est absent (requêtes HTTP/1.0 sans en-tête Host).

php
$request->port(): int

Port issu de l'en-tête Host. Lorsqu'il n'est pas explicitement présent, renvoie la valeur par défaut du schéma : 80 pour HTTP, 443 pour HTTPS.

Derrière un proxy inverse

scheme(), isSecure(), host() et port() respectent X-Forwarded-Proto et X-Forwarded-Host lorsque TRUSTED_PROXIES inclut le pair. Sans proxys de confiance, ils reflètent la connexion directe.

php
$request->queryString(): ?string

La chaîne de requête brute sans le ? initial. Renvoie null lorsqu'il n'y a pas de chaîne de requête.

Protocole

php
$request->httpProtocol(): string

La chaîne de protocole complète : "HTTP/1.1" ou "HTTP/2".

php
$request->httpProtocolVersion(): string

Le numéro de version uniquement : "1.1" ou "2".

Paramètres de la chaîne de requête

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

Accès aux paramètres de la chaîne de requête.

Appel Renvoie
$request->query() Tous les paramètres sous forme de tableau plat de paires nom-valeur
$request->query('page') La valeur de page, ou null si absente
$request->query('page', 1) La valeur de page, ou 1 si absente
Lacune connue

La notation entre crochets n'est pas encore développée en tableaux imbriqués. ?tags[]=php&tags[]=async produit pour l'instant une entrée plate nommée tags[] plutôt qu'un tableau tags, et un nom répété se résout en sa première occurrence via query('name') alors que query() garde la dernière. Utilisez $_GET pour les paramètres de type tableau jusqu'à ce que ce soit corrigé.

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

Les valeurs trouvées sont toujours des chaînes. $default est renvoyé tel quel lorsque la clé est absente. Un paramètre envoyé sans valeur (?flag) et un paramètre envoyé vide (?flag=) se lisent tous deux comme '' — seul un paramètre absent donne null.

Décodage

Les noms et les valeurs sont décodés en pourcent (percent-decoded), et + est lu comme une espace — les mêmes règles que PHP applique en construisant $_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"

Les valeurs sont des chaînes d'octets, si bien qu'une séquence d'échappement encodant quelque chose qui n'est pas de l'UTF-8 valide survit intacte — un jeton signé ou un identifiant binaire peut encore être vérifié contre les octets tels qu'envoyés. queryString() reste le moyen de lire la chaîne de requête exactement telle qu'elle est arrivée, non décodée.

Les noms sont rapportés tels que le client les a envoyés, là où $_GET les rapporte tels que PHP les classe. PHP réécrit ' ', '.' et '[' dans les noms de superglobales, si bien que ?a.b=1 atterrit dans $_GET sous a_b :

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

Un nom est aussi gardé entier là où PHP le couperait. PHP traite les noms de superglobales comme des chaînes C, si bien que ?a%00b=1 devient $_GET['a'] alors que query() garde a\0b. N'y lisez pas une raison de valider via une API et de lire via l'autre : la troncature permet à un paramètre d'atterrir sur la clé d'un autre, si bien que ?a=1&a%00x=2 donne $_GET['a'] === '2' là où query('a') === '1', et les deux divergent sur la valeur plutôt que sur le seul ensemble des noms.

Au plus 1000 paramètres sont analysés. PHP applique sa propre limite, max_input_vars, dont la valeur par défaut est le même 1000 mais qui est configurable — relevez-la et $_GET contiendra des paramètres au-delà du point où query() s'arrête.

Corps analysé

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

Renvoie le corps de requête analysé. Le corps est analysé en fonction de l'en-tête Content-Type :

Content-Type Renvoie
application/x-www-form-urlencoded Tableau associatif des valeurs de champ, sur une requête POST ; un tableau vide pour toute autre méthode
multipart/form-data Tableau associatif des valeurs des champs texte, sur une requête POST ; un tableau vide pour toute autre méthode
application/json La valeur décodée : un tableau pour un objet ou un tableau, sinon le scalaire lui-même (string, int, float, bool) ; null si le JSON est invalide ou si le corps est un null littéral
Toute autre valeur null. La correspondance se fait par préfixe : tout ce qui commence par application/json (application/json-patch+json, par exemple) relève de la ligne JSON ci-dessus, tandis que text/json et application/vnd.api+json tombent ici
Les corps de formulaire sont réservés à POST

Un corps JSON est décodé quelle que soit la méthode de la requête. Un corps de formulaire ne l'est pas : les deux lignes de formulaire lisent ce que PHP a extrait du corps, et PHP ne le fait que pour POST, si bien que payload() sur un PUT ou un PATCH portant des champs de formulaire renvoie un tableau vide. Envoyez ces corps en JSON, ou lisez-les via body() et analysez-les vous-même.

Pour les deux types de formulaire, payload() renvoie les champs tels que PHP les a extraits du corps. Une écriture ultérieure dans $_POST ne modifie que la copie du tableau propre au script, et payload() ne la voit pas.

Le résultat analysé est mis en cache au premier appel et réutilisé par les appels suivants sur le même objet. Chaque appel à oxphp_http_request() renvoie un objet distinct doté de son propre cache, si bien qu'un deuxième objet reconstruit le résultat.

Appel Renvoie
$request->payload() L'intégralité du corps analysé
$request->payload(null, []) L'intégralité du corps analysé, ou [] en son absence : corps vide, Content-Type relevant de la dernière ligne ci-dessus, JSON invalide ou null JSON littéral
$request->payload('email') La valeur d'un seul champ, ou null si absente
$request->payload('email', '') La valeur d'un seul champ, ou '' si absente

Une recherche par clé ne porte que sur un payload de type tableau. Sur un corps JSON décodé en scalaire, il n'y a pas de clés, donc payload('anything') renvoie la valeur par défaut. Un corps qui se décode en false ou en 0 est renvoyé tel quel ; la valeur par défaut ne le remplace pas.

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"]

En-têtes

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

Renvoie la valeur brute de l'en-tête. Les noms d'en-tête sont insensibles à la casse. Pour les en-têtes à valeurs multiples (Accept, X-Forwarded-For), la ligne d'en-tête complète est renvoyée sous forme d'une seule chaîne. L'analyse est à votre charge.

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

Renvoie true si l'en-tête nommé est présent.

php
$request->headers(): array

Renvoie tous les en-têtes sous forme de tableau associatif. Chaque clé est le nom de l'en-tête tel que reçu (sans normalisation), et chaque valeur est la chaîne d'en-tête brute.

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" => "...", ...]

Cookies

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

Renvoie la valeur d'un cookie unique, ou $default si le cookie n'est pas présent. Un cookie envoyé vide (Cookie: a=) se lit comme '' ; seul un cookie absent donne $default.

php
$request->cookies(): array

Renvoie tous les cookies sous forme de tableau associatif de paires nom-valeur.

Décodage

Les cookies se décodent différemment des paramètres de requête, en suivant ce que PHP fait en construisant $_COOKIE : les valeurs sont décodées en pourcent, mais + reste un + littéral, et les noms ne sont pas décodés du tout.

php
// Cookie: token=a%20b; sep=a+b; %D0%BA=1 $request->cookie('token'); // "a b" — percent-escapes decoded $request->cookie('sep'); // "a+b" — unlike query(), "+" is not a space $request->cookie('%D0%BA'); // "1" — look up by the name as sent

Les noms sont rapportés tels qu'envoyés ici aussi. PHP réécrit ' ', '.' et '[' dans chaque nom de superglobale, cookies compris, si bien que Cookie: a.b=1 atterrit dans $_COOKIE sous a_b tandis que cookie('a.b') et cookies()['a.b'] utilisent le nom que le client a envoyé.

Les valeurs sont des chaînes d'octets, si bien qu'un cookie signé ou un setcookie('sid', random_bytes(16)) fait l'aller-retour avec exactement les octets avec lesquels il a été posé.

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

Corps brut

php
$request->body(): string

Renvoie les octets bruts du corps de la requête. C'est l'équivalent OxPHP de file_get_contents('php://input'). Contrairement à payload(), body() n'est pas mis en cache. Chaque appel accède à la structure de données sous-jacente.

php
$request->contentType(): ?string

Renvoie la valeur de l'en-tête Content-Type, ou null si absent.

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() et payload() sont indépendants. Vous pouvez appeler les deux dans la même requête.

Téléversement de fichiers

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

Renvoie le fichier téléversé pour le nom de champ donné, ou null si le champ n'est pas présent. Pour les champs de type tableau (name="photos[]"), renvoie le premier fichier.

php
$request->files(?string $name = null): array
Appel Renvoie
$request->files() Tous les fichiers téléversés sous forme de tableau plat de UploadedFileInterface
$request->files('photos') Tous les fichiers du champ photos (prend en charge name="photos[]")

Les deux construisent de nouveaux objets UploadedFileInterface à chaque appel. Deux appels à file('avatar') renvoient deux objets pour le même fichier téléversé, et un type que l'un d'eux a détecté avec type() n'est pas connu de l'autre. Conservez l'objet obtenu si vous en avez besoin plus d'une fois, comme le fait l'exemple ci-dessous.

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())); } }

Client

php
$request->ip(): string

Renvoie l'adresse IP du client. Lorsque TRUSTED_PROXIES est configuré et que le pair de la requête fait partie de l'ensemble de confiance, il s'agit de l'adresse non fiable la plus à droite issue de X-Forwarded-For ou de Forwarded (RFC 7239). Lorsque TRUSTED_PROXIES nomme cloudflare et que la chaîne de transfert montre que la requête est arrivée depuis un nœud de bordure Cloudflare, il s'agit plutôt de l'adresse CF-Connecting-IP. Sinon, il s'agit de l'IP du pair direct, généralement votre équilibreur de charge, et non le client final.

L'en-tête brut X-Forwarded-For reste accessible via $request->header('X-Forwarded-For') pour les cas avancés, mais l'analyser manuellement est rarement correct (adresse la plus à gauche vs la plus à droite, absence de vérification de confiance par CIDR). Configurez plutôt TRUSTED_PROXIES. Voir Proxys de confiance.

Chronométrage

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

Renvoie l'horodatage Unix correspondant au moment où cette requête a été reçue.

Appel Renvoie
$request->startTime() Secondes entières : 1711234567
$request->startTime(true) Flottant avec précision inférieure à la seconde : 1711234567.3412
php
<?php $elapsed = microtime(true) - $request->startTime(true); error_log(sprintf("Request took %.3fs so far", $elapsed));

Attributs

php
$request->attributes(): AttributesInterface

Renvoie le conteneur d'attributs mutable détenu par cet objet Request. Utilisez les attributs pour transmettre des données des middlewares aux gestionnaires de route et à tout autre code qui reçoit le même objet, plutôt que de recourir à une variable globale.

php
<?php // In authentication middleware $request->attributes()->set('user', $authenticatedUser); // In the route handler $user = $request->attributes()->get('user');
Le conteneur appartient à l'objet, pas à la requête

oxphp_http_request() renvoie un nouvel objet Request à chaque appel, et le conteneur que vous en obtenez est un nouveau conteneur, vide. Une écriture faite via un appel n'est pas lisible via un autre : get() renvoie alors la valeur par défaut, sans avertissement ni journalisation.

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

Si vous transmettez l'objet, comme le fait l'exemple ci-dessus avec $request, tout le code qui le détient lit et écrit le même conteneur, y compris une Fiber à laquelle vous le passez. Une Fiber qui appelle elle-même oxphp_http_request() obtient son propre conteneur, comme n'importe quel autre appelant. Comme les Fibers PHP sont coopératives, deux d'entre elles n'accèdent jamais à un conteneur au même moment.

Aucune réinitialisation entre les requêtes n'est nécessaire. Le conteneur est libéré en même temps que l'objet qui le détient, et le gestionnaire de la requête suivante appelle oxphp_http_request() et obtient le sien.

Session

php
$request->session(): ?SessionInterface

Renvoie une vue en lecture seule de $_SESSION. Renvoie null si session_start() n'a pas été appelé. La gestion de la session (démarrage, sauvegarde, destruction, écriture de valeurs) utilise les fonctions de session PHP standard.

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 est une vue en lecture seule de la session active.

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; }
Méthode Description
id() L'ID de session
name() Le nom de la session (par défaut : "PHPSESSID")
get(key, default) Une seule valeur de session, ou $default si la clé est absente
has(key) true si la clé existe dans $_SESSION
all() Toutes les données de session sous forme de tableau

Les valeurs de session reflètent l'état courant de $_SESSION au moment de l'appel, et non l'état au moment où session() a été appelé pour la première fois.

UploadedFileInterface

UploadedFileInterface représente un seul fichier téléversé.

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; }
Méthode Description
name() Nom de fichier d'origine envoyé par le client
clientType() Type MIME déclaré par le client — ne faites pas confiance à cette valeur pour les décisions de sécurité
type() Type MIME déterminé à partir du contenu réel du fichier via la détection par octets magiques (mime_content_type(), issue d'ext/fileinfo). Renvoie "application/octet-stream" lorsque le type ne peut pas être déterminé. Détecté au premier appel et conservé sur cet objet (voir Détection du type).
size() Taille du fichier en octets
tmpPath() Chemin du fichier temporaire sur le disque
error() L'une des constantes UPLOAD_ERR_*
isValid() true lorsque error() vaut UPLOAD_ERR_OK
moveTo(path) Déplace le fichier vers $path avec move_uploaded_file(), sans détecter son type. Renvoie false si le fichier est invalide ou si le déplacement échoue. Ce que lit type() après un déplacement est décrit sous Détection du type.

Vérifiez toujours isValid() avant d'utiliser un fichier téléversé. Utilisez type() plutôt que clientType() pour les décisions sensibles à la sécurité :

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');

Détection du type

Le premier appel à type() lit le fichier et conserve le résultat sur l'objet ; les appels suivants sur cet objet le renvoient sans relire le fichier. Cela inclut "application/octet-stream" après une détection échouée, y compris lorsque le premier appel a levé une exception sous un gestionnaire d'erreurs qui transforme les avertissements en exceptions : les appels suivants le renvoient sans avertissement. Un objet issu d'un autre appel à file() ou à files() relit le fichier lors de son propre premier appel.

Sans mime_content_type() (ext/fileinfo non chargée, ou la fonction listée dans disable_functions), chaque appel qui devrait lire le fichier lève un avertissement et renvoie "application/octet-stream" sans le conserver, ou lève une exception sous un gestionnaire d'erreurs qui transforme les avertissements en exceptions. OxPHP journalise aussi une ligne à ce sujet par processus, au niveau warn, dans le journal du serveur ou à l'endroit désigné par error_log lorsqu'il est défini. Les avertissements eux-mêmes sont journalisés comme n'importe quel autre avertissement PHP.

moveTo() ne détecte pas le type. Si type() n'a pas été appelé sur l'objet avant le déplacement, son premier appel après un déplacement réussi lit le fichier à la destination :

  • Un chemin local désigne le fichier déplacé, retrouvé par le chemin absolu auquel le déplacement l'a placé. Un changement ultérieur de répertoire de travail n'y change rien, y compris celui que PHP effectue lui-même avant l'exécution des fonctions de shutdown et des destructeurs.
  • Une URL de wrapper de flux est rouverte via son wrapper, et le type est détecté à partir de ce que celui-ci renvoie. Lorsque le wrapper ne peut pas ouvrir l'URL en lecture (avec un avertissement) ou ne peut pas faire de stat dessus (compress.zlib://, par exemple ; avec un avertissement sous PHP 8.4), type() renvoie "application/octet-stream", ou lève une exception sous un gestionnaire d'erreurs qui transforme les avertissements en exceptions.
  • Une URL data: n'est jamais décodée. move_uploaded_file() renomme le fichier vers elle comme s'il s'agissait d'un nom de fichier local, que type() lit, ou, si cela échoue, copie vers l'URL, qui n'accepte aucune écriture : seul un téléversement vide passe, sans que rien ne soit écrit, et type() ne trouve aucun fichier.
  • Un autre objet pour le même téléversement dont type() n'a pas été appelé avant le déplacement (un objet obtenu à nouveau avec file(), par exemple) ne trouve aucun fichier. mime_content_type() lève un avertissement pour le fichier manquant, et le type() de cet objet renvoie "application/octet-stream", ou lève une exception sous un gestionnaire d'erreurs qui transforme les avertissements en exceptions.
Tip

Appelez type() avant moveTo() lorsque la destination est un wrapper de flux, ou lorsque le fichier déplacé risque d'être déplacé de nouveau, supprimé ou réécrit avant que vous ne demandiez son type. L'objet conserve ce que cet appel a détecté.

AttributesInterface

AttributesInterface est la seule partie mutable de l'objet requête. Elle est conçue pour les métadonnées que votre propre code dérive (utilisateur authentifié, paramètres de route résolus, locale, indicateurs de fonctionnalités) et relit plus loin dans la même chaîne d'appels. Seul le code qui reçoit l'objet Request qui la détient peut voir ces données ; voir Attributs.

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; }
Méthode Description
get(key, default) Renvoie la valeur de $key, ou $default si absente
set(key, value) Stocke une valeur
has(key) true si la clé a été définie
remove(key) Supprime la clé
all() Tous les attributs sous forme de tableau associatif

Exceptions

Appeler oxphp_http_request() en dehors d'un contexte de requête active lève une exception du namespace OxPHP\Http\Exception.

php
namespace OxPHP\Http\Exception; class NoActiveRequestException extends \RuntimeException {} class AsyncContextException extends NoActiveRequestException {} class WorkerIdleException extends NoActiveRequestException {}
Exception Cas de levée
NoActiveRequestException Aucune requête HTTP active : CLI, MINIT, après l'arrêt, ou une Fiber qui survit à sa requête
AsyncContextException À l'intérieur d'un callback oxphp_async() — les workers asynchrones s'exécutent sur des threads séparés, sans contexte de requête
WorkerIdleException Mode worker, entre deux requêtes — le worker attend la requête suivante

AsyncContextException et WorkerIdleException étendent toutes deux NoActiveRequestException, donc capturer la classe de base gère tous les cas.

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 }

Dans le code normal de traitement des requêtes, vous n'avez pas besoin de ce try/catch. La protection par exception est utile dans le code d'amorçage susceptible de s'exécuter en dehors d'un contexte de requête.

SUPERGLOBALS_ENABLED

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

Par défaut, OxPHP remplit $_GET, $_POST, $_COOKIE, $_FILES et $_SERVER comme d'habitude. L'API objet HTTP est disponible aux côtés des superglobales dans ce mode.

Définir SUPERGLOBALS_ENABLED=false supprime le travail de description de la requête à PHP : les variables CGI et HTTP_* ne sont pas enregistrées, et la chaîne de requête n'est pas transmise à PHP pour analyse. L'effet est plus restreint que ne le suggère le nom. Le corps de la requête et l'en-tête Cookie parviennent à PHP dans tous les cas, et PHP construit les tableaux qui en dérivent quel que soit ce réglage :

Fonctionnalité Comportement avec SUPERGLOBALS_ENABLED=false
oxphp_http_request() Disponible ; toutes les méthodes répondent, y compris query() et payload()
php://input Disponible (c'est un flux, pas une superglobale)
$_SESSION Disponible (géré par le module de session de PHP)
header(), headers_list() Disponible (fonctions de sortie SAPI)
session_start(), session_*() Disponible (fonctions PHP natives)
$_POST, $_FILES, $_COOKIE Remplis comme d'habitude. Ils sont construits à partir du corps et de l'en-tête Cookie, auxquels ce réglage ne touche pas
$_REQUEST Rempli. PHP le fusionne comme toujours (voir Superglobales) ; seul manque ce que $_GET y aurait apporté
$_GET Vide
$_SERVER Pendant une requête, seulement quatre clés : REQUEST_TIME, REQUEST_TIME_FLOAT, argc et argv, toutes enregistrées par PHP lui-même et non par le serveur. REQUEST_METHOD, REQUEST_URI, les clés HTTP_* et l'environnement du processus sont absents. Le mode worker s'amorce différemment : le code situé au-dessus de oxphp_worker() s'exécute avant toute requête et voit un $_SERVER construit comme si le réglage était activé, environnement du processus compris, plus un REQUEST_URI fictif valant /

Ce réglage n'empêche donc pas les données de requête d'arriver dans les globales de PHP, ni l'analyse d'un corps de formulaire. Il ne supprime que le remplissage de $_SERVER à chaque requête (les variables CGI et HTTP_* plus l'environnement du processus) et l'analyse par PHP de la chaîne de requête vers $_GET.

Routage sur REQUEST_URI

Le code qui route sur $_SERVER['REQUEST_URI'] n'y trouve rien avec ce réglage et doit lire oxphp_http_request()->path() à la place. Dans un script d'entrée de worker, cela concerne le gestionnaire. L'amorçage situé au-dessus voit le / fictif décrit dans le tableau, qui n'est pas non plus la vraie route.

Utilisez oxphp_superglobals_enabled() pour vérifier le réglage courant à l'exécution :

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

Mode worker

En mode worker, oxphp_http_request() construit un nouvel objet Request à chaque appel, et non une fois par requête. Appelez-la dans le gestionnaire et transmettez l'objet là où il est nécessaire, comme le fait l'exemple ci-dessous.

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); });

Ne conservez pas un objet au-delà du gestionnaire qui l'a construit. Un objet conservé d'une requête à l'autre n'est pas invalidé et n'est jamais signalé comme périmé. method(), path(), header() et les autres lecteurs sans cache renvoient les données de la requête que le thread du worker sert au moment de l'appel. Les valeurs en cache (le tableau de query(), le payload() analysé, le conteneur d'attributs) conservent ce que chaque lecteur a capturé lors de son premier appel ; un lecteur appelé pour la première fois pendant la requête courante capture les données de cette requête. Rien sur un objet Request n'est vidé lorsque la requête suivante commence. Ce que couvre la réinitialisation légère entre les requêtes est détaillé dans Ce qui est réinitialisé et ce qui persiste.

Prise en charge des IDE

Installez le paquet de stubs pour obtenir l'autocomplétion et la vérification de types dans PhpStorm, VS Code, ou tout éditeur compatible LSP :

bash
composer require --dev oxphp/stubs

Le paquet de stubs fournit :

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

Aucune dépendance d'exécution n'est ajoutée. Le paquet est uniquement en require-dev.

Exemples

Mode traditionnel

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 avec corps 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()]);

Attributs de 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]);

Mode worker avec session

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(); } });
Fermez la session sur chaque chemin de sortie

Appeler session_write_close() sur chaque chemin de sortie place l'écriture, ainsi que la libération de tout ce que le gestionnaire de sauvegarde a verrouillé, au moment choisi par votre application. Une session laissée ouverte est tout de même écrite et fermée à la fin de sa requête, tant qu'aucune autre requête que porte le worker ne s'y trouve. Une session dans laquelle se trouve une autre requête reste ouverte, le gestionnaire de sauvegarde conservant tout ce qu'il a verrouillé, jusqu'à ce que le worker la rende. Une requête se trouve dans une session si elle l'a ouverte ou a été admise pendant qu'elle était ouverte, qu'elle appelle ou non session_start(). Ainsi, un flux de longue durée admis dans cet intervalle garde la session ouverte aussi longtemps qu'il s'exécute, et les requêtes admises entre-temps en héritent même après le retour de la requête qui l'a ouverte.

Ni la fermer vous-même ni la fermeture en fin de requête ne séparent les requêtes qui se chevauchent : un worker admet de nouvelles requêtes chaque fois que celle qu'il exécute se suspend, et une session encore ouverte à un point de suspension est remise à la requête admise dans cet intervalle. Une session fermée avec session_write_close() avant ce point laisse son identifiant installé, pour que la requête admise dans cet intervalle l'adopte avec son propre session_start(). Voir Mode worker.

Prompts pour votre agent

Ces prompts supposent un agent connecté au serveur MCP de la documentation OxPHP — il lira lui-même les pages dont il a besoin.

prompt
Fais passer mon code des superglobales à l'API de requête d'OxPHP. Lis la page `php/request-api` via le serveur MCP de la documentation OxPHP. Trouve les endroits de ce dépôt qui lisent directement les données de requête, et montre-moi la réécriture pour ceux où cela change le comportement et pas seulement le style.

Voir aussi

  • Superglobales — comment OxPHP remplit $_SERVER, $_GET, $_POST, $_COOKIE et $_FILES
  • Fonctions PHP — référence complète de oxphp_http_request(), oxphp_superglobals_enabled() et de toutes les autres fonctions intégrées
  • Mode worker — workers PHP persistants et cycle de vie de la requête
  • Référence de configuration — SUPERGLOBALS_ENABLED et autres variables d'environnement
Une erreur ? Signalez-la →