diff --git a/CHANGELOG.md b/CHANGELOG.md index 5efb763..66d8b5b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## 5.0.0 under development +- Enh #104: Render exception class PHPDoc description with safe markdown links in HTML debug output (@dbuhonov) - Chg #162: Replace deprecated `ThrowableResponseFactory` class usage to new one, and remove it (@vjik) - Enh #163: Explicitly import classes, functions, and constants in "use" section (@mspirkov) - Bug #164: Fix missing items in stack trace HTML output when handling a PHP error (@vjik) diff --git a/src/Renderer/HtmlRenderer.php b/src/Renderer/HtmlRenderer.php index a3c8eb6..1d2d531 100644 --- a/src/Renderer/HtmlRenderer.php +++ b/src/Renderer/HtmlRenderer.php @@ -16,6 +16,7 @@ use Yiisoft\ErrorHandler\ThrowableRendererInterface; use Yiisoft\FriendlyException\FriendlyExceptionInterface; use Yiisoft\Http\Header; +use ReflectionClass; use ReflectionException; use ReflectionFunction; use ReflectionMethod; @@ -45,15 +46,22 @@ use function ob_implicit_flush; use function ob_start; use function realpath; +use function preg_match; +use function preg_replace; +use function preg_replace_callback; +use function preg_split; use function str_replace; +use function str_starts_with; use function stripos; use function strlen; use function count; use function function_exists; +use function trim; use const DIRECTORY_SEPARATOR; use const ENT_QUOTES; use const EXTR_OVERWRITE; +use const PREG_SPLIT_DELIM_CAPTURE; /** * Formats throwable into HTML string. @@ -204,10 +212,29 @@ public function render(Throwable $t, ?ServerRequestInterface $request = null): E public function renderVerbose(Throwable $t, ?ServerRequestInterface $request = null): ErrorData { + $solution = null; + $exceptionDescription = null; + $displayThrowable = $t; + + if ($t instanceof CompositeException) { + $displayThrowable = $t->getFirstException(); + } + + if ($displayThrowable instanceof FriendlyExceptionInterface) { + $solution = $displayThrowable->getSolution(); + } else { + $exceptionDescription = $this->getThrowableDescription($displayThrowable); + } + return new ErrorData( $this->renderTemplate($this->verboseTemplate, [ 'request' => $request, 'throwable' => $t, + 'displayThrowable' => $displayThrowable, + 'solution' => $solution, + 'exceptionClass' => $displayThrowable::class, + 'exceptionMessage' => $displayThrowable->getMessage(), + 'exceptionDescription' => $exceptionDescription, ]), [Header::CONTENT_TYPE => self::CONTENT_TYPE], ); @@ -541,6 +568,111 @@ public function removeAnonymous(string $value): string return $anonymousPosition !== false ? substr($value, 0, $anonymousPosition) : $value; } + /** + * Extracts a user-facing description from throwable class PHPDoc. + * + * Takes only descriptive text before block tags, normalizes unsafe markup + * into safe markdown/plain text and converts it into an HTML fragment + * suitable for direct inclusion in the error template. + * Inline {@see ...}/{@link ...} annotations are rendered as markdown links. + * + * The returned value is an HTML snippet (for example, containing