Skip to content

Commit c08cf44

Browse files
committed
Fix HtmlRenderer to handle FriendlyException attribute safely when class is not available
1 parent 44f49fa commit c08cf44

File tree

4 files changed

+121
-8
lines changed

4 files changed

+121
-8
lines changed

src/Renderer/HtmlRenderer.php

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@
1414
use Yiisoft\ErrorHandler\Exception\ErrorException;
1515
use Yiisoft\ErrorHandler\ThrowableRendererInterface;
1616
use Yiisoft\FriendlyException\FriendlyExceptionInterface;
17+
use Yiisoft\FriendlyException\Attribute\FriendlyException;
1718
use Yiisoft\Http\Header;
19+
use ReflectionClass;
1820

1921
use function array_values;
2022
use function dirname;
@@ -491,16 +493,33 @@ public function createServerInformationLink(ServerRequestInterface $request): st
491493
}
492494

493495
/**
494-
* Returns the name of the throwable instance.
496+
* Returns string representation of the throwable name.
495497
*
496-
* @return string The name of the throwable instance.
498+
* @param Throwable $throwable The throwable.
499+
*
500+
* @return string The throwable name.
497501
*/
498502
public function getThrowableName(Throwable $throwable): string
499503
{
500504
$name = $throwable::class;
501505

502506
if ($throwable instanceof FriendlyExceptionInterface) {
503507
$name = $throwable->getName() . ' (' . $name . ')';
508+
} else {
509+
// Check if the exception class has FriendlyException attribute
510+
try {
511+
$reflectionClass = new ReflectionClass($throwable);
512+
if (class_exists('Yiisoft\FriendlyException\Attribute\FriendlyException')) {
513+
$attributes = $reflectionClass->getAttributes('Yiisoft\FriendlyException\Attribute\FriendlyException');
514+
515+
if (!empty($attributes)) {
516+
$friendlyExceptionAttribute = $attributes[0]->newInstance();
517+
$name = $friendlyExceptionAttribute->name . ' (' . $name . ')';
518+
}
519+
}
520+
} catch (\Throwable $e) {
521+
// Ignore exception and keep default name
522+
}
504523
}
505524

506525
return $name;

templates/development.php

Lines changed: 58 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
<?php
22

3+
declare(strict_types=1);
4+
35
use Psr\Http\Message\ServerRequestInterface;
6+
use ReflectionClass;
7+
use Throwable;
48
use Yiisoft\ErrorHandler\CompositeException;
59
use Yiisoft\ErrorHandler\Exception\ErrorException;
10+
use Yiisoft\ErrorHandler\HeadersProvider;
611
use Yiisoft\ErrorHandler\Renderer\HtmlRenderer;
712
use Yiisoft\FriendlyException\FriendlyExceptionInterface;
813

@@ -20,6 +25,22 @@
2025
}
2126
$isFriendlyException = $throwable instanceof FriendlyExceptionInterface;
2227
$solution = $isFriendlyException ? $throwable->getSolution() : null;
28+
29+
// Check if the exception class has FriendlyException attribute
30+
if ($solution === null && class_exists('Yiisoft\FriendlyException\Attribute\FriendlyException')) {
31+
try {
32+
$reflectionClass = new ReflectionClass($throwable);
33+
$attributes = $reflectionClass->getAttributes('Yiisoft\FriendlyException\Attribute\FriendlyException');
34+
35+
if (!empty($attributes)) {
36+
$friendlyExceptionAttribute = $attributes[0]->newInstance();
37+
$solution = $friendlyExceptionAttribute->solution;
38+
}
39+
} catch (\Throwable $e) {
40+
// Ignore exception
41+
}
42+
}
43+
2344
$exceptionClass = get_class($throwable);
2445
$exceptionMessage = $throwable->getMessage();
2546

