Skip to content

Commit

Permalink
use ErrorRenderer to be compatible with new Twig-less error rendering
Browse files Browse the repository at this point in the history
  • Loading branch information
VasekPurchart committed Nov 26, 2024
1 parent 74d6c70 commit b121a7a
Show file tree
Hide file tree
Showing 21 changed files with 480 additions and 263 deletions.
7 changes: 0 additions & 7 deletions build/cs-ruleset.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,4 @@
<rule ref="SlevomatCodingStandard.Classes.UnusedPrivateElements">
<exclude-pattern>*/data/*.php</exclude-pattern>
</rule>
<rule ref="SlevomatCodingStandard.Namespaces.ReferenceUsedNamesOnly">
<properties>
<property name="specialExceptionNames" type="array">
<element value="Symfony\Component\Debug\Exception\FatalThrowableError"/>
</property>
</properties>
</rule>
</ruleset>
5 changes: 3 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@
"symfony/config": "^4.0",
"symfony/console": "^4.0",
"symfony/dependency-injection": "^4.0",
"symfony/error-handler": "^4.4.1",
"symfony/http-foundation": "^4.0",
"symfony/http-kernel": "^4.4",
"symfony/twig-bundle": "^4.0",
"symfony/yaml": "^4.0",
"tracy/tracy": "^2.4"
},
Expand All @@ -29,7 +29,8 @@
"php-parallel-lint/php-console-highlighter": "1.0",
"php-parallel-lint/php-parallel-lint": "1.3.2",
"phing/phing": "2.17.2",
"phpunit/phpunit": "8.5.25"
"phpunit/phpunit": "8.5.25",
"symfony/framework-bundle": "^4.4"
},
"autoload": {
"psr-4": {
Expand Down
8 changes: 2 additions & 6 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,9 @@ Usage

If you do not have any custom `kernel.exception` listeners this works out of the box. However if you have any, you have to ensure that they do not return any response, because that prevents the profiler from showing up (the same applies for the default Symfony exception screen).

If you need to change the default position of this listener (see order in `bin/console debug:event-dispatcher`), use the configuration option `listener_priority`.
This bundle expects that you are using the default Symfony `ErrorController`. You could use it with different one, you just need to pass `vasek_purchart.tracy_blue_screen.blue_screen.error_renderer` to it.

This bundle expects that you are using the default Symfony profiler screen rendered via the [TwigBundle](http://symfony.com/doc/current/reference/configuration/twig.html), which must be registered.

Console integration also works out of the box, if you do not have an `console.error` listener that would prevent execution of this one. Again, this can be tweaked using the respective `listener_priority` option.
Console integration also works out of the box, if you do not have an `console.error` listener that would prevent execution of this one. If you need to change the default position of this listener (see order in `bin/console debug:event-dispatcher`), use the configuration option `listener_priority`.

Configure the `browser` option to open the exceptions directly in your browser, configured binary must be executable with [`exec()`](http://php.net/manual/en/function.exec.php).

Expand All @@ -55,8 +53,6 @@ tracy_blue_screen:
# Enable debug screen for controllers.
# Enabled by default only in dev environment with debug mode on.
enabled: ~
# Priority with which the listener will be registered.
listener_priority: 0

console:
# Enable debug screen for console.
Expand Down
66 changes: 66 additions & 0 deletions src/BlueScreen/BlueScreenErrorRenderer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?php

declare(strict_types = 1);

namespace VasekPurchart\TracyBlueScreenBundle\BlueScreen;

use Symfony\Component\ErrorHandler\ErrorRenderer\ErrorRendererInterface;
use Tracy\BlueScreen;

class BlueScreenErrorRenderer implements \Symfony\Component\ErrorHandler\ErrorRenderer\ErrorRendererInterface
{

/** @var \Tracy\BlueScreen */
private $blueScreen;

/** @var bool|\Closure */
private $debug;

/** @var \Symfony\Component\ErrorHandler\ErrorRenderer\ErrorRendererInterface */
private $fallbackErrorRenderer;

/**
* @param \Tracy\BlueScreen $blueScreen
* @param bool|\Closure $debug
* @param \Symfony\Component\ErrorHandler\ErrorRenderer\ErrorRendererInterface $fallbackErrorRenderer
*/
public function __construct(
BlueScreen $blueScreen,
$debug,
ErrorRendererInterface $fallbackErrorRenderer
)
{
$this->blueScreen = $blueScreen;
$this->debug = $debug;
$this->fallbackErrorRenderer = $fallbackErrorRenderer;
}

