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:
Fabien Potencier 2019-06-28 07:52:45 +02:00
commit 7f6ed32a1d
10 changed files with 47 additions and 47 deletions

View File

@ -22,7 +22,7 @@
<argument type="service" id="debug.file_link_formatter" />
<argument>%kernel.debug%</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 id="debug.file_link_formatter" class="Symfony\Component\HttpKernel\Debug\FileLinkFormatter">

View File

@ -5,7 +5,7 @@
xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd">
<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 -->
</service>

View File

@ -25,7 +25,7 @@ class ErrorCatcherPass implements CompilerPassInterface
private $rendererService;
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->rendererTag = $rendererTag;

View File

@ -12,14 +12,14 @@
namespace Symfony\Component\ErrorCatcher\DependencyInjection;
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.
*
* @author Yonel Ceruto <yonelceruto@gmail.com>
*/
class ErrorRenderer extends BaseErrorRenderer
class LazyLoadingErrorFormatter extends ErrorFormatter
{
private $container;
private $initialized = [];

View File

@ -15,33 +15,38 @@ use Symfony\Component\ErrorCatcher\Exception\ErrorRendererNotFoundException;
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
*
* @author Yonel Ceruto <yonelceruto@gmail.com>
*/
class ErrorRenderer
class ErrorFormatter
{
private $renderers = [];
/**
* @param ErrorRendererInterface[] $renderers
*/
public function __construct(array $renderers)
public function __construct(iterable $renderers)
{
foreach ($renderers as $renderer) {
if (!$renderer instanceof ErrorRendererInterface) {
throw new \InvalidArgumentException(sprintf('Error renderer "%s" must implement "%s".', \get_class($renderer), ErrorRendererInterface::class));
}
$this->addRenderer($renderer, $renderer::getFormat());
$this->addRenderer($renderer);
}
}
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;
}

View File

@ -14,23 +14,19 @@ namespace Symfony\Component\ErrorCatcher\ErrorRenderer;
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>
*/
interface ErrorRendererInterface
{
/**
* Gets the format of the content.
*
* @return string The content format
* Gets the format this renderer can return errors as.
*/
public static function getFormat(): string;
/**
* Renders an Exception and returns the Response content.
*
* @return string The Response content as a string
* Returns the response content of the rendered exception.
*/
public function render(FlattenException $exception): string;
}

View File

@ -17,17 +17,17 @@ use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ServiceLocator;
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\JsonErrorRenderer;
class ErrorPassTest extends TestCase
class ErrorCatcherPassTest extends TestCase
{
public function testProcess()
{
$container = new ContainerBuilder();
$container->setParameter('kernel.debug', true);
$definition = $container->register('error_catcher.error_renderer', ErrorRenderer::class)
$definition = $container->register('error_catcher.error_formatter', LazyLoadingErrorFormatter::class)
->addArgument([])
;
$container->register('error_catcher.renderer.html', HtmlErrorRenderer::class)

View File

@ -12,11 +12,12 @@
namespace Symfony\Component\ErrorCatcher\Tests\DependencyInjection;
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\Exception\FlattenException;
class ErrorRendererTest extends TestCase
class LazyLoadingErrorFormatterTest extends TestCase
{
/**
* @expectedException \Symfony\Component\ErrorCatcher\Exception\ErrorRendererNotFoundException
@ -24,16 +25,16 @@ class ErrorRendererTest extends TestCase
*/
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);
$exception = FlattenException::createFromThrowable(new \Exception('Foo'));
(new ErrorRenderer($container))->render($exception, 'foo');
(new LazyLoadingErrorFormatter($container))->render($exception, 'foo');
}
public function testCustomErrorRenderer()
{
$container = $this->getMockBuilder('Psr\Container\ContainerInterface')->getMock();
$container = $this->getMockBuilder(ContainerInterface::class)->getMock();
$container
->expects($this->once())
->method('has')
@ -46,7 +47,7 @@ class ErrorRendererTest extends TestCase
->willReturn(new FooErrorRenderer())
;
$errorRenderer = new ErrorRenderer($container);
$errorRenderer = new LazyLoadingErrorFormatter($container);
$exception = FlattenException::createFromThrowable(new \RuntimeException('Foo'));
$this->assertSame('Foo', $errorRenderer->render($exception, 'foo'));

View File

@ -12,11 +12,11 @@
namespace Symfony\Component\ErrorCatcher\Tests\ErrorRenderer;
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\Exception\FlattenException;
class ErrorRendererTest extends TestCase
class ErrorFormatterTest extends TestCase
{
/**
* @expectedException \Symfony\Component\ErrorCatcher\Exception\ErrorRendererNotFoundException
@ -25,23 +25,21 @@ class ErrorRendererTest extends TestCase
public function testErrorRendererNotFound()
{
$exception = FlattenException::createFromThrowable(new \Exception('foo'));
(new ErrorRenderer([]))->render($exception, 'foo');
(new ErrorFormatter([]))->render($exception, 'foo');
}
/**
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage Error renderer "stdClass" must implement "Symfony\Component\ErrorCatcher\ErrorRenderer\ErrorRendererInterface".
* @expectedException \TypeError
*/
public function testInvalidErrorRenderer()
{
$exception = FlattenException::createFromThrowable(new \Exception('foo'));
(new ErrorRenderer([new \stdClass()]))->render($exception, 'foo');
new ErrorFormatter([new \stdClass()]);
}
public function testCustomErrorRenderer()
{
$renderers = [new FooErrorRenderer()];
$errorRenderer = new ErrorRenderer($renderers);
$errorRenderer = new ErrorFormatter($renderers);
$exception = FlattenException::createFromThrowable(new \RuntimeException('Foo'));
$this->assertSame('Foo', $errorRenderer->render($exception, 'foo'));

View File

@ -16,7 +16,7 @@ use Symfony\Component\Console\ConsoleEvents;
use Symfony\Component\Console\Event\ConsoleEvent;
use Symfony\Component\Console\Output\ConsoleOutputInterface;
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\Exception\ErrorRendererNotFoundException;
use Symfony\Component\ErrorCatcher\ExceptionHandler;
@ -46,7 +46,7 @@ class DebugHandlersListener implements EventSubscriberInterface
private $fileLinkFormat;
private $scope;
private $charset;
private $errorRenderer;
private $errorFormatter;
private $firstCall = true;
private $hasTerminatedWithException;
@ -59,7 +59,7 @@ class DebugHandlersListener implements EventSubscriberInterface
* @param string|FileLinkFormatter|null $fileLinkFormat The format for links to source files
* @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->logger = $logger;
@ -69,7 +69,7 @@ class DebugHandlersListener implements EventSubscriberInterface
$this->fileLinkFormat = $fileLinkFormat;
$this->scope = $scope;
$this->charset = $charset;
$this->errorRenderer = $errorRenderer;
$this->errorFormatter = $errorFormatter;
}
/**
@ -167,16 +167,16 @@ class DebugHandlersListener implements EventSubscriberInterface
$debug = $this->scream && $this->scope;
$controller = function (Request $request) use ($debug) {
if (null === $this->errorRenderer) {
$this->errorRenderer = new ErrorRenderer([new HtmlErrorRenderer($debug, $this->charset, $this->fileLinkFormat)]);
if (null === $this->errorFormatter) {
$this->errorFormatter = new ErrorFormatter([new HtmlErrorRenderer($debug, $this->charset, $this->fileLinkFormat)]);
}
$e = $request->attributes->get('exception');
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 $_) {
return new Response($this->errorRenderer->render($e), $e->getStatusCode(), $e->getHeaders());
return new Response($this->errorFormatter->render($e), $e->getStatusCode(), $e->getHeaders());
}
};