bug #32232 [ErrorCatcher] some cleanup and better doc (Tobion)
This PR was merged into the 4.4 branch.
Discussion
----------
[ErrorCatcher] some cleanup and better doc
| Q | A
| ------------- | ---
| Branch? | 4.4
| Bug fix? | no
| New feature? | no <!-- please update src/**/CHANGELOG.md files -->
| BC breaks? | no <!-- see https://symfony.com/bc -->
| Deprecations? | no <!-- please update UPGRADE-*.md and src/**/CHANGELOG.md files -->
| Tests pass? | yes <!-- please add some, will be required by reviewers -->
| Fixed tickets |
| License | MIT
| Doc PR |
Commits
-------
a609f57c03
[ErrorCatcher] some cleanup and better doc
This commit is contained in:
commit
7f6ed32a1d
@ -22,7 +22,7 @@
|
|||||||
<argument type="service" id="debug.file_link_formatter" />
|
<argument type="service" id="debug.file_link_formatter" />
|
||||||
<argument>%kernel.debug%</argument>
|
<argument>%kernel.debug%</argument>
|
||||||
<argument>%kernel.charset%</argument>
|
<argument>%kernel.charset%</argument>
|
||||||
<argument type="service" id="error_handler.error_renderer" on-invalid="null" />
|
<argument type="service" id="error_catcher.error_formatter" on-invalid="null" />
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<service id="debug.file_link_formatter" class="Symfony\Component\HttpKernel\Debug\FileLinkFormatter">
|
<service id="debug.file_link_formatter" class="Symfony\Component\HttpKernel\Debug\FileLinkFormatter">
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd">
|
xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||||
|
|
||||||
<services>
|
<services>
|
||||||
<service id="error_catcher.error_renderer" class="Symfony\Component\ErrorCatcher\DependencyInjection\ErrorRenderer">
|
<service id="error_catcher.error_formatter" class="Symfony\Component\ErrorCatcher\DependencyInjection\LazyLoadingErrorFormatter">
|
||||||
<argument /> <!-- error renderer locator -->
|
<argument /> <!-- error renderer locator -->
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ class ErrorCatcherPass implements CompilerPassInterface
|
|||||||
private $rendererService;
|
private $rendererService;
|
||||||
private $rendererTag;
|
private $rendererTag;
|
||||||
|
|
||||||
public function __construct(string $rendererService = 'error_catcher.error_renderer', string $rendererTag = 'error_catcher.renderer')
|
public function __construct(string $rendererService = 'error_catcher.error_formatter', string $rendererTag = 'error_catcher.renderer')
|
||||||
{
|
{
|
||||||
$this->rendererService = $rendererService;
|
$this->rendererService = $rendererService;
|
||||||
$this->rendererTag = $rendererTag;
|
$this->rendererTag = $rendererTag;
|
||||||
|
@ -12,14 +12,14 @@
|
|||||||
namespace Symfony\Component\ErrorCatcher\DependencyInjection;
|
namespace Symfony\Component\ErrorCatcher\DependencyInjection;
|
||||||
|
|
||||||
use Psr\Container\ContainerInterface;
|
use Psr\Container\ContainerInterface;
|
||||||
use Symfony\Component\ErrorCatcher\ErrorRenderer\ErrorRenderer as BaseErrorRenderer;
|
use Symfony\Component\ErrorCatcher\ErrorRenderer\ErrorFormatter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lazily loads error renderers from the dependency injection container.
|
* Lazily loads error renderers from the dependency injection container.
|
||||||
*
|
*
|
||||||
* @author Yonel Ceruto <yonelceruto@gmail.com>
|
* @author Yonel Ceruto <yonelceruto@gmail.com>
|
||||||
*/
|
*/
|
||||||
class ErrorRenderer extends BaseErrorRenderer
|
class LazyLoadingErrorFormatter extends ErrorFormatter
|
||||||
{
|
{
|
||||||
private $container;
|
private $container;
|
||||||
private $initialized = [];
|
private $initialized = [];
|
@ -15,33 +15,38 @@ use Symfony\Component\ErrorCatcher\Exception\ErrorRendererNotFoundException;
|
|||||||
use Symfony\Component\ErrorCatcher\Exception\FlattenException;
|
use Symfony\Component\ErrorCatcher\Exception\FlattenException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders an Exception that represents a Response content.
|
* Formats an exception to be used as response content.
|
||||||
|
*
|
||||||
|
* It delegates to implementations of ErrorRendererInterface depending on the format.
|
||||||
*
|
*
|
||||||
* @see ErrorRendererInterface
|
* @see ErrorRendererInterface
|
||||||
*
|
*
|
||||||
* @author Yonel Ceruto <yonelceruto@gmail.com>
|
* @author Yonel Ceruto <yonelceruto@gmail.com>
|
||||||
*/
|
*/
|
||||||
class ErrorRenderer
|
class ErrorFormatter
|
||||||
{
|
{
|
||||||
private $renderers = [];
|
private $renderers = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param ErrorRendererInterface[] $renderers
|
* @param ErrorRendererInterface[] $renderers
|
||||||
*/
|
*/
|
||||||
public function __construct(array $renderers)
|
public function __construct(iterable $renderers)
|
||||||
{
|
{
|
||||||
foreach ($renderers as $renderer) {
|
foreach ($renderers as $renderer) {
|
||||||
if (!$renderer instanceof ErrorRendererInterface) {
|
$this->addRenderer($renderer);
|
||||||
throw new \InvalidArgumentException(sprintf('Error renderer "%s" must implement "%s".', \get_class($renderer), ErrorRendererInterface::class));
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->addRenderer($renderer, $renderer::getFormat());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function addRenderer(ErrorRendererInterface $renderer, string $format): self
|
/**
|
||||||
|
* Registers an error renderer that is format specific.
|
||||||
|
*
|
||||||
|
* By passing an explicit format you can register a renderer for a different format than what
|
||||||
|
* ErrorRendererInterface::getFormat() would return in order to register the same renderer for
|
||||||
|
* several format aliases.
|
||||||
|
*/
|
||||||
|
public function addRenderer(ErrorRendererInterface $renderer, string $format = null): self
|
||||||
{
|
{
|
||||||
$this->renderers[$format] = $renderer;
|
$this->renderers[$format ?? $renderer::getFormat()] = $renderer;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
@ -14,23 +14,19 @@ namespace Symfony\Component\ErrorCatcher\ErrorRenderer;
|
|||||||
use Symfony\Component\ErrorCatcher\Exception\FlattenException;
|
use Symfony\Component\ErrorCatcher\Exception\FlattenException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface implemented by all error renderers.
|
* Interface for classes that can render errors in a specific format.
|
||||||
*
|
*
|
||||||
* @author Yonel Ceruto <yonelceruto@gmail.com>
|
* @author Yonel Ceruto <yonelceruto@gmail.com>
|
||||||
*/
|
*/
|
||||||
interface ErrorRendererInterface
|
interface ErrorRendererInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Gets the format of the content.
|
* Gets the format this renderer can return errors as.
|
||||||
*
|
|
||||||
* @return string The content format
|
|
||||||
*/
|
*/
|
||||||
public static function getFormat(): string;
|
public static function getFormat(): string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders an Exception and returns the Response content.
|
* Returns the response content of the rendered exception.
|
||||||
*
|
|
||||||
* @return string The Response content as a string
|
|
||||||
*/
|
*/
|
||||||
public function render(FlattenException $exception): string;
|
public function render(FlattenException $exception): string;
|
||||||
}
|
}
|
||||||
|
@ -17,17 +17,17 @@ use Symfony\Component\DependencyInjection\ContainerBuilder;
|
|||||||
use Symfony\Component\DependencyInjection\Reference;
|
use Symfony\Component\DependencyInjection\Reference;
|
||||||
use Symfony\Component\DependencyInjection\ServiceLocator;
|
use Symfony\Component\DependencyInjection\ServiceLocator;
|
||||||
use Symfony\Component\ErrorCatcher\DependencyInjection\ErrorCatcherPass;
|
use Symfony\Component\ErrorCatcher\DependencyInjection\ErrorCatcherPass;
|
||||||
use Symfony\Component\ErrorCatcher\DependencyInjection\ErrorRenderer;
|
use Symfony\Component\ErrorCatcher\DependencyInjection\LazyLoadingErrorFormatter;
|
||||||
use Symfony\Component\ErrorCatcher\ErrorRenderer\HtmlErrorRenderer;
|
use Symfony\Component\ErrorCatcher\ErrorRenderer\HtmlErrorRenderer;
|
||||||
use Symfony\Component\ErrorCatcher\ErrorRenderer\JsonErrorRenderer;
|
use Symfony\Component\ErrorCatcher\ErrorRenderer\JsonErrorRenderer;
|
||||||
|
|
||||||
class ErrorPassTest extends TestCase
|
class ErrorCatcherPassTest extends TestCase
|
||||||
{
|
{
|
||||||
public function testProcess()
|
public function testProcess()
|
||||||
{
|
{
|
||||||
$container = new ContainerBuilder();
|
$container = new ContainerBuilder();
|
||||||
$container->setParameter('kernel.debug', true);
|
$container->setParameter('kernel.debug', true);
|
||||||
$definition = $container->register('error_catcher.error_renderer', ErrorRenderer::class)
|
$definition = $container->register('error_catcher.error_formatter', LazyLoadingErrorFormatter::class)
|
||||||
->addArgument([])
|
->addArgument([])
|
||||||
;
|
;
|
||||||
$container->register('error_catcher.renderer.html', HtmlErrorRenderer::class)
|
$container->register('error_catcher.renderer.html', HtmlErrorRenderer::class)
|
||||||
|
@ -12,11 +12,12 @@
|
|||||||
namespace Symfony\Component\ErrorCatcher\Tests\DependencyInjection;
|
namespace Symfony\Component\ErrorCatcher\Tests\DependencyInjection;
|
||||||
|
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use Symfony\Component\ErrorCatcher\DependencyInjection\ErrorRenderer;
|
use Psr\Container\ContainerInterface;
|
||||||
|
use Symfony\Component\ErrorCatcher\DependencyInjection\LazyLoadingErrorFormatter;
|
||||||
use Symfony\Component\ErrorCatcher\ErrorRenderer\ErrorRendererInterface;
|
use Symfony\Component\ErrorCatcher\ErrorRenderer\ErrorRendererInterface;
|
||||||
use Symfony\Component\ErrorCatcher\Exception\FlattenException;
|
use Symfony\Component\ErrorCatcher\Exception\FlattenException;
|
||||||
|
|
||||||
class ErrorRendererTest extends TestCase
|
class LazyLoadingErrorFormatterTest extends TestCase
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @expectedException \Symfony\Component\ErrorCatcher\Exception\ErrorRendererNotFoundException
|
* @expectedException \Symfony\Component\ErrorCatcher\Exception\ErrorRendererNotFoundException
|
||||||
@ -24,16 +25,16 @@ class ErrorRendererTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testInvalidErrorRenderer()
|
public function testInvalidErrorRenderer()
|
||||||
{
|
{
|
||||||
$container = $this->getMockBuilder('Psr\Container\ContainerInterface')->getMock();
|
$container = $this->getMockBuilder(ContainerInterface::class)->getMock();
|
||||||
$container->expects($this->once())->method('has')->with('foo')->willReturn(false);
|
$container->expects($this->once())->method('has')->with('foo')->willReturn(false);
|
||||||
|
|
||||||
$exception = FlattenException::createFromThrowable(new \Exception('Foo'));
|
$exception = FlattenException::createFromThrowable(new \Exception('Foo'));
|
||||||
(new ErrorRenderer($container))->render($exception, 'foo');
|
(new LazyLoadingErrorFormatter($container))->render($exception, 'foo');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testCustomErrorRenderer()
|
public function testCustomErrorRenderer()
|
||||||
{
|
{
|
||||||
$container = $this->getMockBuilder('Psr\Container\ContainerInterface')->getMock();
|
$container = $this->getMockBuilder(ContainerInterface::class)->getMock();
|
||||||
$container
|
$container
|
||||||
->expects($this->once())
|
->expects($this->once())
|
||||||
->method('has')
|
->method('has')
|
||||||
@ -46,7 +47,7 @@ class ErrorRendererTest extends TestCase
|
|||||||
->willReturn(new FooErrorRenderer())
|
->willReturn(new FooErrorRenderer())
|
||||||
;
|
;
|
||||||
|
|
||||||
$errorRenderer = new ErrorRenderer($container);
|
$errorRenderer = new LazyLoadingErrorFormatter($container);
|
||||||
|
|
||||||
$exception = FlattenException::createFromThrowable(new \RuntimeException('Foo'));
|
$exception = FlattenException::createFromThrowable(new \RuntimeException('Foo'));
|
||||||
$this->assertSame('Foo', $errorRenderer->render($exception, 'foo'));
|
$this->assertSame('Foo', $errorRenderer->render($exception, 'foo'));
|
@ -12,11 +12,11 @@
|
|||||||
namespace Symfony\Component\ErrorCatcher\Tests\ErrorRenderer;
|
namespace Symfony\Component\ErrorCatcher\Tests\ErrorRenderer;
|
||||||
|
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use Symfony\Component\ErrorCatcher\ErrorRenderer\ErrorRenderer;
|
use Symfony\Component\ErrorCatcher\ErrorRenderer\ErrorFormatter;
|
||||||
use Symfony\Component\ErrorCatcher\ErrorRenderer\ErrorRendererInterface;
|
use Symfony\Component\ErrorCatcher\ErrorRenderer\ErrorRendererInterface;
|
||||||
use Symfony\Component\ErrorCatcher\Exception\FlattenException;
|
use Symfony\Component\ErrorCatcher\Exception\FlattenException;
|
||||||
|
|
||||||
class ErrorRendererTest extends TestCase
|
class ErrorFormatterTest extends TestCase
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @expectedException \Symfony\Component\ErrorCatcher\Exception\ErrorRendererNotFoundException
|
* @expectedException \Symfony\Component\ErrorCatcher\Exception\ErrorRendererNotFoundException
|
||||||
@ -25,23 +25,21 @@ class ErrorRendererTest extends TestCase
|
|||||||
public function testErrorRendererNotFound()
|
public function testErrorRendererNotFound()
|
||||||
{
|
{
|
||||||
$exception = FlattenException::createFromThrowable(new \Exception('foo'));
|
$exception = FlattenException::createFromThrowable(new \Exception('foo'));
|
||||||
(new ErrorRenderer([]))->render($exception, 'foo');
|
(new ErrorFormatter([]))->render($exception, 'foo');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @expectedException \InvalidArgumentException
|
* @expectedException \TypeError
|
||||||
* @expectedExceptionMessage Error renderer "stdClass" must implement "Symfony\Component\ErrorCatcher\ErrorRenderer\ErrorRendererInterface".
|
|
||||||
*/
|
*/
|
||||||
public function testInvalidErrorRenderer()
|
public function testInvalidErrorRenderer()
|
||||||
{
|
{
|
||||||
$exception = FlattenException::createFromThrowable(new \Exception('foo'));
|
new ErrorFormatter([new \stdClass()]);
|
||||||
(new ErrorRenderer([new \stdClass()]))->render($exception, 'foo');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testCustomErrorRenderer()
|
public function testCustomErrorRenderer()
|
||||||
{
|
{
|
||||||
$renderers = [new FooErrorRenderer()];
|
$renderers = [new FooErrorRenderer()];
|
||||||
$errorRenderer = new ErrorRenderer($renderers);
|
$errorRenderer = new ErrorFormatter($renderers);
|
||||||
|
|
||||||
$exception = FlattenException::createFromThrowable(new \RuntimeException('Foo'));
|
$exception = FlattenException::createFromThrowable(new \RuntimeException('Foo'));
|
||||||
$this->assertSame('Foo', $errorRenderer->render($exception, 'foo'));
|
$this->assertSame('Foo', $errorRenderer->render($exception, 'foo'));
|
@ -16,7 +16,7 @@ use Symfony\Component\Console\ConsoleEvents;
|
|||||||
use Symfony\Component\Console\Event\ConsoleEvent;
|
use Symfony\Component\Console\Event\ConsoleEvent;
|
||||||
use Symfony\Component\Console\Output\ConsoleOutputInterface;
|
use Symfony\Component\Console\Output\ConsoleOutputInterface;
|
||||||
use Symfony\Component\ErrorCatcher\ErrorHandler;
|
use Symfony\Component\ErrorCatcher\ErrorHandler;
|
||||||
use Symfony\Component\ErrorCatcher\ErrorRenderer\ErrorRenderer;
|
use Symfony\Component\ErrorCatcher\ErrorRenderer\ErrorFormatter;
|
||||||
use Symfony\Component\ErrorCatcher\ErrorRenderer\HtmlErrorRenderer;
|
use Symfony\Component\ErrorCatcher\ErrorRenderer\HtmlErrorRenderer;
|
||||||
use Symfony\Component\ErrorCatcher\Exception\ErrorRendererNotFoundException;
|
use Symfony\Component\ErrorCatcher\Exception\ErrorRendererNotFoundException;
|
||||||
use Symfony\Component\ErrorCatcher\ExceptionHandler;
|
use Symfony\Component\ErrorCatcher\ExceptionHandler;
|
||||||
@ -46,7 +46,7 @@ class DebugHandlersListener implements EventSubscriberInterface
|
|||||||
private $fileLinkFormat;
|
private $fileLinkFormat;
|
||||||
private $scope;
|
private $scope;
|
||||||
private $charset;
|
private $charset;
|
||||||
private $errorRenderer;
|
private $errorFormatter;
|
||||||
private $firstCall = true;
|
private $firstCall = true;
|
||||||
private $hasTerminatedWithException;
|
private $hasTerminatedWithException;
|
||||||
|
|
||||||
@ -59,7 +59,7 @@ class DebugHandlersListener implements EventSubscriberInterface
|
|||||||
* @param string|FileLinkFormatter|null $fileLinkFormat The format for links to source files
|
* @param string|FileLinkFormatter|null $fileLinkFormat The format for links to source files
|
||||||
* @param bool $scope Enables/disables scoping mode
|
* @param bool $scope Enables/disables scoping mode
|
||||||
*/
|
*/
|
||||||
public function __construct(callable $exceptionHandler = null, LoggerInterface $logger = null, $levels = E_ALL, ?int $throwAt = E_ALL, bool $scream = true, $fileLinkFormat = null, bool $scope = true, string $charset = null, ErrorRenderer $errorRenderer = null)
|
public function __construct(callable $exceptionHandler = null, LoggerInterface $logger = null, $levels = E_ALL, ?int $throwAt = E_ALL, bool $scream = true, $fileLinkFormat = null, bool $scope = true, string $charset = null, ErrorFormatter $errorFormatter = null)
|
||||||
{
|
{
|
||||||
$this->exceptionHandler = $exceptionHandler;
|
$this->exceptionHandler = $exceptionHandler;
|
||||||
$this->logger = $logger;
|
$this->logger = $logger;
|
||||||
@ -69,7 +69,7 @@ class DebugHandlersListener implements EventSubscriberInterface
|
|||||||
$this->fileLinkFormat = $fileLinkFormat;
|
$this->fileLinkFormat = $fileLinkFormat;
|
||||||
$this->scope = $scope;
|
$this->scope = $scope;
|
||||||
$this->charset = $charset;
|
$this->charset = $charset;
|
||||||
$this->errorRenderer = $errorRenderer;
|
$this->errorFormatter = $errorFormatter;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -167,16 +167,16 @@ class DebugHandlersListener implements EventSubscriberInterface
|
|||||||
|
|
||||||
$debug = $this->scream && $this->scope;
|
$debug = $this->scream && $this->scope;
|
||||||
$controller = function (Request $request) use ($debug) {
|
$controller = function (Request $request) use ($debug) {
|
||||||
if (null === $this->errorRenderer) {
|
if (null === $this->errorFormatter) {
|
||||||
$this->errorRenderer = new ErrorRenderer([new HtmlErrorRenderer($debug, $this->charset, $this->fileLinkFormat)]);
|
$this->errorFormatter = new ErrorFormatter([new HtmlErrorRenderer($debug, $this->charset, $this->fileLinkFormat)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$e = $request->attributes->get('exception');
|
$e = $request->attributes->get('exception');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return new Response($this->errorRenderer->render($e, $request->getRequestFormat()), $e->getStatusCode(), $e->getHeaders());
|
return new Response($this->errorFormatter->render($e, $request->getRequestFormat()), $e->getStatusCode(), $e->getHeaders());
|
||||||
} catch (ErrorRendererNotFoundException $_) {
|
} catch (ErrorRendererNotFoundException $_) {
|
||||||
return new Response($this->errorRenderer->render($e), $e->getStatusCode(), $e->getHeaders());
|
return new Response($this->errorFormatter->render($e), $e->getStatusCode(), $e->getHeaders());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user