PHP 8.4: 新增 request_parse_body 函数
PHP自动解析 HTTP POST 请求,以填充 $_POST
和 $_FILES
超级全局变量。然而,使用 PUT
和 PATCH
等方法的其他 HTTP 请求不会自动解析,而是由 PHP 应用来解析请求数据。
随着越来越多地使用 HTTP 方法(如 PUT
、DELETE
和 PATCH
)的 REST API 的流行,一致地解析 HTTP 请求数据非常重要。然而,对于现有的 PHP 程序来说,开始自动解析非 POST 请求的 HTTP 请求数据可能是一个破坏性的变化。
PHP 在 php://input
中提供了一个流式包装器,其包含了请求数据。对于使用的 enctype="multipart/form-data"
的 POST
请求,流式包装器保留为空,因为它自动解析并消费以填充 $_POST
和 $_FILES
变量。自动 $_POST
/$_FILES
处理可以使用 enable_post_data_reading=Off
INI 设置进行控制。
当 enable_post_data_reading
INI 值设置为 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
的新函数,它为其他 HTTP 请求方法暴露了 PHP 内置的请求解析功能。
/**
* 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 文件中设置的限制更小或更大的限制。
例如,要解析对 post_max_size
INI 指令具有更高或更小限制的请求,请使用所需的新值调用 request_parse_body
函数:
request_parse_body(['post_max_size' => 1024]);
$options
数组只接受以下重写:
INI/$option 键 | 描述 |
---|---|
post_max_size | PHP 接受的最大 POST 数据大小。其值可为 0 以禁用限制。 |
max_input_vars | 可以接受多少个 GET/POST/COOKIE 输入变量 |
max_multipart_body_parts | 可以接受多少个 multipart body parts (包括输入变量和上传文件s) 。 |
max_file_uploads | 单个请求可以上传的最大文件数量 |
upload_max_filesize | 允许的上传文件的最大大小限制 |
这些键的值必须是整数或数量字符串(例如 ini_parse_quantity
函数允许的值)。
在 ValueError
异常中,在上面列出的列表之外传递INI 指令:
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
类。
class RequestParseBodyException extends Exception {}
如果 request_parse_body
函数不能解析请求数据,将会抛出 RequestParseBodyException
异常。如果提供的请求数据无效,没有发送 Content-Type
header,或请求数据超出 INI 指令及 $options
参数的约束设置时,也会抛出此异常。
以下是 RequestParseBodyException
异常的列表和它们的起因:
RequestParseBodyException: Request does not provide a content type
请求不包含 Content-Type
header。
RequestParseBodyException: Content-Type ARBITRARY_TYPE is not supported
Content-Type
header 包含的值不是 multipart/form-data
或 application/x-www-form-urlencoded
。
RequestParseBodyException: Missing boundary in multipart/form-data POST data
请求不包含边界(boundary)。确保请求的正确格式化为 multipart/form-data
或者 application/x-www-form-urlencoded
。
RequestParseBodyException: POST Content-Length of ... bytes exceeds the limit of ... bytes
内容长度超过 ($options parameter)
或者 INI 指令中设置的 post_max_size
值。
RequestParseBodyException: Multipart body parts limit exceeded ... To increase the limit change max_multipart_body_parts in php.ini
请求数据部分超过 ($options parameter)
或者 INI 指令中设置的 max_multipart_body_parts
值。
RequestParseBodyException: Input variables exceeded ... To increase the limit change max_input_vars in php.ini.
请求数据部分超过 ($options parameter)
或者 INI 指令中设置的 max_input_vars
值。
RequestParseBodyException: Maximum number of allowable file uploads has been exceeded
上传的文件数量超过 ($options parameter)
或者 INI 指令中设置的 max_file_uploads
值
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
流(e.g.file_get_contents('php://input')
,request_parse_body
函数将返回空结果(i.e.[0 => [], 1 => []]
). request_parse_body
函数不直接修改$_POST
和$_FILES
全局变量;如果有需要由 PHP 应用来重写这些变量。g- 只有第一次调用
request_parse_body
函数时返回解析数据。随后的调用将返回空结果 (i.e.[0 => [], 1 => []]
). - 即使该函数抛出异常,仍然会消费
php://input
并清空,随后的request_parse_body
调用将返回空结果。
向后兼容性影响
此函数不能进行 polyfill,因为它需要利用对基础服务器 API(SAPI)的调用来获取原始数据。
除非 PHP 应用程序声明了自己的 request_parse_body
函数,否则此更改不应导致任何向后兼容性问题。