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
- Obtenir l'objet Request
- Méthodes de RequestInterface
- SessionInterface
- UploadedFileInterface
- AttributesInterface
- Exceptions
- SUPERGLOBALS_ENABLED
- Mode worker
- Prise en charge des IDE
- Exemples
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. Les appels renvoyant le tableau complet, comme $request->headers(), construisent le tableau une seule fois et le mettent en cache dans l'objet PHP pour la durée de la requête.
Pourquoi l'utiliser plutôt que les superglobales ?
- L'analyse du corps JSON est intégrée.
$request->payload()analyseapplication/json,application/x-www-form-urlencodedetmultipart/form-datasans code supplémentaire. - 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=falseest facultatif. L'API objet fonctionne dans tous les cas.
Obtenir l'objet Request
<?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
oxphp_worker(function () {
$request = oxphp_http_request();
$method = $request->method();
// ...
});Méthodes de RequestInterface
URI et méthode
$request->method(): stringRenvoie la méthode HTTP en majuscules : "GET", "POST", "PUT", "PATCH", "DELETE", etc.
$request->isMethod(string $method): boolVérification de méthode insensible à la casse.
$request->path(): stringLe chemin de l'URI sans la chaîne de requête : "/users/42".
$request->fullUri(): stringL'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.
$request->scheme(): string"https" ou "http".
$request->isSecure(): booltrue lorsque le schéma est "https".
$request->host(): stringNom 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).
$request->port(): intPort 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.
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.
$request->queryString(): ?stringLa chaîne de requête brute sans le ? initial. Renvoie null lorsqu'il n'y a pas de chaîne de requête.
Protocole
$request->httpProtocol(): stringLa chaîne de protocole complète : "HTTP/1.1" ou "HTTP/2".
$request->httpProtocolVersion(): stringLe numéro de version uniquement : "1.1" ou "2".
Paramètres de la chaîne de requête
$request->query(?string $key = null, mixed $default = null): mixedAccès aux paramètres de la chaîne de requête.
| Appel | Renvoie |
|---|---|
$request->query() |
Tous les paramètres sous forme de tableau, y compris les tableaux imbriqués |
$request->query('page') |
La valeur de page, ou null si absente |
$request->query('page', 1) |
La valeur de page, ou 1 si absente |
La notation entre crochets (?tags[]=php&tags[]=async) est analysée en tableaux imbriqués :
// Request: GET /search?q=oxphp&tags[]=php&tags[]=async
$q = $request->query('q'); // "oxphp"
$tags = $request->query('tags'); // ["php", "async"]
$all = $request->query(); // ["q" => "oxphp", "tags" => ["php", "async"]]Les valeurs trouvées sont toujours des chaînes. $default est renvoyé tel quel lorsque la clé est absente.
Corps analysé
$request->payload(?string $key = null, mixed $default = null): mixedRenvoie 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 |
multipart/form-data |
Tableau associatif des valeurs des champs texte |
application/json |
Tableau ou scalaire décodé ; null si le JSON est invalide |
| Toute autre valeur | null |
payload() n'est pas limité aux requêtes POST. Il fonctionne avec PUT, PATCH et toute autre méthode qui envoie un corps. Le résultat analysé est mis en cache au premier appel et réutilisé pendant toute la durée de la requête.
| Appel | Renvoie |
|---|---|
$request->payload() |
L'intégralité du corps analysé |
$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 |
<?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
$request->header(string $name, ?string $default = null): ?stringRenvoie 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.
$request->hasHeader(string $name): boolRenvoie true si l'en-tête nommé est présent.
$request->headers(): arrayRenvoie 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
$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
$request->cookie(string $name, ?string $default = null): ?stringRenvoie la valeur d'un cookie unique, ou $default si le cookie n'est pas présent.
$request->cookies(): arrayRenvoie tous les cookies sous forme de tableau associatif de paires nom-valeur.
<?php
$theme = $request->cookie('theme', 'light'); // "dark" or "light"
$session = $request->cookie('session'); // null if absent
$all = $request->cookies(); // ["theme" => "dark", ...]Corps brut
$request->body(): stringRenvoie 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.
$request->contentType(): ?stringRenvoie la valeur de l'en-tête Content-Type, ou null si absent.
<?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
$request->file(string $name): ?UploadedFileInterfaceRenvoie 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.
$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[]") |
<?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
$request->ip(): stringRenvoie 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). 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
$request->startTime(bool $asFloat = false): int|floatRenvoie 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
$elapsed = microtime(true) - $request->startTime(true);
error_log(sprintf("Request took %.3fs so far", $elapsed));Attributs
$request->attributes(): AttributesInterfaceRenvoie le conteneur d'attributs mutable pour la requête courante. Utilisez les attributs pour partager des données entre les middlewares, les gestionnaires de route et tout autre code de la même requête, sans recourir à des variables globales.
<?php
// In authentication middleware
$request->attributes()->set('user', $authenticatedUser);
// In the route handler
$user = $request->attributes()->get('user');Les attributs sont propres à chaque requête et sont réinitialisés à chaque nouvelle requête en mode worker. Lorsque vous utilisez des Fibers, les attributs sont partagés entre toutes les Fibers s'exécutant sur le même thread de worker pour la même requête. Comme les Fibers PHP sont coopératives, un accès concurrent n'est pas possible.
Session
$request->session(): ?SessionInterfaceRenvoie 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
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.
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é.
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. Renvoie "application/octet-stream" lorsque le type ne peut pas être déterminé. Mis en cache au premier appel. |
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. Appelle type() avant le déplacement. Renvoie false si le fichier est invalide ou si le déplacement échoue. |
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
$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');AttributesInterface
AttributesInterface est la seule partie mutable de l'objet requête. Elle est conçue pour stocker des métadonnées propres à la requête — utilisateur authentifié, paramètres de route résolus, locale, indicateurs de fonctionnalités — auxquelles plusieurs parties de l'application doivent accéder.
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.
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
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
SUPERGLOBALS_ENABLED=true # default — full backward compatibility
SUPERGLOBALS_ENABLED=false # superglobals are empty arraysPar 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 rend ces tableaux vides, ce qui élimine le coût de leur construction à chaque requête. Les éléments suivants fonctionnent toujours, quel que soit ce réglage :
| Fonctionnalité | Comportement avec SUPERGLOBALS_ENABLED=false |
|---|---|
oxphp_http_request() |
Toujours disponible |
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) |
$_GET, $_POST, $_COOKIE, $_FILES, $_SERVER |
Tableaux vides |
Utilisez oxphp_superglobals_enabled() pour vérifier le réglage courant à l'exécution :
<?php
if (!oxphp_superglobals_enabled()) {
$method = oxphp_http_request()->method();
} else {
$method = $_SERVER['REQUEST_METHOD'];
}Mode worker
En mode worker, un nouvel objet Request est créé pour chaque requête entrante. L'objet de la requête précédente devient invalide une fois la requête terminée. Ne conservez pas de référence vers celui-ci entre les requêtes.
<?php
// worker.php
require __DIR__ . '/vendor/autoload.php';
$app = new MyApp\Application();
oxphp_worker(function () use ($app) {
$request = oxphp_http_request();
$app->handle($request);
});Tous les caches de l'objet Request (en-têtes analysés, cookies, paramètres de requête, payload) sont vidés automatiquement lorsque la requête suivante commence.
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 :
composer require --dev oxphp/stubsLe paquet de stubs fournit :
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.phpAucune dépendance d'exécution n'est ajoutée. Le paquet est uniquement en require-dev.
Exemples
Mode traditionnel
<?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
$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
// 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
<?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;
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')) {
header('Location: /login');
return;
}
echo 'Welcome, ' . htmlspecialchars($session->get('user'));
}
});Voir aussi
- Superglobales — comment OxPHP remplit
$_SERVER,$_GET,$_POST,$_COOKIEet$_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 — processus PHP persistants et cycle de vie de la requête
- Référence de configuration —
SUPERGLOBALS_ENABLEDet autres variables d'environnement