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
- Реализация