PHP 8.4: Новая функция request_parse_body
request_parse_body, раскрывающая встроенную в PHP функциональность парсинга HTTP-запросов отличных от POST.PHP автоматически разбирает HTTP POST-запросы, чтобы заполнить суперглобальные переменные $_POST и $_FILES. Однако другие HTTP-запросы с такими методами, как PUT и PATCH, не разбираются автоматически, и разбор данных запроса остаётся за PHP-приложением.
С популярностью REST API, в которых всё чаще используются такие HTTP-методы, как PUT, DELETE и PATCH, разбор данных HTTP-запросов становится всё более важным. Однако автоматический разбор данных HTTP-запроса для запросов, не относящихся к POST, может стать серьёзным изменением для существующих PHP-приложений.
PHP предоставляет обёртку потока по адресу php://input, содержащую данные запроса. Для POST-запросов с enctype="multipart/form-data" эта потоковая обёртка остаётся пустой, потому что она автоматически парсится и используется для заполнения переменных $_POST и $_FILES. Автоматической обработкой $_POST/$_FILES можно управлять с помощью INI-настройки enable_post_data_reading=Off.
Когда INI-значение enable_post_data_reading установлено в Off, или когда метод HTTP-запроса имеет значение, отличное от POST, потоковая обёртка php://input может быть прочитана в пользовательском PHP-коде для разбора данных HTTP-запроса.
curl --request PUT \
     --location 'https://example.com/post.php' \
     --form 'test="123"'Из PHP-приложения данные формы, полученные в приведённом выше вызове Curl, могут быть прочитаны из потока php://input.
echo file_get_contents('php://input');----------------------------690112416382325217174003
Content-Disposition: form-data; name="test"
123
----------------------------690112416382325217174003--Новая функция request_parse_body
В PHP 8.4 добавлена новая функция request_parse_body, раскрывающая встроенную в PHP функциональность парсинга запросов для других методов HTTP-запросов.
/**
 * Parse and consume php://input and return the values for $_POST
 * and $_FILES variables.
 *
 * @param array<string, int|string>|null $options Overrides for INI values
 * @return array<int, array> Array with key 0 being the post data
 *   (similar to $_POST), and key 1 being the files ($_FILES).
 */
