Работа с php://input и альтернативами для получения содержимого запроса

Раздел: Веб-разработка -> Обработка HTTP запросов

Основные методы получения тела запроса

При разработке веб-приложений часто требуется получить данные, отправленные в теле 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);
Сохраняет сырые данные в файл и выводит длину.

Получение тела запроса - comments

En
Php get body (php)