Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit ff66e6a

Browse files
Fix #7616: Add yii\web\ErrorHandler::EVENT_AFTER_RENDER and yii\web\ErrorHandlerRenderEvent to post-process rendered HTML error output
1 parent 95d8df5 commit ff66e6a

4 files changed

Lines changed: 145 additions & 0 deletions

File tree

framework/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ Yii Framework 2 Change Log
99
- Bug #20785: Fix `@return` annotation for `yii\base\Model::getErrors()` (mspirkov)
1010
- Bug #20785: Fix `@var` annotation for `yii\validators\CompareValidator::$message` (mspirkov)
1111
- Enh #20785: Add `@param-out` annotation for `$error` in `yii\validators\Validator::validate()` (mspirkov)
12+
- Enh #7616: Add `yii\web\ErrorHandler::EVENT_AFTER_RENDER` and `yii\web\ErrorHandlerRenderEvent` to post-process rendered HTML error output (terabytesoftw)
1213

1314

1415
2.0.55 May 09, 2026

framework/web/ErrorHandler.php

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,15 @@
88

99
namespace yii\web;
1010

11+
use Throwable;
1112
use Yii;
1213
use yii\base\ErrorException;
1314
use yii\base\Exception;
1415
use yii\base\UserException;
1516
use yii\helpers\VarDumper;
1617

18+
use function is_string;
19+
1720
/**
1821
* ErrorHandler handles uncaught PHP errors and exceptions.
1922
*
@@ -31,6 +34,15 @@
3134
*/
3235
class ErrorHandler extends \yii\base\ErrorHandler
3336
{
37+
/**
38+
* @event ErrorHandlerRenderEvent an event that is triggered after HTML error content is rendered.
39+
*
40+
* Event handlers may modify [[ErrorHandlerRenderEvent::$output]].
41+
*
42+
* @since 2.0.56
43+
*/
44+
public const EVENT_AFTER_RENDER = 'afterRender';
45+
3446
/**
3547
* @var int maximum number of source code lines to be displayed. Defaults to 19.
3648
*/
@@ -136,6 +148,14 @@ protected function renderException($exception)
136148
$response->data = $this->convertExceptionToArray($exception);
137149
}
138150

151+
if ($response->format === Response::FORMAT_HTML) {
152+
if (is_string($response->data)) {
153+
$response->data = $this->triggerAfterRender($exception, $response->data);
154+
} elseif (is_string($response->content)) {
155+
$response->content = $this->triggerAfterRender($exception, $response->content);
156+
}
157+
}
158+
139159
$response->send();
140160
}
141161

@@ -186,6 +206,25 @@ public function htmlEncode($text)
186206
return htmlspecialchars($text, ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML5, 'UTF-8');
187207
}
188208

209+
/**
210+
* Triggers [[EVENT_AFTER_RENDER]] and returns the final HTML content.
211+
*
212+
* @since 2.0.56
213+
*/
214+
protected function triggerAfterRender(Throwable $exception, string $output): string
215+
{
216+
if ($this->hasEventHandlers(self::EVENT_AFTER_RENDER)) {
217+
$event = new ErrorHandlerRenderEvent();
218+
$event->exception = $exception;
219+
$event->output = $output;
220+
$this->trigger(self::EVENT_AFTER_RENDER, $event);
221+
222+
return $event->output;
223+
}
224+
225+
return $output;
226+
}
227+
189228
/**
190229
* Adds informational links to the given PHP type/class.
191230
* @param string $code type/class name to be linkified.
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
/**
4+
* @link https://www.yiiframework.com/
5+
* @copyright Copyright (c) 2008 Yii Software LLC
6+
* @license https://www.yiiframework.com/license/
7+
*/
8+
9+
namespace yii\web;
10+
11+
use Throwable;
12+
use yii\base\Event;
13+
14+
/**
15+
* ErrorHandlerRenderEvent represents events triggered by [[ErrorHandler]].
16+
*
17+
* @author Wilmer Arambula <[email protected]>
18+
* @since 2.0.56
19+
*/
20+
class ErrorHandlerRenderEvent extends Event
21+
{
22+
/**
23+
* @var \Throwable|null Exception being rendered.
24+
*/
25+
public ?Throwable $exception = null;
26+
/**
27+
* @var string Rendered HTML output.
28+
*/
29+
public string $output = '';
30+
}

