diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index 14cff76..7dd79f4 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -16,6 +16,7 @@ Yii Framework 2 Change Log - Enh #12938: Allow to pass additional parameters to `yii\base\View::renderDynamic()` (mikehaertl) - Enh #13006: Added a `/` to the `yii\captcha\Captcha::$captchaAction` string to work correctly in a module also (boehsermoe) - Removed methods marked as deprecated in 2.0.x (samdark) +- Chg #14784: Signature of `yii\web\RequestParserInterface::parse()` changed to accept `yii\web\Request` instance as a sole argument (klimov-paul) - Chg #10771: Consistent behavior of `run()` method in all framework widgets. All return the result now for better extensibility (pkirill99, cebe) - Chg #11397: Minimum required version of PHP is 7.1 now (samdark) - Chg: Removed `yii\base\Object::className()` in favor of native PHP syntax `::class`, which does not trigger autoloading (cebe) diff --git a/framework/UPGRADE.md b/framework/UPGRADE.md index f4c57d5..89800e5 100644 --- a/framework/UPGRADE.md +++ b/framework/UPGRADE.md @@ -110,6 +110,8 @@ Upgrade from Yii 2.0.x with PHP. For details please refer to [guide on autoloading](https://github.com/yiisoft/yii2/blob/2.1/docs/guide/concept-autoloading.md), [guide on customizing helpers](https://github.com/yiisoft/yii2/blob/2.1/docs/guide/helper-overview.md#customizing-helper-classes-) and [guide on Working with Third-Party Code](https://github.com/yiisoft/yii2/blob/2.1/docs/guide/tutorial-yii-integration.md). +* The signature of `yii\web\RequestParserInterface::parse()` was changed. The method now accepts the `yii\web\Request` instance + as a sole argument. Make sure you declare and implement this method correctly, while creating your own request parser. Upgrade from Yii 2.0.12 diff --git a/framework/web/JsonParser.php b/framework/web/JsonParser.php index f9e6964..4398714 100644 --- a/framework/web/JsonParser.php +++ b/framework/web/JsonParser.php @@ -39,16 +39,13 @@ class JsonParser implements RequestParserInterface /** - * Parses a HTTP request body. - * @param string $rawBody the raw HTTP request body. - * @param string $contentType the content type specified for the request body. - * @return array parameters parsed from the request body + * {@inheritdoc} * @throws BadRequestHttpException if the body contains invalid json and [[throwException]] is `true`. */ - public function parse($rawBody, $contentType) + public function parse($request) { try { - $parameters = Json::decode($rawBody, $this->asArray); + $parameters = Json::decode($request->getBody()->__toString(), $this->asArray); return $parameters === null ? [] : $parameters; } catch (InvalidArgumentException $e) { if ($this->throwException) { diff --git a/framework/web/MultipartFormDataParser.php b/framework/web/MultipartFormDataParser.php index 0ca70ab..54e1997 100644 --- a/framework/web/MultipartFormDataParser.php +++ b/framework/web/MultipartFormDataParser.php @@ -123,9 +123,9 @@ class MultipartFormDataParser extends BaseObject implements RequestParserInterfa } /** - * @inheritdoc + * {@inheritdoc} */ - public function parse($rawBody, $contentType) + public function parse($request) { if (!$this->force) { if (!empty($_POST) || !empty($_FILES)) { @@ -136,6 +136,9 @@ class MultipartFormDataParser extends BaseObject implements RequestParserInterfa $_FILES = []; } + $contentType = $request->getContentType(); + $rawBody = $request->getBody()->__toString(); + if (empty($rawBody)) { return []; } diff --git a/framework/web/Request.php b/framework/web/Request.php index e4a193b..a2ea991 100644 --- a/framework/web/Request.php +++ b/framework/web/Request.php @@ -16,6 +16,7 @@ use yii\di\Instance; use yii\http\Cookie; use yii\http\CookieCollection; use yii\http\FileStream; +use yii\http\MemoryStream; use yii\http\MessageTrait; use yii\http\Uri; @@ -479,19 +480,13 @@ class Request extends \yii\base\Request implements RequestInterface ]); } - private $_rawBody; - /** * Returns the raw HTTP request body. * @return string the request body */ public function getRawBody() { - if ($this->_rawBody === null) { - $this->_rawBody = $this->getBody()->__toString(); - } - - return $this->_rawBody; + return $this->getBody()->__toString(); } /** @@ -500,7 +495,9 @@ class Request extends \yii\base\Request implements RequestInterface */ public function setRawBody($rawBody) { - $this->_rawBody = $rawBody; + $body = new MemoryStream(); + $body->write($rawBody); + $this->setBody($body); } private $_bodyParams; @@ -539,13 +536,13 @@ class Request extends \yii\base\Request implements RequestInterface if (!($parser instanceof RequestParserInterface)) { throw new InvalidConfigException("The '$contentType' request parser is invalid. It must implement the yii\\web\\RequestParserInterface."); } - $this->_bodyParams = $parser->parse($this->getRawBody(), $rawContentType); + $this->_bodyParams = $parser->parse($this); } elseif (isset($this->parsers['*'])) { $parser = Yii::createObject($this->parsers['*']); if (!($parser instanceof RequestParserInterface)) { throw new InvalidConfigException('The fallback request parser is invalid. It must implement the yii\\web\\RequestParserInterface.'); } - $this->_bodyParams = $parser->parse($this->getRawBody(), $rawContentType); + $this->_bodyParams = $parser->parse($this); } elseif ($this->getMethod() === 'POST') { // PHP has already parsed the body so we have all params in $_POST $this->_bodyParams = $_POST; diff --git a/framework/web/RequestParserInterface.php b/framework/web/RequestParserInterface.php index 2108b82..4b58153 100644 --- a/framework/web/RequestParserInterface.php +++ b/framework/web/RequestParserInterface.php @@ -11,15 +11,18 @@ namespace yii\web; * Interface for classes that parse the raw request body into a parameters array. * * @author Dan Schmidt + * @author Paul Klimov * @since 2.0 */ interface RequestParserInterface { /** - * Parses a HTTP request body. - * @param string $rawBody the raw HTTP request body. - * @param string $contentType the content type specified for the request body. - * @return array parameters parsed from the request body + * Parses a given request instance determining its body parameters. + * This method MUST return the array of body parameters detected from [[$request]] data. + * However, this method MAY adjust the given [[Request]] instance directly for extra configuration. + * @param Request $request the HTTP request instance to be parsed. + * @return array parameters parsed from the request body. + * @throws BadRequestHttpException in case request body format is invalid. */ - public function parse($rawBody, $contentType); + public function parse($request); } diff --git a/tests/framework/web/MultipartFormDataParserTest.php b/tests/framework/web/MultipartFormDataParserTest.php index c4b2f82..2dabf50 100644 --- a/tests/framework/web/MultipartFormDataParserTest.php +++ b/tests/framework/web/MultipartFormDataParserTest.php @@ -8,6 +8,7 @@ namespace yiiunit\framework\web; use yii\web\MultipartFormDataParser; +use yii\web\Request; use yiiunit\TestCase; class MultipartFormDataParserTest extends TestCase @@ -24,7 +25,14 @@ class MultipartFormDataParserTest extends TestCase $rawBody .= "\r\n--{$boundary}\nContent-Disposition: form-data; name=\"Item[file]\"; filename=\"item-file.txt\"\nContent-Type: text/plain\r\n\r\nitem file content"; $rawBody .= "\r\n--{$boundary}--"; - $bodyParams = $parser->parse($rawBody, $contentType); + $request = new Request([ + 'rawBody' => $rawBody, + 'headers' => [ + 'content-type' => [$contentType] + ] + ]); + + $bodyParams = $parser->parse($request); $expectedBodyParams = [ 'title' => 'test-title', @@ -59,7 +67,13 @@ class MultipartFormDataParserTest extends TestCase 'name' => 'value', ]; - $bodyParams = $parser->parse('should not matter', 'multipart/form-data; boundary=---12345'); + $request = new Request([ + 'rawBody' => 'should not matter', + 'headers' => [ + 'content-type' => ['multipart/form-data; boundary=---12345'] + ] + ]); + $bodyParams = $parser->parse($request); $this->assertEquals($_POST, $bodyParams); $this->assertEquals([], $_FILES); } @@ -82,7 +96,13 @@ class MultipartFormDataParserTest extends TestCase $contentType = 'multipart/form-data; boundary=' . $boundary; $rawBody = "--{$boundary}\nContent-Disposition: form-data; name=\"title\"\r\ntest-title--{$boundary}--"; - $bodyParams = $parser->parse($rawBody, $contentType); + $request = new Request([ + 'rawBody' => $rawBody, + 'headers' => [ + 'content-type' => [$contentType] + ] + ]); + $bodyParams = $parser->parse($request); $this->assertEquals([], $bodyParams); } @@ -101,7 +121,13 @@ class MultipartFormDataParserTest extends TestCase $rawBody .= "--{$boundary}\nContent-Disposition: form-data; name=\"thirdFile\"; filename=\"third-file.txt\"\nContent-Type: text/plain\r\n\r\nthird file content"; $rawBody .= "--{$boundary}--"; - $parser->parse($rawBody, $contentType); + $request = new Request([ + 'rawBody' => $rawBody, + 'headers' => [ + 'content-type' => [$contentType] + ] + ]); + $parser->parse($request); $this->assertCount(2, $_FILES); } @@ -120,7 +146,13 @@ class MultipartFormDataParserTest extends TestCase $rawBody .= "--{$boundary}\nContent-Disposition: form-data; name=\"thirdFile\"; filename=\"third-file.txt\"\nContent-Type: text/plain\r\n\r\nthird file with too long file content"; $rawBody .= "--{$boundary}--"; - $parser->parse($rawBody, $contentType); + $request = new Request([ + 'rawBody' => $rawBody, + 'headers' => [ + 'content-type' => [$contentType] + ] + ]); + $parser->parse($request); $this->assertCount(3, $_FILES); $this->assertEquals(UPLOAD_ERR_INI_SIZE, $_FILES['thirdFile']['error']); } @@ -150,7 +182,13 @@ class MultipartFormDataParserTest extends TestCase $rawBody .= "\r\n--{$boundary}\nContent-Disposition: form-data; name=\"someFile\"; filename=\"some-file.txt\"\nContent-Type: text/plain\r\n\r\nsome file content"; $rawBody .= "\r\n--{$boundary}--"; - $bodyParams = $parser->parse($rawBody, $contentType); + $request = new Request([ + 'rawBody' => $rawBody, + 'headers' => [ + 'content-type' => [$contentType] + ] + ]); + $bodyParams = $parser->parse($request); $expectedBodyParams = [ 'title' => 'test-title',