Render exception class PHPDoc description in HTML debug output#167
Render exception class PHPDoc description in HTML debug output#167dbuhonov wants to merge 1 commit intoyiisoft:masterfrom
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #167 +/- ##
============================================
+ Coverage 79.39% 85.49% +6.10%
- Complexity 196 208 +12
============================================
Files 19 19
Lines 626 662 +36
============================================
+ Hits 497 566 +69
+ Misses 129 96 -33 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
7cd35a2 to
fe553f4
Compare
fe553f4 to
8918890
Compare
There was a problem hiding this comment.
Pull request overview
Adds support for showing an exception class’ PHPDoc summary/details in the verbose HTML debug page, including basic rendering of inline {@see ...} / {@link ...} annotations, for exceptions that don’t implement FriendlyExceptionInterface.
Changes:
- Extract throwable class description from PHPDoc and pass it into the verbose template.
- Render the extracted description in
templates/development.phpusing the existing markdown pipeline. - Add PHPUnit coverage and supporting test exception classes.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
src/Renderer/HtmlRenderer.php |
Adds PHPDoc description extraction + inline tag normalization and passes result to the verbose template. |
templates/development.php |
Renders the new $exceptionDescription block in verbose HTML output. |
tests/Renderer/HtmlRendererTest.php |
Adds tests covering rendering behavior for docblock/no-docblock/friendly-exception cases. |
tests/Support/TestDocBlockException.php |
New fixture exception with PHPDoc containing inline tags. |
tests/Support/TestExceptionWithoutDocBlock.php |
New fixture exception without a PHPDoc. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| private function getThrowableDescription(Throwable $throwable): ?string | ||
| { | ||
| $docComment = (new ReflectionClass($throwable))->getDocComment(); | ||
| if ($docComment === false) { | ||
| return null; | ||
| } | ||
|
|
||
| $descriptionLines = []; | ||
| foreach (preg_split('/\R/', $docComment) ?: [] as $line) { | ||
| $line = trim($line); | ||
| $line = preg_replace('/^\/\*\*?/', '', $line) ?? $line; | ||
| $line = preg_replace('/\*\/$/', '', $line) ?? $line; | ||
| $line = preg_replace('/^\*/', '', $line) ?? $line; | ||
| $line = trim($line); | ||
|
|
||
| if ($line !== '' && str_starts_with($line, '@')) { | ||
| break; | ||
| } | ||
|
|
||
| $descriptionLines[] = $line; | ||
| } | ||
|
|
||
| $description = trim(implode("\n", $descriptionLines)); | ||
| if ($description === '') { | ||
| return null; | ||
| } | ||
|
|
||
| return preg_replace_callback( | ||
| '/\{@(see|link)\s+([^\s}]+)(?:\s+([^}]+))?\}/i', | ||
| static function (array $matches): string { | ||
| $target = $matches[2]; | ||
| $label = trim($matches[3] ?? ''); | ||
|
|
||
| if (preg_match('/^https?:\/\//i', $target) === 1) { | ||
| $text = $label !== '' ? $label : $target; | ||
| return '[' . $text . '](' . $target . ')'; | ||
| } | ||
|
|
||
| if ($label !== '') { | ||
| return $label . ' (`' . $target . '`)'; | ||
| } | ||
|
|
||
| return '`' . $target . '`'; | ||
| }, | ||
| $description, | ||
| ) ?? $description; | ||
| } |
There was a problem hiding this comment.
getThrowableDescription() returns raw PHPDoc text (with inline tag substitutions) that can contain arbitrary HTML/Markdown. Because parseMarkdown() does not sanitize attributes/URL schemes, a PHPDoc containing HTML like <img onerror=...> or markdown links to javascript: could result in XSS when rendered. Consider escaping/removing raw HTML in the extracted description and validating link/image URL schemes before handing it to the markdown renderer.
| </div> | ||
|
|
||
| <?php if ($exceptionDescription !== null): ?> | ||
| <div class="exception-description solution"><?= $this->parseMarkdown($exceptionDescription) ?></div> |
There was a problem hiding this comment.
$exceptionDescription ultimately gets rendered via parseMarkdown(), which allows raw HTML output from the Markdown parser and keeps tags like <a>/<img> without stripping unsafe attributes (e.g. onerror, onclick) or javascript: URLs. Since the description is sourced from third‑party exception class PHPDoc, it should be treated as untrusted and sanitized/escaped before rendering to avoid XSS in the debug page (e.g. escape HTML before parsing markdown and/or sanitize allowed tags/attributes + URL schemes).
| <div class="exception-description solution"><?= $this->parseMarkdown($exceptionDescription) ?></div> | |
| <div class="exception-description solution"><?= $this->parseMarkdown($this->htmlEncode($exceptionDescription)) ?></div> |
|
Overall, a very nice addition. Would be good to address the security part. I think that plain HTML should be either escaped or stripped before rendering but markdown part should be converted to HTML. |
Uh oh!
There was an error while loading. Please reload this page.