tests/framework/web/ErrorHandlerTest.php

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use yii\base\ErrorException;
1414
use yii\web\Application;
1515
use Yii;
16+
use yii\web\ErrorHandlerRenderEvent;
1617
use yii\web\NotFoundHttpException;
1718
use yii\web\View;
1819
use yiiunit\TestCase;
@@ -124,6 +125,80 @@ public function testClearAssetFilesInErrorActionView(): void
124125
$this->assertStringNotContainsString('<script', $out);
125126
}
126127

128+
public function testAfterRenderEventCanModifyOutput(): void
129+
{
130+
/** @var ErrorHandler $handler */
131+
$handler = Yii::$app->getErrorHandler();
132+
133+
$exception = new Exception('Some Exception');
134+
135+
$actualException = null;
136+
137+
$handler->on(
138+
ErrorHandler::EVENT_AFTER_RENDER,
139+
static function (ErrorHandlerRenderEvent $event) use (&$actualException): void {
140+
$actualException = $event->exception;
141+
$event->output .= "\n<!--after-render-->";
142+
}
143+
);
144+
145+
ob_start(); // suppress response output
146+
$this->invokeMethod($handler, 'renderException', [$exception]);
147+
ob_get_clean();
148+
149+
$this->assertSame($exception, $actualException);
150+
$this->assertStringContainsString('<!--after-render-->', Yii::$app->response->data);
151+
}
152+
153+
public function testAfterRenderEventCanModifyOutputInErrorActionView(): void
154+
{
155+
/** @var ErrorHandler $handler */
156+
$handler = Yii::$app->getErrorHandler();
157+
$handler->errorAction = 'test/error';
158+
159+
$exception = new NotFoundHttpException('Resource not found');
160+
161+
$actualException = null;
162+
163+
$handler->on(
164+
ErrorHandler::EVENT_AFTER_RENDER,
165+
static function (ErrorHandlerRenderEvent $event) use (&$actualException): void {
166+
$actualException = $event->exception;
167+
$event->output .= "\n<!--after-render-error-action-->";
168+
}
169+
);
170+
171+
ob_start(); // suppress response output
172+
$this->invokeMethod($handler, 'renderException', [$exception]);
173+
ob_get_clean();
174+
175+
$this->assertSame($exception, $actualException);
176+
$this->assertStringContainsString('<!--after-render-error-action-->', Yii::$app->response->data);
177+
}
178+
179+
public function testAfterRenderEventCanModifyOutputForPhpErrors(): void
180+
{
181+
/** @var ErrorHandler $handler */
182+
$handler = Yii::$app->getErrorHandler();
183+
184+
$exception = new ErrorException('PHP Warning', E_WARNING, E_WARNING, __FILE__, __LINE__);
185+
186+
$handler->exception = $exception;
187+
188+
$handler->on(
189+
ErrorHandler::EVENT_AFTER_RENDER,
190+
static function (ErrorHandlerRenderEvent $event): void {
191+
$event->output .= "\n<!--php-error-after-render-->";
192+
}
193+
);
194+
195+
ob_start(); // suppress response output
196+
$this->invokeMethod($handler, 'renderException', [$exception]);
197+
ob_get_clean();
198+
199+
$this->assertStringContainsString('<!--php-error-after-render-->', Yii::$app->response->data);
200+
}
201+
127202
public function testRenderCallStackItem(): void
128203
{
129204
$handler = Yii::$app->getErrorHandler();

0 commit comments

Comments
 (0)