Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PSR-7: getParsedBody() vs getBody()

Scenario 1 sending x-www-form-urlencoded data

POST /path HTTP/1.1
Content-Type: application/x-www-form-urlencoded

foo=bar

Running print_r($request->getParsedBody()); returns fine:

Array
(
    [foo] => bar
)

Running print_r($request->getBody()->getContents()); returns a string foo=bar


Scenario 2 sending application/json data

POST /path HTTP/1.1
Content-Type: application/json

{
    "foo": "bar"
}

Running print_r($request->getParsedBody()); returns an empty array. Array ( )

But, running print_r($request->getBody()->getContents()); returns fine:

{"foo":"bar"}


Is this expected behavior?

Meaning, if we're sending x-www-form-urlencoded data, we should use getParsedBody().

While getBody()->getContents() should be used if we're sending application/json?


Additional info:

Request object is created using:

$request = \Laminas\Diactoros\ServerRequestFactory::fromGlobals(
        $_SERVER, $_GET, $_POST, $_COOKIE, $_FILES
);
like image 491
IMB Avatar asked Dec 31 '22 07:12

IMB


1 Answers

Message body:

In a PSR-7 library, the message body is abstracted by the StreamInterface. Any implementation of this interface MUST wrap a PHP stream and, of course, should provide the proper functionality to perform the specific read/write/seek operations on it. PHP provides a list of I/O streams, from which php://input is suitable for the task in question.

php://input is a read-only stream that allows you to read raw data from the request body. php://input is not available with enctype="multipart/form-data".

In this context, when a request to the server is performed, the request body data (regardless of its data type) is automatically written to the php://input stream, in raw format (string). The information can later be read from it by calling StreamInterface::getContents, StreamInterface::__toString, or StreamInterface::read (which would probably make use of stream_get_contents(), or similar, in their implementation).

Note: The method StreamInterface::__toString is automatically called when the object representing the message body, e.g. the instance of the class implementing StreamInterface is cast to a string. For example, like this - see Type Casting in PHP:

$messageBodyObject = $request->getBody(); // implements StreamInterface
$contentOfMessageBody = (string) $messageBodyObject; // cast to string => StreamInterface::__toString is called

echo $contentOfMessageBody;

Parsed body:

In regard of the PSR-7, the parsed body is a "characteristic" of the applications where PHP is "used as a server-side application to fulfill HTTP requests" (in comparison with the applications where PHP is used as "an HTTP client") - see Summary of the PSR-7 Meta Document. So, the parsed body is a component of the ServerRequestInterface only.

The parsed body (read the comments of ServerRequestInterface::getParsedBody and ServerRequestInterface::withParsedBody) is thought as a representation in a "parsed" form (array, or object) of the raw data (a string) saved in the php://input stream as the result of performing a request. For example, the $_POST variable, which is an array, holds the parsed body of a POST request, under the conditions presented bellow.

Relevant use-cases:

If a POST request is performed and the header Content-Type is application/x-www-form-urlencoded (for example when submitting a normal HTML form), the content of the request body is automatically saved into both the php://input stream (serialized) and the $_POST variable (array). So, in the PSR-7 context, calling both StreamInterface::getContents (or StreamInterface::__toString, or StreamInterface::read) and ServerRequestInterface::getParsedBody will return "valid" values.

If a POST request is performed and the header Content-Type is multipart/form-data (for example when performing a file(s) upload), the content of the request body is NOT saved into the php://input stream at all, but only into the $_POST variable (array). So, in the PSR-7 context, only calling ServerRequestInterface::getParsedBody will return a "valid" value.

If a POST request is performed and the header Content-Type has other value than the two presented above (for example application/json, or text/plain; charset=utf-8), the content of the request body is saved only into the php://input stream. So, in the PSR-7 context, only calling StreamInterface::getContents (or StreamInterface::__toString, or StreamInterface::read) will return a "valid" value.

Resources:

  • PSR-7: HTTP message interfaces
  • PSR-7 Meta Document
  • How to get body of a POST in php?
  • PHP "php://input" vs $_POST
  • php://input - what does it do in fopen()? (the comment to the answer)
like image 75
dakis Avatar answered Jan 08 '23 00:01

dakis