From 84289310549011398a761e7b13d7a7e6b8bef63c Mon Sep 17 00:00:00 2001 From: Lonnie Ezell Date: Sun, 11 Jun 2017 22:25:19 -0500 Subject: [PATCH] JSONFormatter corrections and improvements. Fixes #544 --- system/Autoloader/Autoloader.php | 2 + system/Format/JSONFormatter.php | 34 +++++----------- tests/system/API/ResponseTraitTest.php | 37 +++++++++++------ tests/system/Autoloader/AutoloaderTest.php | 4 +- tests/system/Format/JSONFormatterTest.php | 47 +++++++++++++++++++++- 5 files changed, 84 insertions(+), 40 deletions(-) diff --git a/system/Autoloader/Autoloader.php b/system/Autoloader/Autoloader.php index 073db6a18ab4..cb6a19bd8e8c 100644 --- a/system/Autoloader/Autoloader.php +++ b/system/Autoloader/Autoloader.php @@ -255,6 +255,8 @@ protected function loadInNamespace($class) foreach ($directories as $directory) { + $directory = rtrim($directory, '/'); + if (strpos($class, $namespace) === 0) { $filePath = $directory . str_replace('\\', '/', substr($class, strlen($namespace))) . '.php'; $filename = $this->requireFile($filePath); diff --git a/system/Format/JSONFormatter.php b/system/Format/JSONFormatter.php index 6ea86cfbcf26..c8e9583ca6fc 100644 --- a/system/Format/JSONFormatter.php +++ b/system/Format/JSONFormatter.php @@ -38,22 +38,6 @@ class JSONFormatter implements FormatterInterface { - /** - * The error strings to use if encoding hits an error. - * - * @var array - */ - protected $errors = [ - JSON_ERROR_NONE => 'No error has occurred', - JSON_ERROR_DEPTH => 'The maximum stack depth has been exceeded', - JSON_ERROR_STATE_MISMATCH => 'Invalid or malformed JSON', - JSON_ERROR_CTRL_CHAR => 'Control character error, possibly incorrectly encoded', - JSON_ERROR_SYNTAX => 'Syntax error', - JSON_ERROR_UTF8 => 'Malformed UTF-8 characters, possibly incorrectly encoded', - ]; - - //-------------------------------------------------------------------- - /** * Takes the given data and formats it. * @@ -63,20 +47,20 @@ class JSONFormatter implements FormatterInterface */ public function format(array $data) { - $options = ENVIRONMENT == 'production' - ? JSON_NUMERIC_CHECK - : JSON_NUMERIC_CHECK | JSON_PRETTY_PRINT; + $options = JSON_NUMERIC_CHECK | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRESERVE_ZERO_FRACTION; + + $options = ENVIRONMENT === 'production' + ? $options + : $options | JSON_PRETTY_PRINT; - $result = json_encode($data, 512, $options); + $result = json_encode($data, $options, 512); - // If result is NULL, then an error happened. - // Let them know. - if ($result === null) + if (json_last_error() !== JSON_ERROR_NONE) { - throw new \RuntimeException($this->errors[json_last_error()]); + throw new \RuntimeException( sprintf("Failed to parse json string, error: '%s'", json_last_error_msg()) ); } - return utf8_encode($result); + return $result; } //-------------------------------------------------------------------- diff --git a/tests/system/API/ResponseTraitTest.php b/tests/system/API/ResponseTraitTest.php index 127546cfcc39..350892ebea1f 100644 --- a/tests/system/API/ResponseTraitTest.php +++ b/tests/system/API/ResponseTraitTest.php @@ -2,6 +2,7 @@ use CodeIgniter\API\ResponseTrait; use CodeIgniter\Controller; +use CodeIgniter\Format\JSONFormatter; use CodeIgniter\HTTP\MockIncomingRequest; use CodeIgniter\HTTP\MockResponse; use CodeIgniter\HTTP\URI; @@ -11,8 +12,20 @@ class ResponseTraitTest extends \CIUnitTestCase protected $request; protected $response; + /** + * @var JSONFormatter + */ + protected $formatter; - protected function makeController(array $userConfig = [], string $uri = 'http://example.com', array $userHeaders = []) + public function setUp() + { + parent::setUp(); + + $this->formatter = new JSONFormatter(); + } + + + protected function makeController(array $userConfig = [], string $uri = 'http://example.com', array $userHeaders = []) { $config = [ 'baseURL' => 'http://example.com', @@ -125,7 +138,7 @@ public function testFailSingleMessage() ]; $this->assertTrue(strpos($this->response->getHeaderLine('Content-Type'), 'application/json') === 0); - $this->assertEquals(json_encode($expected), $this->response->getBody()); + $this->assertEquals($this->formatter->format($expected), $this->response->getBody()); $this->assertEquals(500, $this->response->getStatusCode()); $this->assertEquals('A Custom Reason', $this->response->getReason()); } @@ -137,7 +150,7 @@ public function testCreated() $this->assertEquals('A Custom Reason', $this->response->getReason()); $this->assertEquals(201, $this->response->getStatusCode()); - $this->assertEquals(json_encode(['id' => 3]), $this->response->getBody()); + $this->assertEquals($this->formatter->format(['id' => 3]), $this->response->getBody()); } public function testDeleted() @@ -147,7 +160,7 @@ public function testDeleted() $this->assertEquals('A Custom Reason', $this->response->getReason()); $this->assertEquals(200, $this->response->getStatusCode()); - $this->assertEquals(json_encode(['id' => 3]), $this->response->getBody()); + $this->assertEquals($this->formatter->format(['id' => 3]), $this->response->getBody()); } public function testUnauthorized() @@ -165,7 +178,7 @@ public function testUnauthorized() $this->assertEquals('A Custom Reason', $this->response->getReason()); $this->assertEquals(401, $this->response->getStatusCode()); - $this->assertEquals(json_encode($expected), $this->response->getBody()); + $this->assertEquals($this->formatter->format($expected), $this->response->getBody()); } public function testForbidden() @@ -183,7 +196,7 @@ public function testForbidden() $this->assertEquals('A Custom Reason', $this->response->getReason()); $this->assertEquals(403, $this->response->getStatusCode()); - $this->assertEquals(json_encode($expected), $this->response->getBody()); + $this->assertEquals($this->formatter->format($expected), $this->response->getBody()); } public function testNotFound() @@ -201,7 +214,7 @@ public function testNotFound() $this->assertEquals('A Custom Reason', $this->response->getReason()); $this->assertEquals(404, $this->response->getStatusCode()); - $this->assertEquals(json_encode($expected), $this->response->getBody()); + $this->assertEquals($this->formatter->format($expected), $this->response->getBody()); } public function testValidationError() @@ -219,7 +232,7 @@ public function testValidationError() $this->assertEquals('A Custom Reason', $this->response->getReason()); $this->assertEquals(400, $this->response->getStatusCode()); - $this->assertEquals(json_encode($expected), $this->response->getBody()); + $this->assertEquals($this->formatter->format($expected), $this->response->getBody()); } public function testResourceExists() @@ -237,7 +250,7 @@ public function testResourceExists() $this->assertEquals('A Custom Reason', $this->response->getReason()); $this->assertEquals(409, $this->response->getStatusCode()); - $this->assertEquals(json_encode($expected), $this->response->getBody()); + $this->assertEquals($this->formatter->format($expected), $this->response->getBody()); } public function testResourceGone() @@ -255,7 +268,7 @@ public function testResourceGone() $this->assertEquals('A Custom Reason', $this->response->getReason()); $this->assertEquals(410, $this->response->getStatusCode()); - $this->assertEquals(json_encode($expected), $this->response->getBody()); + $this->assertEquals($this->formatter->format($expected), $this->response->getBody()); } public function testTooManyRequests() @@ -273,7 +286,7 @@ public function testTooManyRequests() $this->assertEquals('A Custom Reason', $this->response->getReason()); $this->assertEquals(429, $this->response->getStatusCode()); - $this->assertEquals(json_encode($expected), $this->response->getBody()); + $this->assertEquals($this->formatter->format($expected), $this->response->getBody()); } public function testServerError() @@ -283,7 +296,7 @@ public function testServerError() $this::assertEquals('A custom reason.', $this->response->getReason()); $this::assertEquals(500, $this->response->getStatusCode()); - $this::assertEquals(json_encode([ + $this::assertEquals($this->formatter->format([ 'status' => 500, 'error' => 'FAT-CHANCE', 'messages' => [ diff --git a/tests/system/Autoloader/AutoloaderTest.php b/tests/system/Autoloader/AutoloaderTest.php index 19dbcffc4b99..c93547b010dc 100644 --- a/tests/system/Autoloader/AutoloaderTest.php +++ b/tests/system/Autoloader/AutoloaderTest.php @@ -49,7 +49,7 @@ public function testServiceAutoLoaderFromShareInstances() { $auto_loader = \CodeIgniter\Config\Services::autoloader(); // $auto_loader->register(); $actual = $auto_loader->loadClass('App\Controllers\Checks'); - $expected = APPPATH.'/Controllers/Checks.php'; + $expected = APPPATH.'Controllers/Checks.php'; $this->assertSame($expected, $actual); } @@ -62,7 +62,7 @@ public function testServiceAutoLoader() { $auto_loader->initialize(new Autoload()); $auto_loader->register(); $actual = $auto_loader->loadClass('App\Controllers\Checks'); - $expected = APPPATH.'/Controllers/Checks.php'; + $expected = APPPATH.'Controllers/Checks.php'; $this->assertSame($expected, $actual); } diff --git a/tests/system/Format/JSONFormatterTest.php b/tests/system/Format/JSONFormatterTest.php index 052f9de8f8cb..2022a650e678 100644 --- a/tests/system/Format/JSONFormatterTest.php +++ b/tests/system/Format/JSONFormatterTest.php @@ -16,8 +16,53 @@ public function testBasicJSON() 'foo' => 'bar' ]; - $expected = '{"foo":"bar"}'; + $expected = '{ + "foo": "bar" +}'; $this->assertEquals($expected, $this->jsonFormatter->format($data)); } + + public function testUnicodeOutput() + { + $data = [ + 'foo' => 'База данни грешка' + ]; + + $expected = '{ + "foo": "База данни грешка" +}'; + + $this->assertEquals($expected, $this->jsonFormatter->format($data)); + } + + public function testKeepsURLs() + { + $data = [ + 'foo' => 'https://www.example.com/foo/bar' + ]; + + $expected = '{ + "foo": "https://www.example.com/foo/bar" +}'; + + $this->assertEquals($expected, $this->jsonFormatter->format($data)); + } + + public function testDoesNumericFormatting() + { + $data = [ + 'foo' => '32', + 'bar' => 42, + 'baz' => "3.14" + ]; + + $expected = '{ + "foo": 32, + "bar": 42, + "baz": 3.14 +}'; + + $this->assertEquals($expected, $this->jsonFormatter->format($data)); + } }