From 169ee49e9566946bce949a30ce2e7d3a14dc7273 Mon Sep 17 00:00:00 2001 From: Mark Story Date: Wed, 7 Sep 2016 21:42:10 -0400 Subject: [PATCH] Add PSR7 body methods. Add the `withBody` and `getBody` methods. Internally the input parsing has been replaced with streams. This requires changing some internal state and deprecating setInput(). --- src/Network/Request.php | 60 ++++++++++++++++++++------ tests/TestCase/Network/RequestTest.php | 23 ++++++++++ 2 files changed, 70 insertions(+), 13 deletions(-) diff --git a/src/Network/Request.php b/src/Network/Request.php index 4974c1f922b..0a5935b576f 100644 --- a/src/Network/Request.php +++ b/src/Network/Request.php @@ -20,7 +20,10 @@ use Cake\Network\Exception\MethodNotAllowedException; use Cake\Utility\Hash; use InvalidArgumentException; +use Psr\Http\Message\StreamInterface; use Psr\Http\Message\UploadedFileInterface; +use Zend\Diactoros\PhpInputStream; +use Zend\Diactoros\Stream; use Zend\Diactoros\UploadedFile; /** @@ -149,12 +152,11 @@ class Request implements ArrayAccess protected $_detectorCache = []; /** - * Copy of php://input. Since this stream can only be read once in most SAPI's - * keep a copy of it so users don't need to know about that detail. + * Request body stream. Contains php://input unless `input` constructor option is used. * - * @var string + * @var \Psr\Http\Message\StreamInterface */ - protected $_input = ''; + protected $stream; /** * Instance of a Session object relative to this request @@ -280,11 +282,17 @@ protected function _setConfig($config) $this->cookies = $config['cookies']; $this->here = $this->base . '/' . $this->url; $this->webroot = $config['webroot']; - $this->_environment = $config['environment']; + if (isset($config['input'])) { - $this->_input = $config['input']; + $stream = new Stream('php://memory', 'rw'); + $stream->write($config['input']); + $stream->rewind(); + } else { + $stream = new PhpInputStream(); } + $this->stream = $stream; + $config['post'] = $this->_processPost($config['post']); $this->data = $this->_processFiles($config['post'], $config['files']); $this->query = $this->_processGet($config['query']); @@ -1405,16 +1413,14 @@ public function param($name) * @param string|null $callback A decoding callback that will convert the string data to another * representation. Leave empty to access the raw input data. You can also * supply additional parameters for the decoding callback using var args, see above. + * @param array ...$args The additional arguments * @return string The decoded/processed request data. */ - public function input($callback = null) + public function input($callback = null, ...$args) { - $input = $this->_readInput(); - $args = func_get_args(); - if (!empty($args)) { - $callback = array_shift($args); + $input = $this->stream->getContents(); + if ($callback) { array_unshift($args, $input); - return call_user_func_array($callback, $args); } @@ -1573,10 +1579,14 @@ protected function _readInput() * * @param string $input A string to replace original parsed data from input() * @return void + * @deprecated 3.4.0 This method will be removed in 4.0.0. Use withBody() instead. */ public function setInput($input) { - $this->_input = $input; + $stream = new Stream('php://memory', 'rw'); + $stream->write($input); + $stream->rewind(); + $this->stream = $stream; } /** @@ -1756,6 +1766,30 @@ protected function validateUploadedFiles(array $uploadedFiles, $path) } } + /** + * Gets the body of the message. + * + * @return \Psr\Http\Message\StreamInterface Returns the body as a stream. + */ + public function getBody() + { + return $this->stream; + } + + /** + * Return an instance with the specified message body. + * + * @param \Psr\Http\Message\StreamInterface $body The new request body + * @return static + */ + public function withBody(StreamInterface $body) + { + $new = clone $this; + $new->stream = $body; + + return $new; + } + /** * Array access read implementation * diff --git a/tests/TestCase/Network/RequestTest.php b/tests/TestCase/Network/RequestTest.php index f557003a12c..db8a95da25c 100644 --- a/tests/TestCase/Network/RequestTest.php +++ b/tests/TestCase/Network/RequestTest.php @@ -2628,6 +2628,29 @@ public function testInputDecodeExtraParams() */ public function testGetBody() { + $request = new Request([ + 'input' => 'key=val&some=data' + ]); + $result = $request->getBody(); + $this->assertInstanceOf('Psr\Http\Message\StreamInterface', $result); + $this->assertEquals('key=val&some=data', $result->getContents()); + } + + /** + * Test withBody + * + * @return void + */ + public function testWithBody() + { + $request = new Request([ + 'input' => 'key=val&some=data' + ]); + $body = $this->getMockBuilder('Psr\Http\Message\StreamInterface')->getMock(); + $new = $request->withBody($body); + $this->assertNotSame($new, $request); + $this->assertNotSame($body, $request->getBody()); + $this->assertSame($body, $new->getBody()); } /**