In PHP 8.4 and later, what does request_parse_body() do, and why do PUT and PATCH form submissions need it?
answer
- $_POST and $_FILES: POST only
- added in PHP 8.4
- returns a [$post, $files] pair
- options override post_max_size and friends
- the body can be consumed only once
basics
~20 sPHP fills $_POST and $_FILES only for POST requests. request_parse_body(), added in PHP 8.4, runs the same form and multipart parser on demand — for PUT, PATCH or DELETE — and returns a [$post, $files] pair.
solid answer
~40 sPHP parses `application/x-www-form-urlencoded` and `multipart/form-data` bodies into `$_POST` and `$_FILES` automatically, but only when the method is `POST`. A REST-style `PUT /rooms/12` or `PATCH` carrying a multipart form got nothing, and before PHP 8.4 you had to parse the multipart body from `php://input` yourself. `request_parse_body(?array $options = null): array`, added in 8.4, reads the body and parses it according to its `Content-Type`, returning an array whose index `0` is the `$_POST` equivalent and index `1` the `$_FILES` equivalent. The `$options` array overrides the ini limits for this parse: `max_file_uploads`, `max_input_vars`, `max_multipart_body_parts`, `post_max_size` and `upload_max_filesize`. A malformed body throws `RequestParseBodyException`, and an invalid option a `ValueError`. The body can be consumed only once: the function does not buffer it into `php://input`, and returns empty data if the body was already read.
code
php · 18 lines<?php
declare(strict_types=1);
// PATCH /rooms/12 Content-Type: multipart/form-data; boundary=...
if (($_SERVER['REQUEST_METHOD'] ?? '') === 'PATCH') {
try {
[$fields, $files] = request_parse_body([
'upload_max_filesize' => '8M',
'max_file_uploads' => 4,
]);
} catch (RequestParseBodyException $e) {
http_response_code(400);
exit('Malformed form body');
}
$title = $fields['title'] ?? null; // same shape as $_POST
$photos = $files['photos'] ?? null; // same shape as $_FILES
}go deeper
Recall that $_POST and $_FILES are filled only for POST, and that PHP 8.4 added request_parse_body() for other methods.
Explain the returned [$post, $files] pair, the option keys that override ini limits, the exceptions it throws, and why the body can be read only once.
Use request_parse_body() for multipart PUT and PATCH endpoints with per-endpoint limits, and make sure nothing reads php://input before it.
Decide whether APIs accept multipart for non-POST methods at all, and how body-reading responsibilities are split between middleware and handlers.
## The gap it fills PHP's automatic body parsing has always been tied to the `POST` method. Before a script starts, PHP parses the body into `$_POST` (and uploaded files into `$_FILES`) only when: - `enable_post_data_reading` is On; - the method is `POST`; - the `Content-Type` is `application/x-www-form-urlencoded` or `multipart/form-data`. REST-style APIs and HTML-over-the-wire front ends often send forms with `PUT`, `PATCH` or `DELETE`. For those, `$_POST` and `$_FILES` stay empty. Before PHP 8.4 the only options were to parse the raw multipart body from `php://input` in userland — slow, memory-hungry and easy to get wrong — or to pull in a library that did so. ## What request_parse_body() does `request_parse_body(?array $options = null): array`, added in **PHP 8.4**, runs PHP's own body parser on demand: 1. It reads the request body. 2. It parses it according to the request's `Content-Type`, supporting the same two types as `$_POST`: form-urlencoded and multipart. 3. It returns a two-element array: index `0` holds what `$_POST` would contain, index `1` what `$_FILES` would contain. Destructuring makes the call read naturally: `[$fields, $files] = request_parse_body();`. Uploaded files returned this way have the same shape as `$_FILES` entries. ## Options and limits The optional `$options` array overrides, for this call only, the php.ini directives that limit body parsing: | Option key | Limits | |---|---| | `post_max_size` | The total body size | | `upload_max_filesize` | Each uploaded file | | `max_file_uploads` | The number of files | | `max_input_vars` | The number of form fields | | `max_multipart_body_parts` | The number of multipart parts | This lets one endpoint accept larger uploads without raising the limits for the whole application. ## Errors - A body that is invalid for its `Content-Type` throws **`RequestParseBodyException`**, a subclass of `Exception`. - An unknown option key, or an invalid value for a known key, throws **`ValueError`**. Both are exceptions, so an endpoint can map a malformed body to a 400 response instead of failing silently. ## The body is consumed once The request body is a stream that PHP reads from the client once: - `request_parse_body()` consumes the body **without** buffering it into `php://input`, so reading `php://input` afterwards yields nothing useful; - if the body was already read — for example through `php://input` — `request_parse_body()` returns empty data. Choose one way to read each request's body. A framework that reads `php://input` early for logging will break a later `request_parse_body()` call. ## Using it for POST too With `enable_post_data_reading` set to Off, PHP does not parse even `POST` bodies. The body then remains available, and `request_parse_body()` can parse it when — and only if — the endpoint actually needs the fields. That suits proxies and endpoints that stream large bodies elsewhere. ## Before PHP 8.4 Applications that needed multipart `PUT` or `PATCH` support before 8.4 had three options, all worse: - read `php://input` and parse the multipart boundaries in PHP code, which is slow and duplicates what the engine already does in C; - have clients send `POST` with a method-override field such as `_method=PATCH`, a convention many frameworks still support; - accept only JSON for those methods and keep file uploads on `POST` endpoints. ## Interplay with frameworks Frameworks that build their own request objects may already read the body during bootstrap. Because the body can be consumed only once, calling `request_parse_body()` inside a controller after the framework has read `php://input` returns empty data. Use it in plain PHP entry points, or in the one place where your framework creates the request object, not in individual handlers. ## Version notes - Available from **PHP 8.4**; code that must also run on 8.3 or earlier needs a fallback parser. - It does not decode JSON. A JSON body is still read with `file_get_contents('php://input')` and `json_decode()`.
- What does request_parse_body() return if middleware already read php://input?Empty data. The request body is consumed once, and `request_parse_body()` has nothing left to parse when something else read the body first. The reverse also holds: after `request_parse_body()`, `php://input` does not hold the body, because the function does not buffer it there.
- Does request_parse_body() decode a JSON body?No. It supports the same two content types as automatic POST parsing, `application/x-www-form-urlencoded` and `multipart/form-data`. JSON bodies are still read with `file_get_contents('php://input')` and decoded with `json_decode()`.
saying these in an interview costs you the question
- PHP 8 fills $_POST for PUT requests with form bodies
- request_parse_body() decodes JSON bodies too
- You can call request_parse_body() after reading php://input
- request_parse_body() returns an object with post and files properties
- A malformed body makes it return false