From b041d064920a479ecbd950f619a32718b4c59ada Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Mon, 6 Mar 2023 18:35:02 +0100 Subject: [PATCH] [ErrorHander] Display exception properties in the HTML error page --- .../Component/ErrorHandler/CHANGELOG.md | 5 +++++ .../ErrorRenderer/HtmlErrorRenderer.php | 19 +++++++++++++++++++ .../Exception/FlattenException.php | 14 ++++++++++++++ .../Resources/assets/css/exception.css | 6 +++++- .../Resources/views/exception.html.php | 1 - .../Resources/views/traces.html.php | 8 ++++++++ 6 files changed, 51 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/ErrorHandler/CHANGELOG.md b/src/Symfony/Component/ErrorHandler/CHANGELOG.md index 9d2ee803f9bf6..d753991b3a2f9 100644 --- a/src/Symfony/Component/ErrorHandler/CHANGELOG.md +++ b/src/Symfony/Component/ErrorHandler/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +6.3 +--- + + * Display exception properties in the HTML error page + 6.1 --- diff --git a/src/Symfony/Component/ErrorHandler/ErrorRenderer/HtmlErrorRenderer.php b/src/Symfony/Component/ErrorHandler/ErrorRenderer/HtmlErrorRenderer.php index 8472f0cc20377..95ed8504efcd9 100644 --- a/src/Symfony/Component/ErrorHandler/ErrorRenderer/HtmlErrorRenderer.php +++ b/src/Symfony/Component/ErrorHandler/ErrorRenderer/HtmlErrorRenderer.php @@ -17,6 +17,8 @@ use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Debug\FileLinkFormatter; use Symfony\Component\HttpKernel\Log\DebugLoggerInterface; +use Symfony\Component\VarDumper\Cloner\VarCloner; +use Symfony\Component\VarDumper\Dumper\HtmlDumper; /** * @author Yonel Ceruto @@ -147,6 +149,23 @@ private function renderException(FlattenException $exception, string $debugTempl ]); } + private function dumpValue(mixed $value): string + { + $cloner = new VarCloner(); + $data = $cloner->cloneVar($value); + + $dumper = new HtmlDumper(); + $dumper->setTheme('light'); + $dumper->setOutput($output = fopen('php://memory', 'r+')); + $dumper->dump($data); + + $dump = stream_get_contents($output, -1, 0); + rewind($output); + ftruncate($output, 0); + + return $dump; + } + private function formatArgs(array $args): string { $result = []; diff --git a/src/Symfony/Component/ErrorHandler/Exception/FlattenException.php b/src/Symfony/Component/ErrorHandler/Exception/FlattenException.php index 461c26812c047..8d1c6575b7d04 100644 --- a/src/Symfony/Component/ErrorHandler/Exception/FlattenException.php +++ b/src/Symfony/Component/ErrorHandler/Exception/FlattenException.php @@ -36,6 +36,7 @@ class FlattenException private string $file; private int $line; private ?string $asString = null; + private array $properties = []; public static function create(\Exception $exception, int $statusCode = null, array $headers = []): static { @@ -77,6 +78,13 @@ public static function createFromThrowable(\Throwable $exception, int $statusCod $e->setPrevious(static::createFromThrowable($previous)); } + if ((new \ReflectionClass($exception::class))->isUserDefined()) { + $getProperties = \Closure::bind(fn (\Throwable $e) => get_object_vars($e), null, $exception::class); + $properties = $getProperties($exception); + unset($properties['message'], $properties['code'], $properties['file'], $properties['line']); + $e->properties = $properties; + } + return $e; } @@ -88,6 +96,7 @@ public function toArray(): array 'message' => $exception->getMessage(), 'class' => $exception->getClass(), 'trace' => $exception->getTrace(), + 'properties' => $exception->getProperties(), ]; } @@ -221,6 +230,11 @@ public function setCode(int|string $code): static return $this; } + public function getProperties(): array + { + return $this->properties; + } + public function getPrevious(): ?self { return $this->previous; diff --git a/src/Symfony/Component/ErrorHandler/Resources/assets/css/exception.css b/src/Symfony/Component/ErrorHandler/Resources/assets/css/exception.css index 7cb3206da2055..bcdb8e1482553 100644 --- a/src/Symfony/Component/ErrorHandler/Resources/assets/css/exception.css +++ b/src/Symfony/Component/ErrorHandler/Resources/assets/css/exception.css @@ -83,7 +83,7 @@ --card-label-color: var(--tab-active-color); } -html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0} +html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}summary{cursor: pointer} html { /* always display the vertical scrollbar to avoid jumps when toggling contents */ @@ -208,6 +208,10 @@ header .container { display: flex; justify-content: space-between; } .exception-message a { border-bottom: 1px solid rgba(255, 255, 255, 0.5); font-size: inherit; text-decoration: none; } .exception-message a:hover { border-bottom-color: #ffffff; } +.exception-properties-wrapper { margin: .8em 0; } +.exception-properties { background: var(--base-0); border: var(--border); box-shadow: 0px 0px 1px rgba(128, 128, 128, .2); } +.exception-properties pre { margin: 0; padding: 0.2em 0; } + .exception-illustration { flex-basis: 111px; flex-shrink: 0; height: 66px; margin-left: 15px; opacity: .7; } .trace + .trace { margin-top: 30px; } diff --git a/src/Symfony/Component/ErrorHandler/Resources/views/exception.html.php b/src/Symfony/Component/ErrorHandler/Resources/views/exception.html.php index 8468fa76f4b32..31554a468d163 100644 --- a/src/Symfony/Component/ErrorHandler/Resources/views/exception.html.php +++ b/src/Symfony/Component/ErrorHandler/Resources/views/exception.html.php @@ -13,7 +13,6 @@ -

formatFileFromText(nl2br($exceptionMessage)); ?>

diff --git a/src/Symfony/Component/ErrorHandler/Resources/views/traces.html.php b/src/Symfony/Component/ErrorHandler/Resources/views/traces.html.php index 38752bc1a892f..fdca8a70e72e2 100644 --- a/src/Symfony/Component/ErrorHandler/Resources/views/traces.html.php +++ b/src/Symfony/Component/ErrorHandler/Resources/views/traces.html.php @@ -25,6 +25,14 @@

escape($exception['message']); ?>

+ +
+ Show exception properties +
+ dumpValue($exception['properties']) ?> +
+
+