Refactor ServerRequestMarshaller and ServerRequest

This commit is contained in:
PJ Dietz 2020-08-16 08:52:33 -04:00
parent 20012dc671
commit 9243dd7663
3 changed files with 76 additions and 43 deletions

View File

@ -4,7 +4,9 @@ namespace WellRESTed\Message;
use InvalidArgumentException; use InvalidArgumentException;
use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\StreamInterface;
use Psr\Http\Message\UploadedFileInterface; use Psr\Http\Message\UploadedFileInterface;
use Psr\Http\Message\UriInterface;
/** /**
* Representation of an incoming, server-side HTTP request. * Representation of an incoming, server-side HTTP request.
@ -51,9 +53,31 @@ class ServerRequest extends Request implements ServerRequestInterface
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
public function __construct(array $serverParams = []) /**
{ * Create a new ServerRequest.
parent::__construct(); *
* $headers is an optional associative array with header field names as
* string keys and values as either string or string[].
*
* If no StreamInterface is provided for $body, the instance will create
* a NullStream instance for the message body.
*
* @param string $method
* @param string|UriInterface $uri
* @param array $headers Associative array with header field names as
* keys and values as string|string[]
* @param StreamInterface|null $body A stream representation of the message
* entity body
* @param array $serverParams An array of Server API (SAPI) parameters
*/
public function __construct(
string $method = 'GET',
$uri = '',
array $headers = [],
?StreamInterface $body = null,
array $serverParams = []
){
parent::__construct($method, $uri, $headers, $body);
$this->serverParams = $serverParams; $this->serverParams = $serverParams;
$this->cookieParams = []; $this->cookieParams = [];
$this->queryParams = []; $this->queryParams = [];

View File

@ -8,38 +8,28 @@ use Psr\Http\Message\UriInterface;
class ServerRequestMarshaller class ServerRequestMarshaller
{ {
public function getServerRequest( /**
?array $serverParams = null, * Read the request as sent from the client and construct a ServerRequest
?array $cookieParams = null, * representation.
?array $queryParams = null, *
?array $postParams = null, * @return ServerRequestInterface
?array $fileParams = null, * @internal
string $inputStream = 'php://input' */
): ServerRequestInterface { public function getServerRequest(): ServerRequestInterface
$serverParams = $serverParams ?? $_SERVER; {
$cookieParams = $cookieParams ?? $_COOKIE; $method = self::parseMethod($_SERVER);
$queryParams = $queryParams ?? self::parseQuery($serverParams); $uri = self::readUri($_SERVER);
$postParams = $postParams ?? $_POST; $headers = self::parseHeaders($_SERVER);
$fileParams = $fileParams ?? $_FILES; $body = self::readBody();
$request = new ServerRequest($serverParams); $request = (new ServerRequest($method, $uri, $headers, $body, $_SERVER))
->withProtocolVersion(self::parseProtocolVersion($_SERVER))
$request = $request ->withUploadedFiles(self::readUploadedFiles($_FILES))
->withProtocolVersion(self::parseProtocolVersion($serverParams)) ->withCookieParams($_COOKIE)
->withMethod(self::parseMethod($serverParams)) ->withQueryParams(self::parseQuery($_SERVER));
->withBody(self::readBody($inputStream))
->withUri(self::readUri($serverParams))
->withUploadedFiles(self::readUploadedFiles($fileParams))
->withCookieParams($cookieParams)
->withQueryParams($queryParams);
$headers = self::parseHeaders($serverParams);
foreach ($headers as $name => $value) {
$request = $request->withAddedHeader($name, $value);
}
if (self::isForm($request)) { if (self::isForm($request)) {
$request = $request->withParsedBody($postParams); $request = $request->withParsedBody($_POST);
} }
return $request; return $request;
@ -95,9 +85,9 @@ class ServerRequestMarshaller
return $serverParams['REQUEST_METHOD'] ?? 'GET'; return $serverParams['REQUEST_METHOD'] ?? 'GET';
} }
private static function readBody(string $inputStream): StreamInterface private static function readBody(): StreamInterface
{ {
$input = fopen($inputStream, 'rb'); $input = fopen('php://input', 'rb');
$temp = fopen('php://temp', 'wb+'); $temp = fopen('php://temp', 'wb+');
stream_copy_to_stream($input, $temp); stream_copy_to_stream($input, $temp);
rewind($temp); rewind($temp);

View File

@ -28,6 +28,8 @@ class ServerRequestMarshallerTest extends TestCase
'hamster' => 'Dusty' 'hamster' => 'Dusty'
]; ];
FopenHelper::$inputTempFile = null;
$this->marshaller = new ServerRequestMarshaller(); $this->marshaller = new ServerRequestMarshaller();
} }
@ -103,15 +105,9 @@ class ServerRequestMarshallerTest extends TestCase
$tempFilePath = tempnam(sys_get_temp_dir(), 'test'); $tempFilePath = tempnam(sys_get_temp_dir(), 'test');
$content = 'Body content'; $content = 'Body content';
file_put_contents($tempFilePath, $content); file_put_contents($tempFilePath, $content);
FopenHelper::$inputTempFile = $tempFilePath;
$request = $this->marshaller->getServerRequest( $request = $this->marshaller->getServerRequest();
null,
null,
null,
null,
null,
$tempFilePath
);
unlink($tempFilePath); unlink($tempFilePath);
$this->assertEquals($content, (string) $request->getBody()); $this->assertEquals($content, (string) $request->getBody());
@ -182,7 +178,8 @@ class ServerRequestMarshallerTest extends TestCase
*/ */
public function testProvidesUri(UriInterface $expected, array $serverParams): void public function testProvidesUri(UriInterface $expected, array $serverParams): void
{ {
$request = $this->marshaller->getServerRequest($serverParams); $_SERVER = $serverParams;
$request = $this->marshaller->getServerRequest();
$this->assertEquals($expected, $request->getUri()); $this->assertEquals($expected, $request->getUri());
} }
@ -379,3 +376,25 @@ class ServerRequestMarshallerTest extends TestCase
]; ];
} }
} }
// -----------------------------------------------------------------------------
// Declare fopen function in this namespace so the class under test will use
// this instead of the internal global functions during testing.
class FopenHelper
{
/**
* @var string Path to temp file to read in place of 'php://input'
*/
public static $inputTempFile;
}
function fopen($filename, $mode)
{
if (FopenHelper::$inputTempFile && $filename === 'php://input') {
$filename = FopenHelper::$inputTempFile;
}
return \fopen($filename, $mode);
}