PHP 8.4: New request_parse_body
function
PHP automatically parses HTTP POST requests to populate the $_POST
and $_FILES
super global variables. However, other HTTP requests with methods such as PUT
and PATCH
do not get parsed automatically, and it is up to the PHP application to parse the request data.
With the popularity of the REST APIs that increasingly utilize HTTP methods such as PUT
, DELETE
, and PATCH
, parsing HTTP request data consistently is important. However, starting to automatically parse HTTP request data for non-POST requests can be a breaking change for existing PHP applications.
PHP provides a stream wrapper at php://input
, that contains the request data. For POST
requests with enctype="multipart/form-data"
, this stream wrapper remains empty because it's automatically parsed and consumed to populate $_POST
and $_FILES
variables. The automatic $_POST
/$_FILES
processing can be controlled with the enable_post_data_reading=Off
INI setting.
When the enable_post_data_reading
INI value is set to Off
, or when the HTTP request method is a value apart from POST
, the php://input
stream wrapper can be read in user-land PHP code to parse the HTTP request data.
curl --request PUT \
--location 'https://example.com/post.php' \
--form 'test="123"'
From a PHP application, the form data of the Curl call above can be read from the php://input
stream.
echo file_get_contents('php://input');
----------------------------690112416382325217174003
Content-Disposition: form-data; name="test"
123
----------------------------690112416382325217174003--
New request_parse_body
function
PHP 8.4 adds a new function named request_parse_body
that exposes the PHP's built-in request parsing functionality for other HTTP request methods.
/**
* 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 {}
When called, the request_parse_body
function reads the entire contents that are otherwise available on the php://input
stream and creates the values that can be used in $_POST
and $_FILES
variables.
The return value will be an array containing two keys, 0
and 1
, containing the parsed values that can be used as $_POST
(array key index 0
) and $_FILES
(index 1
). Both array keys will be present at all times — even if there are no request data, and/or files.
It is possible to populate the $_POST
and $_FILES
values directly from the return values:
[$_POST, $_FILES] = request_parse_body();
Note that the request parsing continues to be bound to the limitations set by the INI directives. For example, if the post_max_size
directive (which limits the max size of the requests) is set to 2000
bytes, attempting to call the request_parse_body
function with a request larger than that continues to result in an error.
The request parsing options can be overridden with smaller or larger values by passing the $options
parameter.
If the request being attempted to parse violates the limits set at the INI directives or the custom options, the request_parse_body
function throws a new exception named RequestParseBodyException
.
Overriding request parsing options
The $options
parameter can be used to pass an array of INI values related to request parsing. These values do not need to be smaller than the global configuration. This gives the advantage to selectively process smaller or larger limits than the limits set at INI files.
For example, to parse a request with a higher or smaller limit to the post_max_size
INI directive, call the request_parse_body
function with the desired new value:
request_parse_body(['post_max_size' => 1024]);
The $options
array only accepts the following overrides:
INI/$option key |
Description |
---|---|
post_max_size |
Maximum size of POST data that PHP will accept. Its value may be 0 to disable the limit. |
max_input_vars |
How many GET/POST/COOKIE input variables may be accepted |
max_multipart_body_parts |
How many multipart body parts (combined input variable and file uploads) may be accepted. |
max_file_uploads |
Maximum number of files that can be uploaded via a single request |
upload_max_filesize |
Maximum allowed size for uploaded files. |
The values for these keys must be an integer or a quantity string (such as the values allowed by the ini_parse_quantity
function.
Passing INI directives apart from the list above lists in a ValueError
exception:
request_parse_body(['arbitrary_value' => 42]);
ValueError: Invalid key 'arbitrary_value' in $options argument
Passing non-integer and non-quantity string values results in a PHP warning:
request_parse_body(['post_max_size' => 'arbitrary_value']);
Warning: Invalid quantity "arbitrary_value": no valid leading digits, interpreting as "0" for backwards compatibility
Passing non-string and non-integer values as values to the $options
keys results in a ValueError
exception:
request_parse_body(['post_max_size' => []]);
ValueError: Invalid array value in $options argument
RequestParseBodyException
Exception Class
RequestParseBodyException
is a new Exception class declared in the global namespace, extending the Exception
class.
class RequestParseBodyException extends Exception {}
RequestParseBodyException
exceptions are thrown if the request_parse_body
function can not parse the request data. This can occur if the provided request data are invalid, does not send a Content-Type
header, or if the request data falls outside the constraint set by INI directives and the optional $options
parameter.
The following is a list of RequestParseBodyException
exception cases and their cause:
-
RequestParseBodyException: Request does not provide a content type
The request does not contain aContent-Type
header. -
RequestParseBodyException: Content-Type ARBITRARY_TYPE is not supported
TheContent-Type
header contains a value other thanmultipart/form-data
orapplication/x-www-form-urlencoded
. -
RequestParseBodyException: Missing boundary in multipart/form-data POST data
The request does not contain a boundary. Make sure the request is correctly formatted asmultipart/form-data
orapplication/x-www-form-urlencoded
. -
RequestParseBodyException: POST Content-Length of ... bytes exceeds the limit of ... bytes
The content length has exceeded thepost_max_size
value set at the ($options parameter
](#options) or INI directive. -
RequestParseBodyException: Multipart body parts limit exceeded ... To increase the limit change max_multipart_body_parts in php.ini
The request data parts have exceeded themax_multipart_body_parts
value set at the ($options parameter
](#options) or the INI directive. -
RequestParseBodyException: Input variables exceeded ... To increase the limit change max_input_vars in php.ini.
The request data parts have exceeded themax_input_vars
value set at the ($options parameter
](#options) or the INI directive. -
RequestParseBodyException: Maximum number of allowable file uploads has been exceeded
The number of files being uploaded exceeds themax_file_uploads
value set at the ($options parameter
](#options) or the INI directive.
request_parse_body
caveats
request_parse_body
function is designed to be called only one time per request. It provides no way to specify the string to be parsed, and it destructively consumes the php://input
. On subsequent calls, the function will return an array with empty data, and the php://input
stream will be empty after the first request_parse_body()
call.
Non-idempotent behavior of
request_parse_body
Note that calling therequest_parse_body
function has potential destructive behavior, including it consuming thephp://input
stream and emptying its content.
- Calling
request_parse_body
function consumes thephp://input
stream. Thephp://input
stream will be empty - If the
php://input
stream was read previously (e.g.file_get_contents('php://input')
,request_parse_body
function returns an empty result (i.e.[0 => [], 1 => []]
). request_parse_body
function does not directly modify$_POST
and$_FILES
global variables; it is up to the PHP application to overwrite those variables if desired.- Only the first call of
request_parse_body
function returns the parsed data. Subsequent calls return an empty result (i.e.[0 => [], 1 => []]
). - Even if the function throws an exception, the
php://input
is still consumed and cleared, and subsequentrequest_parse_body
calls return an empty result.
Backward Compatibility Impact
This function cannot be polyfilled because it needs to utilize calls to the underlying Server API (SAPI) to obtain the raw data.
This change should not cause any backward compatibility issues unless a PHP application declares its own request_parse_body
function.