Работа с php://input и альтернативами для получения содержимого запроса
Основные методы получения тела запроса
При разработке веб-приложений часто требуется получить данные, отправленные в теле HTTP запроса. Это могут быть JSON, XML, данные формы или произвольный поток. В PHP существует несколько способов чтения тела запроса, каждый из которых подходит для определённых задач.
Универсальный способ: file_get_contents('php://input')
Наиболее универсальный и современный способ - использование потока php://input в сочетании с file_get_contents(). Этот метод работает для любого Content-Type, включая JSON, XML, простой текст.
$rawBody = file_get_contents('php://input');Php get body (получение тела запроса)
Затем данные можно декодировать, например, для JSON:
$data = json_decode($rawBody, true);Php get response (получение серверных переменных в php)
Пояснение: Поток php://input предоставляет доступ к необработанным данным тела запроса. file_get_contents() читает всё содержимое в строку. Если передать вторым аргументом true в json_decode(), результатом будет ассоциативный массив.
Типичные проблемы: Поток php://input можно прочитать только один раз. Для повторного доступа необходимо сохранить результат в переменную. При больших объёмах данных чтение целиком может потребовать много памяти; лучше использовать потоковое чтение. Также следует проверять, что данные не пусты, например, перед декодированием.
Как получить данные из HTML формы стандартным способом?
Для форм с Content-Type application/x-www-form-urlencoded или multipart/form-data PHP автоматически заполняет суперглобальный массив $_POST. Этот способ прост, но не подходит для других типов контента (например, JSON).
var_dump($_POST);Достаточно отправить форму методом POST, и данные станут доступны в $_POST.
Проблема: Если форма отправлена с нестандартным Content-Type или методом GET, $_POST будет пуст. Также $_POST не содержит файлов (для них есть $_FILES).
Как прочитать тело запроса в старых версиях PHP?
В PHP 5.6 и старше существовала директива always_populate_raw_post_data, которая заполняла переменную $HTTP_RAW_POST_DATA. Однако начиная с PHP 7.0 этот способ объявлен устаревшим и удалён в PHP 8.0. Использовать его не рекомендуется.
if (isset($HTTP_RAW_POST_DATA)) {
$body = $HTTP_RAW_POST_DATA;
}Проблемы: Полагаться на $HTTP_RAW_POST_DATA ненадёжно, так как он может быть не задан. В современных версиях PHP эта переменная отсутствует. Лучше использовать php://input.
Как прочитать тело запроса по частям для больших объёмов данных?
Открыть поток php://input с помощью fopen() и читать данные фрагментами через fread(). Это позволяет обрабатывать большие запросы без выделения всей памяти под строку.
$handle = fopen('php://input', 'r');
$body = '';
while (!feof($handle)) {
$body .= fread($handle, 8192);
}
fclose($handle);Размер буфера (8192 байт) можно регулировать. После чтения поток также становится недоступным для повторного чтения.
Проблемы: Следует проверять ошибки при открытии потока (if ($handle === false)). Нельзя читать из потока повторно. При работе с очень большими объёмами данных лучше обрабатывать данные по мере чтения, не накапливая всю строку.
Как упростить получение тела запроса с помощью библиотек?
Современные фреймворки, такие как Symfony или Laravel, предоставляют объекты Request с методами getContent() или getBody(). Внутри они используют php://input, но добавляют удобную обёртку, обрабатывают кодировку и позволяют несколько раз читать тело (кэшируют).
// Пример с Symfony
use Symfony\Component\HttpFoundation\Request;
$request = Request::createFromGlobals();
$body = $request->getContent();
$data = json_decode($body, true);Недостаток: Требуется подключение библиотеки, что может быть избыточно для простых скриптов. Однако в рамках фреймворка это стандартный подход.
Как разобрать строку запроса из тела с помощью parse_str?
Если тело запроса представлено в формате application/x-www-form-urlencoded, можно прочитать необработанные данные через php://input и затем разобрать их функцией parse_str(). Это альтернатива $_POST, когда нужно работать с сырыми данными.
$raw = file_get_contents('php://input');
parse_str($raw, $data);
print_r($data);Проблемы: Не работает для multipart/form-data. Для JSON или XML parse_str выдаст некорректный результат. Необходимо знать точный формат.
Расширенные примеры
Пример 1: Обработка JSON с валидацией
$rawBody = file_get_contents('php://input');
if ($rawBody === false || $rawBody === '') {
http_response_code(400);
echo json_encode(['error' => 'Empty body']);
exit;
}
$data = json_decode($rawBody, true);
if (json_last_error() !== JSON_ERROR_NONE) {
http_response_code(400);
echo json_encode(['error' => 'Invalid JSON: ' . json_last_error_msg()]);
exit;
}
print_r($data);Пример входящего JSON: {"name":"John","age":30}
Результат: Array ( [name] => John [age] => 30 )Пример 2: Ручной разбор multipart/form-data
$contentType = $_SERVER['CONTENT_TYPE'] ?? '';
if (preg_match('/boundary=(.+)$/i', $contentType, $matches)) {
$boundary = '--' . $matches[1];
$raw = file_get_contents('php://input');
$parts = explode($boundary, $raw);
foreach ($parts as $part) {
if (trim($part) === '' || trim($part) === '--') continue;
list($headersRaw, $bodyPart) = explode("\r\n\r\n", $part, 2);
$headers = explode("\r\n", $headersRaw);
$disposition = '';
foreach ($headers as $header) {
if (stripos($header, 'Content-Disposition') !== false) {
$disposition = $header;
break;
}
}
preg_match('/name="([^"]+)"/', $disposition, $nameMatch);
$fieldName = $nameMatch[1] ?? 'unknown';
$bodyPart = substr($bodyPart, 0, -2);
echo "Field $fieldName: $bodyPart\n";
}
}Пример multipart/form-data с полями name и file (без файла). Результат: Field name: John Field file: (содержимое файла)
Пример 3: Декодирование gzip на лету
$rawStream = fopen('php://input', 'r');
stream_filter_append($rawStream, 'zlib.inflate', STREAM_FILTER_READ);
$body = stream_get_contents($rawStream);
fclose($rawStream);
$data = json_decode($body, true);
print_r($data);Примечание: клиент должен отправлять заголовок Content-Encoding: gzip.
Если запрос приходит с gzip сжатием, результат будет декодированное JSON.
Пример 4: Универсальный обработчик на основе Content-Type
$contentType = $_SERVER['CONTENT_TYPE'] ?? '';
$rawBody = file_get_contents('php://input');
$data = [];
if (strpos($contentType, 'application/json') !== false) {
$data = json_decode($rawBody, true);
} elseif (strpos($contentType, 'application/xml') !== false || strpos($contentType, 'text/xml') !== false) {
$xml = simplexml_load_string($rawBody);
$data = json_decode(json_encode($xml), true);
} elseif (strpos($contentType, 'application/x-www-form-urlencoded') !== false) {
parse_str($rawBody, $data);
} else {
$data = $_POST;
}
print_r($data);Результат зависит от типа запроса. Например, для JSON выведет массив.
Пример 5: Чтение тела PUT запроса и сохранение в файл
$rawBody = file_get_contents('php://input');
file_put_contents('/tmp/request_body.dat', $rawBody);
echo 'Body saved, length: ' . strlen($rawBody);Сохраняет сырые данные в файл и выводит длину.