@@ -78,13 +99,44 @@
7899
<div class="exception-card">
79100
<div class="exception-class">
80101
<?php
81-
if ($isFriendlyException): ?>
102+
$hasFriendlyName = false;
103+
if ($isFriendlyException) {
104+
$hasFriendlyName = true;
105+
?>
82106
<span><?= $this->htmlEncode($throwable->getName())?></span>
83107
&mdash;
84108
<?= $exceptionClass ?>
85-
<?php else: ?>
86-
<span><?= $exceptionClass ?></span>
87-
<?php endif ?>
109+
<?php
110+
} else {
111+
// Check if the exception class has FriendlyException attribute
112+
$hasFriendlyNameFromAttribute = false;
113+
114+
if (class_exists('Yiisoft\FriendlyException\Attribute\FriendlyException')) {
115+
try {
116+
$reflectionClass = new ReflectionClass($throwable);
117+
$attributes = $reflectionClass->getAttributes('Yiisoft\FriendlyException\Attribute\FriendlyException');
118+
119+
if (!empty($attributes)) {
120+
$friendlyExceptionAttribute = $attributes[0]->newInstance();
121+
$hasFriendlyNameFromAttribute = true;
122+
?>
123+
<span><?= $this->htmlEncode($friendlyExceptionAttribute->name) ?></span>
124+
&mdash;
125+
<?= $exceptionClass ?>
126+
<?php
127+
}
128+
} catch (\Throwable $e) {
129+
// Ignore exception
130+
}
131+
}
132+
133+
if (!$hasFriendlyName && !$hasFriendlyNameFromAttribute) {
134+
?>
135+
<span><?= $exceptionClass ?></span>
136+
<?php
137+
}
138+
}
139+
?>
88140
(Code #<?= $throwable->getCode() ?>)
89141
</div>
90142

@@ -98,12 +150,12 @@
98150

99151
<?= $this->renderPreviousExceptions($originalException) ?>
100152

101-
<textarea id="clipboard"><?= $this->htmlEncode($throwable) ?></textarea>
153+
<textarea id="clipboard"><?= $this->htmlEncode((string)$throwable) ?></textarea>
102154
<span id="copied">Copied!</span>
103155

104156
<a href="#"
105157
class="copy-clipboard"
106-
data-clipboard="<?= $this->htmlEncode($throwable) ?>"
158+
data-clipboard="<?= $this->htmlEncode((string)$throwable) ?>"
107159
title="Copy the stacktrace for use in a bug report or pastebin"
108160
>
109161
<svg width="26" height="30" fill="none" xmlns="http://www.w3.org/2000/svg">
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Yiisoft\ErrorHandler\Tests\Renderer\Attribute;
6+
7+
use PHPUnit\Framework\TestCase;
8+
use Yiisoft\ErrorHandler\Renderer\HtmlRenderer;
9+
use Yiisoft\ErrorHandler\Tests\Support\TestExceptionWithAttribute;
10+
11+
final class HtmlRendererWithAttributeTest extends TestCase
12+
{
13+
public function testGetThrowableNameWithAttribute(): void
14+
{
15+
$this->markTestSkipped('The attribute feature is not available in the current version of friendly-exception package');
16+
17+
$renderer = new HtmlRenderer();
18+
$exception = new TestExceptionWithAttribute();
19+
20+
$name = $renderer->getThrowableName($exception);
21+
22+
$this->assertStringContainsString('Test Exception Name', $name);
23+
$this->assertStringContainsString($exception::class, $name);
24+
}
25+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Yiisoft\ErrorHandler\Tests\Support;
6+
7+
use Exception;
8+
use Yiisoft\FriendlyException\Attribute\FriendlyException;
9+
10+
#[FriendlyException(name: 'Test Exception Name', solution: 'This is a test solution for an exception.')]
11+
final class TestExceptionWithAttribute extends Exception
12+
{
13+
public function __construct()
14+
{
15+
parent::__construct('This is a test exception with attribute.');
16+
}
17+
}

0 commit comments

Comments
 (0)