Request getSize() returns null for php://input stream
odan opened this issue · comments
Environment:
PHP Version: 8.2
Package Versions:
- Nyholm/psr7-server: 1.1.0
- Nyholm/psr7: 1.8.1
I have observed an issue with the getSize() method implementation when using the php://input
stream.
https://github.com/Nyholm/psr7-server/blob/1.1.0/src/ServerRequestCreator.php#L71
Here are the steps to reproduce the problem:
use Nyholm\Psr7\Factory\Psr17Factory;
use Nyholm\Psr7\ServerRequest;
use Nyholm\Psr7Server\ServerRequestCreator;
// Create a Nyholm PSR-7 request
$factory = new Psr17Factory();
$serverRequestCreator = new ServerRequestCreator($factory, $factory, $factory, $factory);
$request = $serverRequestCreator->fromGlobals();
// Attempt to get the size of the request body
$requestSize = $request->getBody()->getSize(); // NULL
Expected result:
When I send a JSON (application/json) request via POST, the $requestSize
is expected to contain the size of the request body.
The only exception is when POST'ing a multipart/form-data
. This is a typical behavior in PHP, because
PHP already reads the input
stream into the $_POST
variable. In this case, the body size would always be 0 or null.
Actual Behavior:
The getSize() method returns null
, indicating that it cannot determine the size of the php://input
stream.
The reason is that php://input
resource / stream does not provide support for fstat
. See here:
Workaround
// This works
$contents = (string)$request->getBody();
$realSize = strlen($contents);
I have also tested this behavior with other PSR-7 implementations, such as slim/psr7
, and encountered the same issue.
This behavior impacts any application that relies on the accurate measurement of request body size, and it appears to be a general issue with PSR-7 implementations when using the php://input
stream.
I'm unsure if this is an intentional behavior or a bug, so I'm reporting it here for further investigation.
Update. A possible fix would be to read the php://input
and copy it into a php://memory
resource.
This is much more memory efficient than passing a string.
Before:
return $this->fromArrays($server, $headers, $_COOKIE, $_GET, $post, $_FILES, \fopen('php://input', 'r') ?: null);
After:
$body = \fopen('php://memory', 'r+') ?: null;
stream_copy_to_stream(\fopen('php://input', 'r'), $body);
return $this->fromArrays($server, $headers, $_COOKIE, $_GET, $post, $_FILES, $body);
https://github.com/Nyholm/psr7-server/blob/1.1.0/src/ServerRequestCreator.php#L71