public function render(\Throwable $exception): \Symfony\Component\ErrorHandler\Exception\FlattenException
{
if (!$this->isDebug()) {
return $this->fallbackErrorRenderer->render($exception);
}

return \Symfony\Component\ErrorHandler\Exception\FlattenException::createFromThrowable($exception)
->setAsString($this->getBlueScreenAsString($exception));
}

private function getBlueScreenAsString(\Throwable $exception): string
{
ob_start();
ob_start(); // double buffer prevents sending HTTP headers in some PHP

$this->blueScreen->render($exception);

$result = ob_get_clean();
ob_end_clean();

return $result;
}

private function isDebug(): bool
{
return is_bool($this->debug) ? $this->debug : ($this->debug)();
}

}
51 changes: 0 additions & 51 deletions src/BlueScreen/ControllerBlueScreenExceptionListener.php

This file was deleted.

2 changes: 0 additions & 2 deletions src/DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ class Configuration implements \Symfony\Component\Config\Definition\Configuratio
public const PARAMETER_CONSOLE_LISTENER_PRIORITY = 'listener_priority';
public const PARAMETER_CONSOLE_LOG_DIRECTORY = 'log_directory';
public const PARAMETER_CONTROLLER_ENABLED = 'enabled';
public const PARAMETER_CONTROLLER_LISTENER_PRIORITY = 'listener_priority';

public const SECTION_BLUE_SCREEN = 'blue_screen';
public const SECTION_CONSOLE = 'console';
Expand Down Expand Up @@ -67,7 +66,6 @@ private function createControllerNode(string $nodeName): ArrayNodeDefinition
$node = new ArrayNodeDefinition($nodeName);
$node->addDefaultsIfNotSet();
$node->children()->append($this->createDebugNode(self::PARAMETER_CONTROLLER_ENABLED, 'Enable debug screen for controllers.'));
$node->children()->append($this->createPriorityNode(self::PARAMETER_CONTROLLER_LISTENER_PRIORITY));

return $node;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

declare(strict_types = 1);

namespace VasekPurchart\TracyBlueScreenBundle\DependencyInjection;

use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\HttpKernel\Controller\ErrorController;

class ReplaceErrorControllerErrorRendererCompilerPass implements \Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface
{

public function process(ContainerBuilder $container): void
{
if (
!$container->hasParameter('vasek_purchart.tracy_blue_screen.controller.enabled')
|| !$container->getParameter('vasek_purchart.tracy_blue_screen.controller.enabled')
) {
return;
}

$errorControllerDefinition = $container->findDefinition('error_controller');
if ($errorControllerDefinition->getClass() !== ErrorController::class) {
throw new \VasekPurchart\TracyBlueScreenBundle\DependencyInjection\CannotReplaceErrorRendererForNonDefaultErrorControllerException($errorControllerDefinition->getClass());
}

$errorControllerDefinition->setArgument(
'$errorRenderer',
new Reference('vasek_purchart.tracy_blue_screen.blue_screen.error_renderer')
);
}

}
48 changes: 9 additions & 39 deletions src/DependencyInjection/TracyBlueScreenExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,47 +4,23 @@

namespace VasekPurchart\TracyBlueScreenBundle\DependencyInjection;

use ReflectionClass;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
use VasekPurchart\TracyBlueScreenBundle\TracyBlueScreenBundle;

