API HTTP-запроса
OxPHP предоставляет объектно-ориентированный API для доступа к данным HTTP-запроса. Вместо чтения $_GET, $_POST, $_COOKIE, $_FILES и $_SERVER вы вызываете методы объекта Request, который возвращает ровно то, что вы запрашиваете, — не больше и не меньше.
Содержание
- Обзор
- Получение объекта Request
- Методы RequestInterface
- SessionInterface
- UploadedFileInterface
- AttributesInterface
- Исключения
- SUPERGLOBALS_ENABLED
- Режим воркеров
- Поддержка в IDE
- Примеры
Обзор
oxphp_http_request() возвращает прокси только для чтения к данным HTTP-запроса, хранящимся в текущем потоке воркера. Данные загружаются лениво. Одиночный вызов метода вроде $request->header('Accept') обращается напрямую к структуре данных на стороне Rust и возвращает только это значение. Вызовы, возвращающие массив целиком, вроде $request->headers(), строят массив один раз и кэшируют его в PHP-объекте на время обработки запроса.
Зачем использовать его вместо суперглобальных переменных?
- Разбор JSON-тела встроен.
$request->payload()разбираетapplication/json,application/x-www-form-urlencodedиmultipart/form-dataбез дополнительного кода. - Никаких опечаток в ключах массива. В
$request->method()труднее ошибиться, чем в$_SERVER['REQUEST_METHOD']. - Определение типа загруженных файлов.
$request->file('avatar')->type()возвращает MIME-тип, определённый по фактическому содержимому файла, а не по значению, переданному клиентом. - Тестируемость. Поскольку поведение задаётся интерфейсами, в модульных тестах можно внедрять mock-реализации.
- Суперглобальные переменные остаются доступны. Установка
SUPERGLOBALS_ENABLED=falseнеобязательна. Объектный API работает в любом случае.
Получение объекта Request
<?php
$request = oxphp_http_request();Вызывайте oxphp_http_request() в любом месте скрипта, выполняющегося в рамках активного HTTP-запроса, в том числе внутри колбэка oxphp_worker():
<?php
oxphp_worker(function () {
$request = oxphp_http_request();
$method = $request->method();
// ...
});Методы RequestInterface
URI и метод
$request->method(): stringВозвращает HTTP-метод в верхнем регистре: "GET", "POST", "PUT", "PATCH", "DELETE" и т. д.
$request->isMethod(string $method): boolПроверка метода без учёта регистра.
$request->path(): stringПуть URI без строки запроса: "/users/42".
$request->fullUri(): stringПолный URI, включая схему, хост, необязательный нестандартный порт, путь и строку запроса: "https://example.com:8080/users/42?page=2". Стандартные порты (80 для HTTP, 443 для HTTPS) опускаются.
$request->scheme(): string"https" или "http".
$request->isSecure(): booltrue, если схема — "https".
$request->host(): stringИмя хоста из заголовка Host. Возвращает пустую строку, если заголовок отсутствует (запросы HTTP/1.0 без заголовка Host).
$request->port(): intПорт из заголовка Host. Если он не указан явно, возвращает значение по умолчанию для схемы: 80 для HTTP, 443 для HTTPS.
scheme(), isSecure(), host() и port() учитывают X-Forwarded-Proto и X-Forwarded-Host, если удалённый узел (peer) входит в TRUSTED_PROXIES. Без доверенных прокси они отражают параметры прямого соединения.
$request->queryString(): ?stringНеобработанная строка запроса без ведущего ?. Возвращает null, если строки запроса нет.
Протокол
$request->httpProtocol(): stringПолная строка протокола: "HTTP/1.1" или "HTTP/2".
$request->httpProtocolVersion(): stringТолько номер версии: "1.1" или "2".
Параметры строки запроса
$request->query(?string $key = null, mixed $default = null): mixedДоступ к параметрам строки запроса.
| Вызов | Возвращает |
|---|---|
$request->query() |
Все параметры в виде массива, включая вложенные массивы |
$request->query('page') |
Значение page или null, если отсутствует |
$request->query('page', 1) |
Значение page или 1, если отсутствует |
Синтаксис с квадратными скобками (?tags[]=php&tags[]=async) разбирается во вложенные массивы:
// 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"]]Найденные значения всегда являются строками. $default возвращается как есть, если ключ отсутствует.
Разобранное тело запроса
$request->payload(?string $key = null, mixed $default = null): mixedВозвращает разобранное тело запроса. Тело разбирается в соответствии с заголовком Content-Type:
| Content-Type | Возвращает |
|---|---|
application/x-www-form-urlencoded |
Ассоциативный массив значений полей |
multipart/form-data |
Ассоциативный массив значений текстовых полей |
application/json |
Декодированный массив или скаляр; null при некорректном JSON |
| Любое другое значение | null |
payload() не ограничивается POST-запросами. Он работает с PUT, PATCH и любым другим методом, который отправляет тело. Разобранный результат кэшируется при первом вызове и переиспользуется на протяжении всего времени жизни запроса.
| Вызов | Возвращает |
|---|---|
$request->payload() |
Всё разобранное тело запроса |
$request->payload('email') |
Значение одного поля или null, если отсутствует |
$request->payload('email', '') |
Значение одного поля или '', если отсутствует |
<?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"]Заголовки
$request->header(string $name, ?string $default = null): ?stringВозвращает необработанное значение заголовка. Имена заголовков нечувствительны к регистру. Для заголовков с несколькими значениями (Accept, X-Forwarded-For) вся строка заголовка возвращается как единая строка. Разбор — на вашей ответственности.
$request->hasHeader(string $name): boolВозвращает true, если указанный заголовок присутствует.
$request->headers(): arrayВозвращает все заголовки в виде ассоциативного массива. Каждый ключ — это имя заголовка в том виде, в каком оно получено (без нормализации), а каждое значение — необработанная строка заголовка.
<?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): ?stringВозвращает значение одной cookie или $default, если cookie отсутствует.
$request->cookies(): arrayВозвращает все cookies в виде ассоциативного массива пар имя-значение.
<?php
$theme = $request->cookie('theme', 'light'); // "dark" or "light"
$session = $request->cookie('session'); // null if absent
$all = $request->cookies(); // ["theme" => "dark", ...]Сырое тело запроса
$request->body(): stringВозвращает необработанные байты тела запроса. Это эквивалент file_get_contents('php://input') в OxPHP. В отличие от payload(), body() не кэшируется. Каждый вызов обращается к нижележащей структуре данных.
$request->contentType(): ?stringВозвращает значение заголовка Content-Type или null, если он отсутствует.
<?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() и payload() независимы. Вы можете вызвать оба в рамках одного запроса.
Загрузка файлов
$request->file(string $name): ?UploadedFileInterfaceВозвращает загруженный файл для указанного имени поля или null, если поле отсутствует. Для полей-массивов (name="photos[]") возвращает первый файл.
$request->files(?string $name = null): array| Вызов | Возвращает |
|---|---|
$request->files() |
Все загруженные файлы в виде плоского массива UploadedFileInterface |
$request->files('photos') |
Все файлы для поля photos (поддерживает 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()));
}
}Клиент
$request->ip(): stringВозвращает IP-адрес клиента. Когда TRUSTED_PROXIES настроен и узел, от которого пришёл запрос, входит в доверенный набор, это самый правый недоверенный адрес из X-Forwarded-For или Forwarded (RFC 7239). В противном случае это IP непосредственного узла — как правило, вашего балансировщика нагрузки, а не конечного клиента.
Необработанный заголовок X-Forwarded-For по-прежнему доступен через $request->header('X-Forwarded-For') для нетривиальных случаев, но разбирать его вручную редко получается правильно (крайний левый или крайний правый адрес, отсутствие проверки доверия по CIDR). Вместо этого настройте TRUSTED_PROXIES. См. Доверенные прокси.
Тайминги
$request->startTime(bool $asFloat = false): int|floatВозвращает Unix-таймстамп момента получения этого запроса.
| Вызов | Возвращает |
|---|---|
$request->startTime() |
Целое число секунд: 1711234567 |
$request->startTime(true) |
Число с плавающей точкой с субсекундной точностью: 1711234567.3412 |
<?php
$elapsed = microtime(true) - $request->startTime(true);
error_log(sprintf("Request took %.3fs so far", $elapsed));Атрибуты
$request->attributes(): AttributesInterfaceВозвращает изменяемый контейнер атрибутов для текущего запроса. Используйте атрибуты, чтобы разделять данные между middleware, обработчиками маршрутов и другим кодом в рамках одного запроса без использования глобальных переменных.
<?php
// In authentication middleware
$request->attributes()->set('user', $authenticatedUser);
// In the route handler
$user = $request->attributes()->get('user');Атрибуты существуют в пределах одного запроса и сбрасываются при каждом новом запросе в режиме воркеров. При использовании файберов атрибуты являются общими для всех файберов, выполняющихся в одном потоке воркера для одного и того же запроса. Поскольку файберы PHP кооперативны, конкурентный доступ невозможен.
Сессия
$request->session(): ?SessionInterfaceВозвращает представление $_SESSION только для чтения. Возвращает null, если session_start() не был вызван. Управление сессией (запуск, сохранение, уничтожение, запись значений) выполняется стандартными функциями сессий 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 — это представление активной сессии только для чтения.
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;
}| Метод | Описание |
|---|---|
id() |
Идентификатор сессии |
name() |
Имя сессии (по умолчанию: "PHPSESSID") |
get(key, default) |
Одно значение из сессии или $default, если ключ отсутствует |
has(key) |
true, если ключ существует в $_SESSION |
all() |
Все данные сессии в виде массива |
Значения сессии отражают текущее состояние $_SESSION на момент вызова, а не состояние на момент первого вызова session().
UploadedFileInterface
UploadedFileInterface представляет один загруженный файл.
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;
}| Метод | Описание |
|---|---|
name() |
Исходное имя файла, отправленное клиентом |
clientType() |
MIME-тип, заявленный клиентом, — не доверяйте этому значению при принятии решений о безопасности |
type() |
MIME-тип, определённый по фактическому содержимому файла с помощью анализа магических байтов. Возвращает "application/octet-stream", если тип не удаётся определить. Кэшируется при первом вызове. |
size() |
Размер файла в байтах |
tmpPath() |
Путь к временному файлу на диске |
error() |
Одна из констант UPLOAD_ERR_* |
isValid() |
true, когда error() равно UPLOAD_ERR_OK |
moveTo(path) |
Перемещает файл в $path. Перед перемещением вызывает type(). Возвращает false, если файл некорректен или перемещение не удалось. |
Всегда проверяйте isValid() перед использованием загруженного файла. Используйте type(), а не clientType(), при принятии решений, связанных с безопасностью:
<?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 — единственная изменяемая часть объекта запроса. Он предназначен для хранения метаданных уровня запроса — аутентифицированного пользователя, разрешённых параметров маршрута, локали, флагов функций, — к которым нужен доступ из разных частей приложения.
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;
}| Метод | Описание |
|---|---|
get(key, default) |
Возвращает значение для $key или $default, если отсутствует |
set(key, value) |
Сохраняет значение |
has(key) |
true, если ключ был установлен |
remove(key) |
Удаляет ключ |
all() |
Все атрибуты в виде ассоциативного массива |
Исключения
Вызов oxphp_http_request() вне контекста активного запроса выбрасывает исключение из пространства имён OxPHP\Http\Exception.
namespace OxPHP\Http\Exception;
class NoActiveRequestException extends \RuntimeException {}
class AsyncContextException extends NoActiveRequestException {}
class WorkerIdleException extends NoActiveRequestException {}| Исключение | Когда выбрасывается |
|---|---|
NoActiveRequestException |
Нет активного HTTP-запроса: CLI, MINIT, после завершения работы или файбер, переживший свой запрос |
AsyncContextException |
Внутри колбэка oxphp_async() — асинхронные воркеры выполняются в отдельных потоках без контекста запроса |
WorkerIdleException |
Режим воркеров, между запросами — воркер ожидает следующий запрос |
AsyncContextException и WorkerIdleException наследуются от NoActiveRequestException, поэтому перехват базового класса покрывает все случаи.
<?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
}В обычном коде обработки запросов этот try/catch не нужен. Защита от исключений полезна в коде инициализации (bootstrap), который может выполняться вне контекста запроса.
SUPERGLOBALS_ENABLED
SUPERGLOBALS_ENABLED=true # default — full backward compatibility
SUPERGLOBALS_ENABLED=false # superglobals are empty arraysПо умолчанию OxPHP заполняет $_GET, $_POST, $_COOKIE, $_FILES и $_SERVER как обычно. В этом режиме объектный HTTP API доступен наряду с суперглобальными переменными.
Установка SUPERGLOBALS_ENABLED=false делает эти массивы пустыми, что устраняет затраты на их построение для каждого запроса. Перечисленное ниже продолжает работать независимо от этой настройки:
| Возможность | Поведение при SUPERGLOBALS_ENABLED=false |
|---|---|
oxphp_http_request() |
Всегда доступен |
php://input |
Доступен (это поток, а не суперглобальная переменная) |
$_SESSION |
Доступен (управляется модулем сессий PHP) |
header(), headers_list() |
Доступны (функции вывода SAPI) |
session_start(), session_*() |
Доступны (нативные функции PHP) |
$_GET, $_POST, $_COOKIE, $_FILES, $_SERVER |
Пустые массивы |
Используйте oxphp_superglobals_enabled(), чтобы проверить текущую настройку во время выполнения:
<?php
if (!oxphp_superglobals_enabled()) {
$method = oxphp_http_request()->method();
} else {
$method = $_SERVER['REQUEST_METHOD'];
}Режим воркеров
В режиме воркеров для каждого входящего запроса создаётся новый объект Request. Объект предыдущего запроса становится недействительным, как только запрос завершается. Не сохраняйте ссылку на него между запросами.
<?php
// worker.php
require __DIR__ . '/vendor/autoload.php';
$app = new MyApp\Application();
oxphp_worker(function () use ($app) {
$request = oxphp_http_request();
$app->handle($request);
});Все кэши объекта Request (разобранные заголовки, cookies, параметры строки запроса, тело запроса) очищаются автоматически при начале следующего запроса.
Поддержка в IDE
Установите пакет со стабами, чтобы получить автодополнение и проверку типов в PhpStorm, VS Code или любом редакторе с поддержкой LSP:
composer require --dev oxphp/stubsПакет со стабами предоставляет:
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Никакой зависимости времени выполнения не добавляется. Пакет предназначен только для require-dev.
Примеры
Традиционный режим
<?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 с 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()]);Атрибуты в 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]);Режим воркеров с сессией
<?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'));
}
});Смотрите также
- Суперглобальные переменные — как OxPHP заполняет
$_SERVER,$_GET,$_POST,$_COOKIEи$_FILES - Функции PHP — полный справочник по
oxphp_http_request(),oxphp_superglobals_enabled()и всем остальным встроенным функциям - Режим воркеров — постоянные процессы PHP и жизненный цикл запроса
- Справочник конфигурации —
SUPERGLOBALS_ENABLEDи другие переменные окружения