Skip to content

Commit

Permalink
Do not imply having parsed the request body (#27)
Browse files Browse the repository at this point in the history
* Do not imply having parsed the request body

Strictly follow the PSR-7 guidance where $_POST is used for a request’s
parsed body only for very specific requests. All other requests should
result in a null value and leave other parsing up to application logic.

* Address assumptions made about MIME types

* Add tests for filling parsedBody

* Fix StyleCI response
  • Loading branch information
Zegnat committed Jun 6, 2020
1 parent aab2962 commit 4db66c8
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 4 deletions.
21 changes: 19 additions & 2 deletions src/ServerRequestCreator.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,30 @@ public function fromGlobals(): ServerRequestInterface

$headers = \function_exists('getallheaders') ? getallheaders() : static::getHeadersFromServer($_SERVER);

return $this->fromArrays($server, $headers, $_COOKIE, $_GET, $_POST, $_FILES, \fopen('php://input', 'r') ?: null);
$post = null;
if ('POST' === $this->getMethodFromEnv($server)) {
foreach ($headers as $headerName => $headerValue) {
if ('content-type' !== \strtolower($headerName)) {
continue;
}
if (\in_array(
\strtolower(\trim(\explode(';', $headerValue, 2)[0])),
['application/x-www-form-urlencoded', 'multipart/form-data']
)) {
$post = $_POST;

break;
}
}
}

return $this->fromArrays($server, $headers, $_COOKIE, $_GET, $post, $_FILES, \fopen('php://input', 'r') ?: null);
}

/**
* {@inheritdoc}
*/
public function fromArrays(array $server, array $headers = [], array $cookie = [], array $get = [], array $post = [], array $files = [], $body = null): ServerRequestInterface
public function fromArrays(array $server, array $headers = [], array $cookie = [], array $get = [], array $post = null, array $files = [], $body = null): ServerRequestInterface
{
$method = $this->getMethodFromEnv($server);
$uri = $this->getUriFromEnvWithHTTP($server);
Expand Down
4 changes: 2 additions & 2 deletions src/ServerRequestCreatorInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public function fromGlobals(): ServerRequestInterface;
* @param array $headers typically the output of getallheaders() or similar structure
* @param array $cookie typically $_COOKIE or similar structure
* @param array $get typically $_GET or similar structure
* @param array $post typically $_POST or similar structure
* @param array|null $post typically $_POST or similar structure, represents parsed request body
* @param array $files typically $_FILES or similar structure
* @param StreamInterface|resource|string|null $body Typically stdIn
*
Expand All @@ -42,7 +42,7 @@ public function fromArrays(
array $headers = [],
array $cookie = [],
array $get = [],
array $post = [],
array $post = null,
array $files = [],
$body = null
): ServerRequestInterface;
Expand Down
53 changes: 53 additions & 0 deletions tests/ServerRequestCreatorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -594,4 +594,57 @@ public function testFailingStreamFromFile()
);
$this->assertEquals($expected, $created);
}

public function testNoParsedBodyWithoutPOSTMethod()
{
$_POST = ['a' => 'b', 'c' => 'd'];
$instance = $this->creator->fromGlobals();
$this->assertNull($instance->getParsedBody());
}

public function testNoParsedBodyWithPOSTMethodWithoutContentType()
{
$_SERVER['REQUEST_METHOD'] = 'POST';
$_POST = ['a' => 'b', 'c' => 'd'];
$instance = $this->creator->fromGlobals();
$this->assertNull($instance->getParsedBody());
}

/**
* @dataProvider dataContentTypesThatTriggerParsedBody
*/
public function testParsedBodyWithPOSTMethodDifferentContentType($parsedBody, $contentType)
{
$_SERVER['REQUEST_METHOD'] = 'POST';
$_SERVER['HTTP_CONTENT_TYPE'] = $contentType;
$_POST = ['a' => 'b', 'c' => 'd'];
$instance = $this->creator->fromGlobals();
$this->assertSame($parsedBody ? $_POST : null, $instance->getParsedBody());
}

public function dataContentTypesThatTriggerParsedBody()
{
return [
// Acceptable values
'Standard HTML Form' => [
true, 'application/x-www-form-urlencoded',
],
'HTML Form with MIME body' => [
true, 'multipart/form-data',
],
'Standard HTML Form, mixed case MIME' => [
true, 'appLication/x-WWW-form-URLEncoded',
],
'Standard HTML Form, surrounding whitespace' => [
true, ' application/x-www-form-urlencoded ',
],
'Standard HTML Form, with flags' => [
true, 'application/x-www-form-urlencoded;charset=utf-8',
],
// Nonacceptable values
'JSON is not parsed by PHP' => [
false, 'application/json',
],
];
}
}

0 comments on commit 4db66c8

Please sign in to comment.