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 argumentRequestParseBodyException 异常类
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 supportedContent-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 函数,否则此更改不应导致任何向后兼容性问题。