diff --git a/app/Config/Format.php b/app/Config/Format.php index 5f64b702f424..5c3811058e45 100644 --- a/app/Config/Format.php +++ b/app/Config/Format.php @@ -1,4 +1,6 @@ - \CodeIgniter\Format\XMLFormatter::class, 'text/xml' => \CodeIgniter\Format\XMLFormatter::class, ]; - - //-------------------------------------------------------------------- - - /** - * A Factory method to return the appropriate formatter for the given mime type. - * - * @param string $mime - * - * @return \CodeIgniter\Format\FormatterInterface - */ - public function getFormatter(string $mime) - { - if (! array_key_exists($mime, $this->formatters)) - { - throw new \InvalidArgumentException('No Formatter defined for mime type: ' . $mime); - } - - $class = $this->formatters[$mime]; - - if (! class_exists($class)) - { - throw new \BadMethodCallException($class . ' is not a valid Formatter.'); - } - - return new $class(); - } - - //-------------------------------------------------------------------- - } diff --git a/system/API/ResponseTrait.php b/system/API/ResponseTrait.php index 49248eb198ed..b57e899f62e4 100644 --- a/system/API/ResponseTrait.php +++ b/system/API/ResponseTrait.php @@ -40,7 +40,7 @@ namespace CodeIgniter\API; use CodeIgniter\HTTP\Response; -use Config\Format; +use Config\Services; /** * Response trait. @@ -134,8 +134,7 @@ public function respond($data = null, int $status = null, string $message = '') $output = $this->format($data); } - return $this->response->setBody($output) - ->setStatusCode($status, $message); + return $this->response->setBody($output)->setStatusCode($status, $message); } //-------------------------------------------------------------------- @@ -386,30 +385,31 @@ protected function format($data = null) return $data; } - $config = new Format(); - $format = "application/$this->format"; + $mime = "application/$this->format"; + + $format = Services::format(); // Determine correct response type through content negotiation if not explicitly declared if (empty($this->format) || ! in_array($this->format, ['json', 'xml'])) { - $format = $this->request->negotiate('media', $config->supportedResponseFormats, false); + $mime = $this->request->negotiate('media', $format->getConfig()->supportedResponseFormats); } - $this->response->setContentType($format); + $this->response->setContentType($mime); - // if we don't have a formatter, make one - if (! isset($this->formatter)) - { - // if no formatter, use the default - $this->formatter = $config->getFormatter($format); - } - - if ($format !== 'application/json') + if ($mime !== 'application/json') { // Recursively convert objects into associative arrays // Conversion not required for JSONFormatter $data = json_decode(json_encode($data), true); } + + // if we don't have a formatter, make one + if (! isset($this->formatter)) + { + // if no formatter, use the default + $this->formatter = $format->getFormatter($mime); + } return $this->formatter->format($data); } diff --git a/system/Config/Services.php b/system/Config/Services.php index e7e2aeb93ce3..4862270d1082 100644 --- a/system/Config/Services.php +++ b/system/Config/Services.php @@ -48,6 +48,7 @@ use CodeIgniter\Encryption\EncrypterInterface; use CodeIgniter\Encryption\Encryption; use CodeIgniter\Filters\Filters; +use CodeIgniter\Format\Format; use CodeIgniter\Honeypot\Honeypot; use CodeIgniter\HTTP\CLIRequest; use CodeIgniter\HTTP\CURLRequest; @@ -310,6 +311,31 @@ public static function filters($config = null, bool $getShared = true) //-------------------------------------------------------------------- + /** + * The Format class works as a factory for formatters. + * + * @param mixed $config + * @param boolean $getShared + * + * @return \CodeIgniter\Format\Format + */ + public static function format($config = null, bool $getShared = true) + { + if ($getShared) + { + return static::getSharedInstance('format', $config); + } + + if (is_null($config)) + { + $config = new \Config\Format(); + } + + return new Format($config); + } + + //-------------------------------------------------------------------- + /** * The Honeypot provides a secret input on forms that bots should NOT * fill in, providing an additional safeguard when accepting user input. diff --git a/system/Format/Exceptions/FormatException.php b/system/Format/Exceptions/FormatException.php index 4529fd0fafa0..523c8346de85 100644 --- a/system/Format/Exceptions/FormatException.php +++ b/system/Format/Exceptions/FormatException.php @@ -1,14 +1,27 @@ -config = $config; + } + + //-------------------------------------------------------------------- + + /** + * A Factory method to return the appropriate formatter for the given mime type. + * + * @param string $mime + * + * @return CodeIgniter\Format\FormatterInterface + */ + public function getFormatter(string $mime): FormatterInterface + { + if (! array_key_exists($mime, $this->config->formatters)) + { + throw FormatException::forInvalidMime($mime); + } + + $class = $this->config->formatters[$mime]; + + if (! class_exists($class)) + { + throw FormatException::forInvalidFormatter($class); + } + + return new $class(); + } + + /** + * Get instance of format configuration class + * + * @return \Config\Format + */ + public function getConfig(): object + { + if (! $this->config instanceof \Config\Format) + { + return $this->config; + } + + return new \Config\Format(); + } +} diff --git a/system/HTTP/Response.php b/system/HTTP/Response.php index e0127321bc3a..9a09b7970b2b 100644 --- a/system/HTTP/Response.php +++ b/system/HTTP/Response.php @@ -43,7 +43,7 @@ use CodeIgniter\HTTP\Exceptions\HTTPException; use CodeIgniter\Pager\PagerInterface; use Config\App; -use Config\Format; +use Config\Services; /** * Representation of an outgoing, getServer-side response. @@ -467,13 +467,7 @@ public function getJSON() if ($this->bodyFormat !== 'json') { - /** - * @var Format $config - */ - $config = config(Format::class); - $formatter = $config->getFormatter('application/json'); - - $body = $formatter->format($body); + $body = Services::format()->getFormatter('application/json')->format($body); } return $body ?: null; @@ -509,13 +503,7 @@ public function getXML() if ($this->bodyFormat !== 'xml') { - /** - * @var Format $config - */ - $config = config(Format::class); - $formatter = $config->getFormatter('application/xml'); - - $body = $formatter->format($body); + $body = Services::format()->getFormatter('application/xml')->format($body); } return $body; @@ -542,13 +530,7 @@ protected function formatBody($body, string $format) // Nothing much to do for a string... if (! is_string($body) || $format === 'json-unencoded') { - /** - * @var Format $config - */ - $config = config(Format::class); - $formatter = $config->getFormatter($mime); - - $body = $formatter->format($body); + $body = Services::format()->getFormatter($mime)->format($body); } return $body; diff --git a/system/Language/en/Format.php b/system/Language/en/Format.php index c0070c8534b5..45afd33066e0 100644 --- a/system/Language/en/Format.php +++ b/system/Language/en/Format.php @@ -15,6 +15,8 @@ */ return [ + 'invalidFormatter' => '"{0}" is not a valid Formatter.', 'invalidJSON' => 'Failed to parse json string, error: "{0}".', + 'invalidMime' => 'No Formatter defined for mime type: "{0}".', 'missingExtension' => 'The SimpleXML extension is required to format XML.', ]; diff --git a/system/Test/FeatureResponse.php b/system/Test/FeatureResponse.php index 6c919b7638eb..2c79e7d0eeeb 100644 --- a/system/Test/FeatureResponse.php +++ b/system/Test/FeatureResponse.php @@ -40,7 +40,7 @@ use CodeIgniter\HTTP\RedirectResponse; use CodeIgniter\HTTP\ResponseInterface; -use Config\Format; +use Config\Services; use PHPUnit\Framework\TestCase; /** @@ -414,9 +414,8 @@ public function assertJSONExact($test) if (is_array($test)) { - $config = new Format(); - $formatter = $config->getFormatter('application/json'); - $test = $formatter->format($test); + $format = Services::format(); + $test = $format->getFormatter('application/json')->format($test); } $this->assertJsonStringEqualsJsonString($test, $json, 'Response does not contain matching JSON.'); diff --git a/tests/system/HTTP/ResponseTest.php b/tests/system/HTTP/ResponseTest.php index b6a73e51ee76..5f68de82c800 100644 --- a/tests/system/HTTP/ResponseTest.php +++ b/tests/system/HTTP/ResponseTest.php @@ -5,7 +5,7 @@ use CodeIgniter\HTTP\Exceptions\HTTPException; use CodeIgniter\Test\Mock\MockResponse; use Config\App; -use Config\Format; +use Config\Services; use DateTime; use DateTimeZone; @@ -341,8 +341,6 @@ public function testSetCookieSuccessOnPrefix() public function testJSONWithArray() { $response = new Response(new App()); - $config = new Format(); - $formatter = $config->getFormatter('application/json'); $body = [ 'foo' => 'bar', @@ -352,19 +350,16 @@ public function testJSONWithArray() 3, ], ]; - $expected = $formatter->format($body); $response->setJSON($body); - $this->assertEquals($expected, $response->getJSON()); + $this->assertEquals(Services::format()->getFormatter('application/json')->format($body), $response->getJSON()); $this->assertTrue(strpos($response->getHeaderLine('content-type'), 'application/json') !== false); } public function testJSONGetFromNormalBody() { $response = new Response(new App()); - $config = new Format(); - $formatter = $config->getFormatter('application/json'); $body = [ 'foo' => 'bar', @@ -374,11 +369,10 @@ public function testJSONGetFromNormalBody() 3, ], ]; - $expected = $formatter->format($body); $response->setBody($body); - $this->assertEquals($expected, $response->getJSON()); + $this->assertEquals(Services::format()->getFormatter('application/json')->format($body), $response->getJSON()); } //-------------------------------------------------------------------- @@ -386,8 +380,6 @@ public function testJSONGetFromNormalBody() public function testXMLWithArray() { $response = new Response(new App()); - $config = new Format(); - $formatter = $config->getFormatter('application/xml'); $body = [ 'foo' => 'bar', @@ -397,19 +389,16 @@ public function testXMLWithArray() 3, ], ]; - $expected = $formatter->format($body); $response->setXML($body); - $this->assertEquals($expected, $response->getXML()); + $this->assertEquals(Services::format()->getFormatter('application/xml')->format($body), $response->getXML()); $this->assertTrue(strpos($response->getHeaderLine('content-type'), 'application/xml') !== false); } public function testXMLGetFromNormalBody() { $response = new Response(new App()); - $config = new Format(); - $formatter = $config->getFormatter('application/xml'); $body = [ 'foo' => 'bar', @@ -419,11 +408,10 @@ public function testXMLGetFromNormalBody() 3, ], ]; - $expected = $formatter->format($body); $response->setBody($body); - $this->assertEquals($expected, $response->getXML()); + $this->assertEquals(Services::format()->getFormatter('application/xml')->format($body), $response->getXML()); } //-------------------------------------------------------------------- diff --git a/tests/system/Test/FeatureResponseTest.php b/tests/system/Test/FeatureResponseTest.php index 75e6aa9ae66a..7ac3c22f255b 100644 --- a/tests/system/Test/FeatureResponseTest.php +++ b/tests/system/Test/FeatureResponseTest.php @@ -2,9 +2,12 @@ use CodeIgniter\HTTP\RedirectResponse; use CodeIgniter\HTTP\Response; +use CodeIgniter\Test\CIUnitTestCase; use CodeIgniter\Test\FeatureResponse; +use Config\App; +use Config\Services; -class FeatureResponseTest extends \CodeIgniter\Test\CIUnitTestCase +class FeatureResponseTest extends CIUnitTestCase { /** @@ -115,7 +118,7 @@ public function testAssertRedirectFail() public function testAssertRedirectSuccess() { $this->getFeatureResponse('