class TracyBlueScreenExtension
extends \Symfony\Component\HttpKernel\DependencyInjection\ConfigurableExtension
implements \Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface
{

private const TWIG_BUNDLE_ALIAS = 'twig';
private const TWIG_TEMPLATES_NAMESPACE = 'Twig';

public function prepend(ContainerBuilder $container): void
{
if (!$container->hasExtension(self::TWIG_BUNDLE_ALIAS)) {
throw new \VasekPurchart\TracyBlueScreenBundle\DependencyInjection\TwigBundleRequiredException();
}

$container->loadFromExtension(self::TWIG_BUNDLE_ALIAS, [
'paths' => [
$this->getTemplatesDirectory() => self::TWIG_TEMPLATES_NAMESPACE,
],
]);
}

private function getTemplatesDirectory(): string
{
$bundleClassReflection = new ReflectionClass(TracyBlueScreenBundle::class);
$srcDirectoryPath = dirname($bundleClassReflection->getFileName());

return $srcDirectoryPath . '/Resources/views';
}

/**
* @param mixed[] $mergedConfig
* @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
*/
public function loadInternal(array $mergedConfig, ContainerBuilder $container): void
{
$environment = $container->getParameter('kernel.environment');
$debug = $container->getParameter('kernel.debug');

$container->setParameter(
'vasek_purchart.tracy_blue_screen.blue_screen.collapse_paths',
$mergedConfig[Configuration::SECTION_BLUE_SCREEN][Configuration::PARAMETER_COLLAPSE_PATHS]
Expand All @@ -62,30 +38,24 @@ public function loadInternal(array $mergedConfig, ContainerBuilder $container):
$mergedConfig[Configuration::SECTION_CONSOLE][Configuration::PARAMETER_CONSOLE_LOG_DIRECTORY]
);
$container->setParameter(
'vasek_purchart.tracy_blue_screen.controller.listener_priority',
$mergedConfig[Configuration::SECTION_CONTROLLER][Configuration::PARAMETER_CONTROLLER_LISTENER_PRIORITY]
'vasek_purchart.tracy_blue_screen.controller.enabled',
$this->isEnabled(
$mergedConfig[Configuration::SECTION_CONTROLLER][Configuration::PARAMETER_CONTROLLER_ENABLED],
$environment,
$debug
)
);

$loader = new YamlFileLoader($container, new FileLocator(__DIR__ . '/config'));
$loader->load('services.yaml');

$environment = $container->getParameter('kernel.environment');
$debug = $container->getParameter('kernel.debug');

if ($this->isEnabled(
$mergedConfig[Configuration::SECTION_CONSOLE][Configuration::PARAMETER_CONSOLE_ENABLED],
$environment,
$debug
)) {
$loader->load('console_listener.yaml');
}
if ($this->isEnabled(
$mergedConfig[Configuration::SECTION_CONTROLLER][Configuration::PARAMETER_CONTROLLER_ENABLED],
$environment,
$debug
)) {
$loader->load('controller_listener.yaml');
}
}

/**
Expand Down
10 changes: 0 additions & 10 deletions src/DependencyInjection/config/controller_listener.yaml

This file was deleted.

16 changes: 16 additions & 0 deletions src/DependencyInjection/config/services.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,20 @@
services:
vasek_purchart.tracy_blue_screen.blue_screen.error_renderer: '@vasek_purchart.tracy_blue_screen.blue_screen.error_renderer.default'

vasek_purchart.tracy_blue_screen.blue_screen.error_renderer.default:
class: 'VasekPurchart\TracyBlueScreenBundle\BlueScreen\BlueScreenErrorRenderer'
arguments:
$blueScreen: '@vasek_purchart.tracy_blue_screen.tracy.blue_screen'
$debug: '@vasek_purchart.tracy_blue_screen.blue_screen.error_renderer.is_debug'
$fallbackErrorRenderer: '@error_renderer'

vasek_purchart.tracy_blue_screen.blue_screen.error_renderer.is_debug:
class: 'Closure'
factory: 'Symfony\Component\ErrorHandler\ErrorRenderer\HtmlErrorRenderer::isDebug'
arguments:
$requestStack: '@request_stack'
$debug: '%kernel.debug%'

vasek_purchart.tracy_blue_screen.tracy.blue_screen: '@vasek_purchart.tracy_blue_screen.tracy.blue_screen.default'

vasek_purchart.tracy_blue_screen.tracy.blue_screen.default:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

declare(strict_types = 1);

namespace VasekPurchart\TracyBlueScreenBundle\DependencyInjection;

use Symfony\Component\HttpKernel\Controller\ErrorController;

class CannotReplaceErrorRendererForNonDefaultErrorControllerException extends \Exception
{

/** @var string */
private $customErrorControllerClass;

public function __construct(
string $errorControllerClass,
?\Throwable $previous = null
)
{
parent::__construct(sprintf(
'Automatic error renderer replacement is available only for default error controller (%s), currently %s is used.' . PHP_EOL
. 'You can:' . PHP_EOL
. '1) Use the default error controller (%s)' . PHP_EOL
. '2) Use the provided BlueScreen error renderer in your current error controller (%s). Just pass the prepared `vasek_purchart.tracy_blue_screen.blue_screen.error_renderer` DI service.' . PHP_EOL
. '3) If you do not want to use BlueScreen for requests, disable `controller` in this bundles configuration (you can still use the `console` part independently)',
ErrorController::class,
$errorControllerClass,
ErrorController::class,
$errorControllerClass
), 0, $previous);

$this->customErrorControllerClass = $errorControllerClass;
}

public function getCustomErrorControllerClass(): string
{
return $this->customErrorControllerClass;
}

}
Loading

0 comments on commit b121a7a

Please sign in to comment.