diff --git a/src/Symfony/Component/HttpFoundation/JsonResponse.php b/src/Symfony/Component/HttpFoundation/JsonResponse.php index f6b247bfa1..79791a7233 100644 --- a/src/Symfony/Component/HttpFoundation/JsonResponse.php +++ b/src/Symfony/Component/HttpFoundation/JsonResponse.php @@ -43,6 +43,10 @@ class JsonResponse extends Response { parent::__construct('', $status, $headers); + if ($json && !\is_string($data) && !is_numeric($data) && !\is_callable([$data, '__toString'])) { + throw new \TypeError(sprintf('"%s": If $json is set to true, argument $data must be a string or object implementing __toString(), "%s" given.', __METHOD__, get_debug_type($data))); + } + if (null === $data) { $data = new \ArrayObject(); } @@ -77,13 +81,13 @@ class JsonResponse extends Response * return JsonResponse::fromJsonString('{"key": "value"}') * ->setSharedMaxAge(300); * - * @param string|null $data The JSON response string - * @param int $status The response status code - * @param array $headers An array of response headers + * @param string $data The JSON response string + * @param int $status The response status code + * @param array $headers An array of response headers * * @return static */ - public static function fromJsonString($data = null, $status = 200, $headers = []) + public static function fromJsonString($data, $status = 200, $headers = []) { return new static($data, $status, $headers, true); } diff --git a/src/Symfony/Component/HttpFoundation/Tests/JsonResponseTest.php b/src/Symfony/Component/HttpFoundation/Tests/JsonResponseTest.php index 0468ebd594..8c37fd771b 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/JsonResponseTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/JsonResponseTest.php @@ -240,6 +240,44 @@ class JsonResponseTest extends TestCase $this->assertEquals('/**/ಠ_ಠ["foo"].bar[0]({"foo":"bar"});', $response->getContent()); } + + public function testConstructorWithNullAsDataThrowsAnUnexpectedValueException() + { + $this->expectException(\TypeError::class); + $this->expectExceptionMessage('If $json is set to true, argument $data must be a string or object implementing __toString(), "null" given.'); + + new JsonResponse(null, 200, [], true); + } + + public function testfromJsonStringConstructorWithNullAsDataThrowsAnUnexpectedValueException() + { + $this->expectException(\TypeError::class); + $this->expectExceptionMessage('If $json is set to true, argument $data must be a string or object implementing __toString(), "null" given.'); + + JsonResponse::fromJsonString(null); + } + + public function testConstructorWithObjectWithToStringMethod() + { + $class = new class() { + public function __toString() + { + return '{}'; + } + }; + + $response = new JsonResponse($class, 200, [], true); + + $this->assertSame('{}', $response->getContent()); + } + + public function testConstructorWithObjectWithoutToStringMethodThrowsAnException() + { + $this->expectException(\TypeError::class); + $this->expectExceptionMessage('If $json is set to true, argument $data must be a string or object implementing __toString(), "stdClass" given.'); + + new JsonResponse(new \stdClass(), 200, [], true); + } } if (interface_exists('JsonSerializable', false)) { diff --git a/src/Symfony/Component/HttpFoundation/composer.json b/src/Symfony/Component/HttpFoundation/composer.json index 79f4b396d0..7b3a692bd6 100644 --- a/src/Symfony/Component/HttpFoundation/composer.json +++ b/src/Symfony/Component/HttpFoundation/composer.json @@ -18,7 +18,8 @@ "require": { "php": ">=7.1.3", "symfony/mime": "^4.3|^5.0", - "symfony/polyfill-mbstring": "~1.1" + "symfony/polyfill-mbstring": "~1.1", + "symfony/polyfill-php80": "^1.15" }, "require-dev": { "predis/predis": "~1.0",