function request_parse_body(?array $options = null): array {}При вызове функция request_parse_body считывает всё содержимое, доступное в потоке php://input, и создаёт значения, которые могут быть использованы в переменных $_POST и $_FILES.
Возвращаемое значение будет представлять собой массив с двумя ключами, 0 и 1, содержащий разобранные значения, которые могут быть использованы в качестве $_POST (индекс ключа массива 0) и $_FILES (индекс 1). Оба ключа массива будут присутствовать всегда — даже если нет данных запроса и/или файлов.
Можно заполнить значения $_POST и $_FILES непосредственно из возвращаемых значений:
[$_POST, $_FILES] = request_parse_body();Обратите внимание, что разбор запроса по-прежнему связан с ограничениями, установленными директивами INI. Например, если директива post_max_size (которая ограничивает максимальный размер запросов) установлена на 2000 байт, попытка вызвать функцию request_parse_body с запросом, превышающим этот размер, будет приводить к ошибке.
Параметры разбора запроса могут быть переопределены с меньшими или большими значениями путём передачи параметра $options.
Если запрос, который пытаются разобрать, нарушает ограничения, установленные в директивах INI или пользовательских опциях, функция request_parse_body выбрасывает новое исключение RequestParseBodyException.
Переопределение параметров разбора запроса
Параметр $options может быть использован для передачи массива значений INI, связанных с разбором запроса. Эти значения необязательно должны быть меньше, чем глобальная конфигурация. Это даёт возможность выборочно обрабатывать меньшие или большие лимиты, чем заданные в INI-файлах.
Например, чтобы разобрать запрос с большим или меньшим ограничением INI-директивы post_max_size, вызовите функцию request_parse_body с желаемым новым значением:
request_parse_body(['post_max_size' => 1024]);Массив $options принимает только следующие переопределения:
| INI/ $optionключ | Описание | 
|---|---|
| post_max_size | Максимальный размер | 
| max_input_vars | Сколько входных | 
| max_multipart_body_parts | Сколько многокомпонентных | 
| max_file_uploads | Максимальное количество | 
| upload_max_filesize | Максимально допустимый | 
Значения этих ключей должны быть целым числом или строкой количества (например, значения, разрешённые функцией ini_parse_quantity).
Передача директив INI, отличных от приведённого выше списка, приводит к исключению ValueError:
request_parse_body(['arbitrary_value' => 42]);ValueError: Invalid key 'arbitrary_value' in $options argumentПередача не целочисленных и не количественных строковых значений приводит к предупреждению PHP:
request_parse_body(['post_max_size' => 'arbitrary_value']);Warning: Invalid quantity "arbitrary_value": no valid leading digits, interpreting as "0" for backwards compatibilityПередача не строковых и не целочисленных значений в качестве значений ключей $options приводит к исключению ValueError:
request_parse_body(['post_max_size' => []]);ValueError: Invalid array value in $options argumentКласс исключений RequestParseBodyException
RequestParseBodyException — новый класс Exception, объявленный в глобальном пространстве имён и расширяющий класс Exception.
class RequestParseBodyException extends Exception {}Исключения RequestParseBodyException выбрасываются, если функция request_parse_body не может разобрать данные запроса. Это может произойти, если предоставленные данные запроса некорректны, не отправлен заголовок Content-Type, или если данные запроса выходят за рамки ограничений, установленных директивами INI и дополнительным параметром $options.
Ниже приведён список исключений RequestParseBodyException и их причины:
- RequestParseBodyException: Request does not provide a content type. Запрос не содержит заголовка- Content-Type.
- RequestParseBodyException: Content-Type ARBITRARY_TYPE is not supported. Заголовок Content-Type содержит значение, отличное от- multipart/form-dataили- application/x-www-form-urlencoded.
- RequestParseBodyException: Missing boundary in multipart/form-data POST data. Запрос не содержит границы. Убедитесь, что запрос правильно оформлен как- multipart/form-dataили- application/x-www-form-urlencoded.
- RequestParseBodyException: POST Content-Length of ... bytes exceeds the limit of ... bytes. Длина содержимого превысила значение- post_max_size, установленное в параметре- $optionsили директиве INI.
- RequestParseBodyException: Multipart body parts limit exceeded ... To increase the limit change max_multipart_body_parts in php.ini. Части данных запроса превысили значение- max_multipart_body_parts, установленное в параметре- $optionsили в директиве INI.
- RequestParseBodyException: Input variables exceeded ... To increase the limit change max_input_vars in php.ini. Части данных запроса превысили значение- max_input_vars, установленное в параметре- $optionsили в директиве INI.
- RequestParseBodyException: Maximum number of allowable file uploads has been exceeded. Количество загружаемых файлов превышает значение- max_file_uploads, установленное в параметре- $optionsили в директиве INI.
Предостережения по request_parse_body
Функция request_parse_body предназначена для того, чтобы вызываться только один раз за запрос. Она не предоставляет возможности указать строку для разбора и деструктуризации php://input. При последующих вызовах функция будет возвращать массив с пустыми данными, а поток php://input будет пуст после первого вызова request_parse_body().
Не идемпотентное поведение request_parse_body. Обратите внимание, что вызов функции request_parse_body имеет потенциально деструктивное поведение, в том числе он поглощает поток php://input и очищает его содержимое.
- Вызов функции request_parse_bodyпоглощает потокphp://input. Потокphp://inputбудет пуст
- Если поток php://inputбыл прочитан ранее (например,file_get_contents('php://input')), функцияrequest_parse_bodyвернёт пустой результат (т.е.[0 => [], 1 => []]).
- Функция request_parse_bodyне изменяет глобальные переменные$_POSTи$_FILESнапрямую; PHP-приложение само должно перезаписать эти переменные.
- Только первый вызов функции request_parse_bodyвозвращает разобранные данные. Последующие вызовы возвращают пустой результат (т. е.[0 => [], 1 => []]).
- Даже если функция выбрасывает исключение, php://inputвсё равно поглощается и очищается, а последующие вызовыrequest_parse_bodyвозвращают пустой результат.
Влияние на обратную совместимость
Эта функция не может быть полифункциональной, поскольку для получения исходных данных ей необходимо использовать вызовы базового Server API (SAPI).
Это изменение не должно вызвать проблем с обратной совместимостью, если только PHP-приложение не объявит свою собственную функцию request_parse_body.
- PHP RFC: RFC1867 for non-POST HTTP verbs
- [RFC][Under discussion] RFC1867 for non-POST HTTP verbs
- Реализация