diff --git a/docs/book/response.md b/docs/book/response.md index 5a52153..a390dc2 100644 --- a/docs/book/response.md +++ b/docs/book/response.md @@ -151,6 +151,23 @@ This package also provides a factory for generating the value is provided as the `$defaultTypesMap` parameter; see the [default types documentation](default-types.md) for details on defining this map. (Since 1.1.0.) + - If the service contains a `problem-details` key with an array value + containing a `include-throwable-details` key, + and that value is a boolean, + that value is used instead of global `debug` value for the `$includeThrowableDetail` parameter. + (Since 1.14.0.) + +### Example configuration + +```php +[ + 'debug' => true, + 'problem-details' => [ + 'json_flags' => JSON_PRETTY_PRINT, + 'include-throwable-details' => true, + ] +] +``` If any of the above config values are not present, a `null` value will be passed, allowing the default value to be used. diff --git a/src/ProblemDetailsResponseFactoryFactory.php b/src/ProblemDetailsResponseFactoryFactory.php index aaaf2a6..2bf563e 100644 --- a/src/ProblemDetailsResponseFactoryFactory.php +++ b/src/ProblemDetailsResponseFactoryFactory.php @@ -20,15 +20,22 @@ public function __invoke(ContainerInterface $container): ProblemDetailsResponseF { $config = $container->has('config') ? $container->get('config') : []; Assert::isArrayAccessible($config); - $debug = isset($config['debug']) && is_bool($config['debug']) ? $config['debug'] : null; - $includeThrowableDetail = $debug ?? ProblemDetailsResponseFactory::EXCLUDE_THROWABLE_DETAILS; + $debug = isset($config['debug']) && is_bool($config['debug']) ? $config['debug'] : null; + $debug ??= ProblemDetailsResponseFactory::EXCLUDE_THROWABLE_DETAILS; $problemDetailsConfig = $config['problem-details'] ?? []; Assert::isArrayAccessible($problemDetailsConfig); + + $includeThrowableDetail = isset($problemDetailsConfig['include-throwable-details']) + && is_bool($problemDetailsConfig['include-throwable-details']) + ? $problemDetailsConfig['include-throwable-details'] : null; + $includeThrowableDetail ??= $debug; + $jsonFlags = $problemDetailsConfig['json_flags'] ?? null; assert($jsonFlags === null || is_int($jsonFlags)); $defaultTypesMap = $problemDetailsConfig['default_types_map'] ?? []; Assert::isArray($defaultTypesMap); + foreach ($defaultTypesMap as $key => $value) { assert(is_int($key)); assert(is_string($value)); @@ -38,7 +45,7 @@ public function __invoke(ContainerInterface $container): ProblemDetailsResponseF return new ProblemDetailsResponseFactory( $this->detectResponseFactory($container), - $includeThrowableDetail, + $debug, $jsonFlags, $includeThrowableDetail, ProblemDetailsResponseFactory::DEFAULT_DETAIL_MESSAGE, diff --git a/test/ProblemDetailsResponseFactoryFactoryTest.php b/test/ProblemDetailsResponseFactoryFactoryTest.php index acab0c1..55328ab 100644 --- a/test/ProblemDetailsResponseFactoryFactoryTest.php +++ b/test/ProblemDetailsResponseFactoryFactoryTest.php @@ -59,7 +59,7 @@ public function testLackOfConfigServiceResultsInFactoryUsingDefaults(): void { $response = $this->createMock(ResponseInterface::class); $response->method('withStatus')->willReturnSelf(); - $this->container->set(ResponseInterface::class, static fn () => $response); + $this->container->set(ResponseInterface::class, static fn() => $response); $factoryFactory = new ProblemDetailsResponseFactoryFactory(); $factory = $factoryFactory($this->container); @@ -132,4 +132,25 @@ public function testUsesDefaultTypesSettingFromConfigWhenPresent(): void self::assertSame($expectedDefaultTypes, $defaultTypesMap->getValue($factory)); } + + public function testUsesIncludeThrowableDetailsSettingFromConfigWhenPresent(): void + { + $this->container->set( + 'config', + [ + 'problem-details' => [ + 'include-throwable-details' => ProblemDetailsResponseFactory::INCLUDE_THROWABLE_DETAILS, + ], + ] + ); + $this->container->set(ResponseInterface::class, static fn() => null); + + $factoryFactory = new ProblemDetailsResponseFactoryFactory(); + $factory = $factoryFactory($this->container); + $isDebug = (new ReflectionObject($factory))->getProperty('isDebug'); + $exceptionDetailsInResponse = (new ReflectionObject($factory))->getProperty('exceptionDetailsInResponse'); + + self::assertSame(ProblemDetailsResponseFactory::EXCLUDE_THROWABLE_DETAILS, $isDebug->getValue($factory)); + self::assertSame(true, $exceptionDetailsInResponse->getValue($factory)); + } }