diff --git a/src/Symfony/Component/HttpFoundation/JsonResponse.php b/src/Symfony/Component/HttpFoundation/JsonResponse.php index 8e02926e29..85b607132e 100644 --- a/src/Symfony/Component/HttpFoundation/JsonResponse.php +++ b/src/Symfony/Component/HttpFoundation/JsonResponse.php @@ -18,25 +18,21 @@ namespace Symfony\Component\HttpFoundation; */ class JsonResponse extends Response { + protected $data; + protected $callback; + /** * Constructor. * - * @param mixed $data The response data - * @param integer $status The response status code - * @param array $headers An array of response headers + * @param mixed $data The response data + * @param integer $status The response status code + * @param array $headers An array of response headers */ public function __construct($data = array(), $status = 200, $headers = array()) { - // root should be JSON object, not array - if (is_array($data) && 0 === count($data)) { - $data = new \ArrayObject(); - } + parent::__construct('', $status, $headers); - parent::__construct( - json_encode($data), - $status, - array_merge(array('Content-Type' => 'application/json'), $headers) - ); + $this->setData($data); } /** @@ -46,4 +42,64 @@ class JsonResponse extends Response { return new static($data, $status, $headers); } + + /** + * Sets the JSONP callback. + * + * @param string $callback + * + * @return JsonResponse + */ + public function setCallback($callback = null) + { + if ($callback) { + // taken from http://www.geekality.net/2011/08/03/valid-javascript-identifier/ + $pattern = '/^[$_\p{L}][$_\p{L}\p{Mn}\p{Mc}\p{Nd}\p{Pc}\x{200C}\x{200D}]*+$/u'; + if (!preg_match($pattern, $callback)) { + throw new \InvalidArgumentException('The callback name is not valid.'); + } + } + + $this->callback = $callback; + + return $this->update(); + } + + /** + * Sets the data to be sent as json. + * + * @param mixed $data + * + * @return JsonResponse + */ + public function setData($data = array()) + { + // root should be JSON object, not array + if (is_array($data) && 0 === count($data)) { + $data = new \ArrayObject(); + } + + $this->data = json_encode($data); + + return $this->update(); + } + + /** + * Updates the content and headers according to the json data and callback. + * + * @return JsonResponse + */ + protected function update() + { + $content = $this->data; + $this->headers->set('Content-Type', 'application/json', false); + + if ($this->callback) { + $content = sprintf('%s(%s);', $this->callback, $content); + // Not using application/javascript for compatibility reasons with older browsers. + $this->headers->set('Content-Type', 'text/javascript', true); + } + + return $this->setContent($content); + } } diff --git a/tests/Symfony/Tests/Component/HttpFoundation/JsonResponseTest.php b/tests/Symfony/Tests/Component/HttpFoundation/JsonResponseTest.php index 0f373da0ac..d95f46f897 100644 --- a/tests/Symfony/Tests/Component/HttpFoundation/JsonResponseTest.php +++ b/tests/Symfony/Tests/Component/HttpFoundation/JsonResponseTest.php @@ -15,6 +15,8 @@ use Symfony\Component\HttpFoundation\JsonResponse; /** * @covers Symfony\Component\HttpFoundation\JsonResponse::__construct + * @covers Symfony\Component\HttpFoundation\JsonResponse::setData + * @covers Symfony\Component\HttpFoundation\JsonResponse::setCallback */ class JsonResponseTest extends \PHPUnit_Framework_TestCase { @@ -86,4 +88,20 @@ class JsonResponseTest extends \PHPUnit_Framework_TestCase $this->assertEquals('{"foo":"bar"}', $response->getContent()); $this->assertEquals(204, $response->getStatusCode()); } + + public function testSetCallback() + { + $response = JsonResponse::create(array('foo' => 'bar'))->setCallback('callback'); + + $this->assertEquals('callback({"foo":"bar"});', $response->getContent()); + $this->assertEquals('text/javascript', $response->headers->get('Content-Type')); + } + + public function testSetCallbackInvalidIdentifier() + { + $response = new JsonResponse('foo'); + + $this->setExpectedException('InvalidArgumentException'); + $response->setCallback('+invalid'); + } }