From c08cf44afa33e41e69c3a8e054b186f567eab47d Mon Sep 17 00:00:00 2001 From: DoubleJack Date: Thu, 17 Apr 2025 17:59:00 +0800 Subject: [PATCH 1/3] Fix HtmlRenderer to handle FriendlyException attribute safely when class is not available --- src/Renderer/HtmlRenderer.php | 23 ++++++- templates/development.php | 64 +++++++++++++++++-- .../HtmlRendererWithAttributeTest.php | 25 ++++++++ tests/Support/TestExceptionWithAttribute.php | 17 +++++ 4 files changed, 121 insertions(+), 8 deletions(-) create mode 100644 tests/Renderer/Attribute/HtmlRendererWithAttributeTest.php create mode 100644 tests/Support/TestExceptionWithAttribute.php diff --git a/src/Renderer/HtmlRenderer.php b/src/Renderer/HtmlRenderer.php index c85acb0..c1cae68 100644 --- a/src/Renderer/HtmlRenderer.php +++ b/src/Renderer/HtmlRenderer.php @@ -14,7 +14,9 @@ use Yiisoft\ErrorHandler\Exception\ErrorException; use Yiisoft\ErrorHandler\ThrowableRendererInterface; use Yiisoft\FriendlyException\FriendlyExceptionInterface; +use Yiisoft\FriendlyException\Attribute\FriendlyException; use Yiisoft\Http\Header; +use ReflectionClass; use function array_values; use function dirname; @@ -491,9 +493,11 @@ public function createServerInformationLink(ServerRequestInterface $request): st } /** - * Returns the name of the throwable instance. + * Returns string representation of the throwable name. * - * @return string The name of the throwable instance. + * @param Throwable $throwable The throwable. + * + * @return string The throwable name. */ public function getThrowableName(Throwable $throwable): string { @@ -501,6 +505,21 @@ public function getThrowableName(Throwable $throwable): string if ($throwable instanceof FriendlyExceptionInterface) { $name = $throwable->getName() . ' (' . $name . ')'; + } else { + // Check if the exception class has FriendlyException attribute + try { + $reflectionClass = new ReflectionClass($throwable); + if (class_exists('Yiisoft\FriendlyException\Attribute\FriendlyException')) { + $attributes = $reflectionClass->getAttributes('Yiisoft\FriendlyException\Attribute\FriendlyException'); + + if (!empty($attributes)) { + $friendlyExceptionAttribute = $attributes[0]->newInstance(); + $name = $friendlyExceptionAttribute->name . ' (' . $name . ')'; + } + } + } catch (\Throwable $e) { + // Ignore exception and keep default name + } } return $name; diff --git a/templates/development.php b/templates/development.php index be386b3..fab60af 100644 --- a/templates/development.php +++ b/templates/development.php @@ -1,8 +1,13 @@ getSolution() : null; + +// Check if the exception class has FriendlyException attribute +if ($solution === null && class_exists('Yiisoft\FriendlyException\Attribute\FriendlyException')) { + try { + $reflectionClass = new ReflectionClass($throwable); + $attributes = $reflectionClass->getAttributes('Yiisoft\FriendlyException\Attribute\FriendlyException'); + + if (!empty($attributes)) { + $friendlyExceptionAttribute = $attributes[0]->newInstance(); + $solution = $friendlyExceptionAttribute->solution; + } + } catch (\Throwable $e) { + // Ignore exception + } +} + $exceptionClass = get_class($throwable); $exceptionMessage = $throwable->getMessage(); @@ -78,13 +99,44 @@
+ $hasFriendlyName = false; + if ($isFriendlyException) { + $hasFriendlyName = true; + ?> htmlEncode($throwable->getName())?> - - - + getAttributes('Yiisoft\FriendlyException\Attribute\FriendlyException'); + + if (!empty($attributes)) { + $friendlyExceptionAttribute = $attributes[0]->newInstance(); + $hasFriendlyNameFromAttribute = true; + ?> + htmlEncode($friendlyExceptionAttribute->name) ?> + — + + + + (Code #getCode() ?>)
@@ -98,12 +150,12 @@ renderPreviousExceptions($originalException) ?> - + Copied! diff --git a/tests/Renderer/Attribute/HtmlRendererWithAttributeTest.php b/tests/Renderer/Attribute/HtmlRendererWithAttributeTest.php new file mode 100644 index 0000000..7c6c170 --- /dev/null +++ b/tests/Renderer/Attribute/HtmlRendererWithAttributeTest.php @@ -0,0 +1,25 @@ +markTestSkipped('The attribute feature is not available in the current version of friendly-exception package'); + + $renderer = new HtmlRenderer(); + $exception = new TestExceptionWithAttribute(); + + $name = $renderer->getThrowableName($exception); + + $this->assertStringContainsString('Test Exception Name', $name); + $this->assertStringContainsString($exception::class, $name); + } +} \ No newline at end of file diff --git a/tests/Support/TestExceptionWithAttribute.php b/tests/Support/TestExceptionWithAttribute.php new file mode 100644 index 0000000..c154bed --- /dev/null +++ b/tests/Support/TestExceptionWithAttribute.php @@ -0,0 +1,17 @@ + Date: Thu, 17 Apr 2025 10:06:56 +0000 Subject: [PATCH 2/3] Apply Rector changes (CI) --- src/Renderer/HtmlRenderer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Renderer/HtmlRenderer.php b/src/Renderer/HtmlRenderer.php index c1cae68..cd1e8b9 100644 --- a/src/Renderer/HtmlRenderer.php +++ b/src/Renderer/HtmlRenderer.php @@ -517,7 +517,7 @@ public function getThrowableName(Throwable $throwable): string $name = $friendlyExceptionAttribute->name . ' (' . $name . ')'; } } - } catch (\Throwable $e) { + } catch (\Throwable) { // Ignore exception and keep default name } } From 9b259e2e11f79a7cb9c499916f759757172417d7 Mon Sep 17 00:00:00 2001 From: DoubleJack Date: Sun, 20 Apr 2025 16:50:15 +0800 Subject: [PATCH 3/3] Use class name notation instead of strings for FriendlyException attribute - Remove unnecessary try/catch blocks as suggested in PR review --- src/Renderer/HtmlRenderer.php | 18 +++++++----------- templates/development.php | 8 ++++---- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/src/Renderer/HtmlRenderer.php b/src/Renderer/HtmlRenderer.php index c1cae68..a09d1ff 100644 --- a/src/Renderer/HtmlRenderer.php +++ b/src/Renderer/HtmlRenderer.php @@ -507,18 +507,14 @@ public function getThrowableName(Throwable $throwable): string $name = $throwable->getName() . ' (' . $name . ')'; } else { // Check if the exception class has FriendlyException attribute - try { - $reflectionClass = new ReflectionClass($throwable); - if (class_exists('Yiisoft\FriendlyException\Attribute\FriendlyException')) { - $attributes = $reflectionClass->getAttributes('Yiisoft\FriendlyException\Attribute\FriendlyException'); - - if (!empty($attributes)) { - $friendlyExceptionAttribute = $attributes[0]->newInstance(); - $name = $friendlyExceptionAttribute->name . ' (' . $name . ')'; - } + $reflectionClass = new ReflectionClass($throwable); + if (class_exists(\Yiisoft\FriendlyException\Attribute\FriendlyException::class)) { + $attributes = $reflectionClass->getAttributes(\Yiisoft\FriendlyException\Attribute\FriendlyException::class); + + if (!empty($attributes)) { + $friendlyExceptionAttribute = $attributes[0]->newInstance(); + $name = $friendlyExceptionAttribute->name . ' (' . $name . ')'; } - } catch (\Throwable $e) { - // Ignore exception and keep default name } } diff --git a/templates/development.php b/templates/development.php index fab60af..a06760e 100644 --- a/templates/development.php +++ b/templates/development.php @@ -27,10 +27,10 @@ $solution = $isFriendlyException ? $throwable->getSolution() : null; // Check if the exception class has FriendlyException attribute -if ($solution === null && class_exists('Yiisoft\FriendlyException\Attribute\FriendlyException')) { +if ($solution === null && class_exists(\Yiisoft\FriendlyException\Attribute\FriendlyException::class)) { try { $reflectionClass = new ReflectionClass($throwable); - $attributes = $reflectionClass->getAttributes('Yiisoft\FriendlyException\Attribute\FriendlyException'); + $attributes = $reflectionClass->getAttributes(\Yiisoft\FriendlyException\Attribute\FriendlyException::class); if (!empty($attributes)) { $friendlyExceptionAttribute = $attributes[0]->newInstance(); @@ -111,10 +111,10 @@ // Check if the exception class has FriendlyException attribute $hasFriendlyNameFromAttribute = false; - if (class_exists('Yiisoft\FriendlyException\Attribute\FriendlyException')) { + if (class_exists(\Yiisoft\FriendlyException\Attribute\FriendlyException::class)) { try { $reflectionClass = new ReflectionClass($throwable); - $attributes = $reflectionClass->getAttributes('Yiisoft\FriendlyException\Attribute\FriendlyException'); + $attributes = $reflectionClass->getAttributes(\Yiisoft\FriendlyException\Attribute\FriendlyException::class); if (!empty($attributes)) { $friendlyExceptionAttribute = $attributes[0]->newInstance();