Skip to content
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Yii Error Handler Change Log

## 5.0 under development

- Chg #121: Use `NullLogger` as the default logger in the `ErrorHandler` (@olegbaturin)
- Chg #144: Mark `ErrorException` and `UserException` as final (@olegbaturin)
- Chg #146: Remove deprecated `Yiisoft\ErrorHandler\Factory\ThrowableResponseFactory` (@olegbaturin)
- Chg #157: Change parameters order in the `ThrowableResponseActionInterface` (@olegbaturin)

## 4.3.1 under development

- no changes in this release.
Expand Down
17 changes: 16 additions & 1 deletion config/di-web.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,14 @@

declare(strict_types=1);

use Yiisoft\ErrorHandler\Factory\ThrowableResponseFactory;
use Psr\Container\ContainerInterface;
use Yiisoft\Definitions\DynamicReference;
use Yiisoft\ErrorHandler\Renderer\HtmlRenderer;
use Yiisoft\ErrorHandler\RendererProvider\CompositeRendererProvider;
use Yiisoft\ErrorHandler\RendererProvider\ContentTypeRendererProvider;
use Yiisoft\ErrorHandler\RendererProvider\HeadRendererProvider;
use Yiisoft\ErrorHandler\ThrowableRendererInterface;
use Yiisoft\ErrorHandler\ThrowableResponseFactory;
use Yiisoft\ErrorHandler\ThrowableResponseFactoryInterface;

/**
Expand All @@ -14,4 +19,14 @@
return [
ThrowableRendererInterface::class => HtmlRenderer::class,
ThrowableResponseFactoryInterface::class => ThrowableResponseFactory::class,
ThrowableResponseFactory::class => [
'__construct()' => [
'rendererProvider' => DynamicReference::to(
static fn(ContainerInterface $container) => new CompositeRendererProvider(
new HeadRendererProvider(),
new ContentTypeRendererProvider($container),
)
),
],
],
];
5 changes: 3 additions & 2 deletions src/ErrorHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Psr\EventDispatcher\EventDispatcherInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;
use Throwable;
use Yiisoft\ErrorHandler\Event\ApplicationError;
use Yiisoft\ErrorHandler\Exception\ErrorException;
Expand Down Expand Up @@ -41,16 +42,16 @@
private bool $initialized = false;

/**
* @param LoggerInterface $logger Logger to write errors to.
* @param ThrowableRendererInterface $defaultRenderer Default throwable renderer.
* @param LoggerInterface $logger Logger to write errors to.
* @param EventDispatcherInterface|null $eventDispatcher Event dispatcher for error events.
* @param int $exitShutdownHandlerDepth Depth of the exit() shutdown handler to ensure it's executed last.
*/
public function __construct(
private readonly LoggerInterface $logger,
private readonly ThrowableRendererInterface $defaultRenderer,
private readonly LoggerInterface $logger = new NullLogger(),
private readonly ?EventDispatcherInterface $eventDispatcher = null,
private readonly int $exitShutdownHandlerDepth = 2

Check warning on line 54 in src/ErrorHandler.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.1-ubuntu-latest

Escaped Mutant for Mutator "IncrementInteger": --- Original +++ New @@ @@ * @param EventDispatcherInterface|null $eventDispatcher Event dispatcher for error events. * @param int $exitShutdownHandlerDepth Depth of the exit() shutdown handler to ensure it's executed last. */ - public function __construct(private readonly ThrowableRendererInterface $defaultRenderer, private readonly LoggerInterface $logger = new NullLogger(), private readonly ?EventDispatcherInterface $eventDispatcher = null, private readonly int $exitShutdownHandlerDepth = 2) + public function __construct(private readonly ThrowableRendererInterface $defaultRenderer, private readonly LoggerInterface $logger = new NullLogger(), private readonly ?EventDispatcherInterface $eventDispatcher = null, private readonly int $exitShutdownHandlerDepth = 3) { } /**

Check warning on line 54 in src/ErrorHandler.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.1-ubuntu-latest

Escaped Mutant for Mutator "DecrementInteger": --- Original +++ New @@ @@ * @param EventDispatcherInterface|null $eventDispatcher Event dispatcher for error events. * @param int $exitShutdownHandlerDepth Depth of the exit() shutdown handler to ensure it's executed last. */ - public function __construct(private readonly ThrowableRendererInterface $defaultRenderer, private readonly LoggerInterface $logger = new NullLogger(), private readonly ?EventDispatcherInterface $eventDispatcher = null, private readonly int $exitShutdownHandlerDepth = 2) + public function __construct(private readonly ThrowableRendererInterface $defaultRenderer, private readonly LoggerInterface $logger = new NullLogger(), private readonly ?EventDispatcherInterface $eventDispatcher = null, private readonly int $exitShutdownHandlerDepth = 1) { } /**
) {
}

Expand All @@ -68,7 +69,7 @@
$renderer ??= $this->defaultRenderer;

try {
$this->logger->error($t->getMessage(), ['throwable' => $t]);

Check warning on line 72 in src/ErrorHandler.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.1-ubuntu-latest

Escaped Mutant for Mutator "MethodCallRemoval": --- Original +++ New @@ @@ { $renderer ??= $this->defaultRenderer; try { - $this->logger->error($t->getMessage(), ['throwable' => $t]); + return $this->debug ? $renderer->renderVerbose($t, $request) : $renderer->render($t, $request); } catch (Throwable $t) { return new ErrorData((string) $t);

Check warning on line 72 in src/ErrorHandler.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.1-ubuntu-latest

Escaped Mutant for Mutator "ArrayItemRemoval": --- Original +++ New @@ @@ { $renderer ??= $this->defaultRenderer; try { - $this->logger->error($t->getMessage(), ['throwable' => $t]); + $this->logger->error($t->getMessage(), []); return $this->debug ? $renderer->renderVerbose($t, $request) : $renderer->render($t, $request); } catch (Throwable $t) { return new ErrorData((string) $t);
return $this->debug ? $renderer->renderVerbose($t, $request) : $renderer->render($t, $request);
} catch (Throwable $t) {
return new ErrorData((string) $t);
Expand Down Expand Up @@ -108,14 +109,14 @@
return;
}

if ($this->memoryReserveSize > 0) {

Check warning on line 112 in src/ErrorHandler.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.1-ubuntu-latest

Escaped Mutant for Mutator "GreaterThanNegotiation": --- Original +++ New @@ @@ if ($this->enabled) { return; } - if ($this->memoryReserveSize > 0) { + if ($this->memoryReserveSize <= 0) { $this->memoryReserve = str_repeat('x', $this->memoryReserveSize); } $this->initializeOnce();

Check warning on line 112 in src/ErrorHandler.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.1-ubuntu-latest

Escaped Mutant for Mutator "GreaterThan": --- Original +++ New @@ @@ if ($this->enabled) { return; } - if ($this->memoryReserveSize > 0) { + if ($this->memoryReserveSize >= 0) { $this->memoryReserve = str_repeat('x', $this->memoryReserveSize); } $this->initializeOnce();
$this->memoryReserve = str_repeat('x', $this->memoryReserveSize);
}

$this->initializeOnce();

Check warning on line 116 in src/ErrorHandler.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.1-ubuntu-latest

Escaped Mutant for Mutator "MethodCallRemoval": --- Original +++ New @@ @@ if ($this->memoryReserveSize > 0) { $this->memoryReserve = str_repeat('x', $this->memoryReserveSize); } - $this->initializeOnce(); + // Handles throwable that isn't caught otherwise, echo output and exit. set_exception_handler(function (Throwable $t) : void { if (!$this->enabled) {

// Handles throwable that isn't caught otherwise, echo output and exit.
set_exception_handler(function (Throwable $t): void {

Check warning on line 119 in src/ErrorHandler.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.1-ubuntu-latest

Escaped Mutant for Mutator "FunctionCallRemoval": --- Original +++ New @@ @@ $this->memoryReserve = str_repeat('x', $this->memoryReserveSize); } $this->initializeOnce(); - // Handles throwable that isn't caught otherwise, echo output and exit. - set_exception_handler(function (Throwable $t) : void { - if (!$this->enabled) { - return; - } - $this->renderThrowableAndTerminate($t); - }); + // Handles PHP execution errors such as warnings and notices. set_error_handler(function (int $severity, string $message, string $file, int $line) : bool { if (!$this->enabled) {
if (!$this->enabled) {
return;
}
Expand All @@ -129,12 +130,12 @@
return false;
}

if (!(error_reporting() & $severity)) {

Check warning on line 133 in src/ErrorHandler.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.1-ubuntu-latest

Escaped Mutant for Mutator "BitwiseAnd": --- Original +++ New @@ @@ if (!$this->enabled) { return false; } - if (!(error_reporting() & $severity)) { + if (!(error_reporting() | $severity)) { // This error code is not included in error_reporting. return true; }
// This error code is not included in error_reporting.
return true;
}

$backtrace = debug_backtrace(0);

Check warning on line 138 in src/ErrorHandler.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.1-ubuntu-latest

Escaped Mutant for Mutator "DecrementInteger": --- Original +++ New @@ @@ // This error code is not included in error_reporting. return true; } - $backtrace = debug_backtrace(0); + $backtrace = debug_backtrace(-1); array_shift($backtrace); throw new ErrorException($message, $severity, $severity, $file, $line, null, $backtrace); });
array_shift($backtrace);
throw new ErrorException($message, $severity, $severity, $file, $line, null, $backtrace);
});
Expand Down
4 changes: 1 addition & 3 deletions src/Exception/ErrorException.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,8 @@
/**
* `ErrorException` represents a PHP error.
* @psalm-type DebugBacktraceType = list<array{args?: array<mixed>, class?: class-string, file?: string, function?: string, line?: int, object?: object, type?: string}>
*
* @final
*/
class ErrorException extends \ErrorException implements FriendlyExceptionInterface
final class ErrorException extends \ErrorException implements FriendlyExceptionInterface
{
/** @psalm-suppress MissingClassConstType Private constants never change. */
private const ERROR_NAMES = [
Expand Down
4 changes: 1 addition & 3 deletions src/Exception/UserException.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,9 @@
* - throw directly (`throw new UserException(...)`) for explicit user-facing errors;
* - annotate any exception class with the `#[UserException]` attribute
* to mark its messages as user-facing without extending this class.
*
* @final
*/
#[Attribute(Attribute::TARGET_CLASS)]
class UserException extends Exception
final class UserException extends Exception
{
public static function isUserException(Throwable $throwable): bool
{
Expand Down
190 changes: 0 additions & 190 deletions src/Factory/ThrowableResponseFactory.php

This file was deleted.

2 changes: 1 addition & 1 deletion src/Middleware/ErrorCatcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface
$t = new CompositeException($e, $t);
}

return $this->throwableResponseFactory->create($t, $request);
return $this->throwableResponseFactory->handle($request, $t);
}
}
}
7 changes: 2 additions & 5 deletions src/ThrowableResponseFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,15 @@
*/
final class ThrowableResponseFactory implements ThrowableResponseFactoryInterface
{
private readonly HeadersProvider $headersProvider;

public function __construct(
private readonly ResponseFactoryInterface $responseFactory,
private readonly ErrorHandler $errorHandler,
private readonly RendererProviderInterface $rendererProvider,
?HeadersProvider $headersProvider = null,
private readonly HeadersProvider $headersProvider = new HeadersProvider(),
) {
$this->headersProvider = $headersProvider ?? new HeadersProvider();
}

public function create(Throwable $throwable, ServerRequestInterface $request): ResponseInterface
public function handle(ServerRequestInterface $request, Throwable $throwable): ResponseInterface
{
$renderer = $this->rendererProvider->get($request);

Expand Down
2 changes: 1 addition & 1 deletion src/ThrowableResponseFactoryInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ interface ThrowableResponseFactoryInterface
/**
* Handles a `Throwable` object and produces a response.
*/
public function create(Throwable $throwable, ServerRequestInterface $request): ResponseInterface;
public function handle(ServerRequestInterface $request, Throwable $throwable): ResponseInterface;
}
15 changes: 11 additions & 4 deletions tests/ConfigTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,15 @@

namespace Yiisoft\ErrorHandler\Tests;

use HttpSoft\Message\ResponseFactory;
use PHPUnit\Framework\TestCase;
use Psr\Http\Message\ResponseFactoryInterface;
use Yiisoft\Di\Container;
use Yiisoft\Di\ContainerConfig;
use Yiisoft\ErrorHandler\Renderer\HtmlRenderer;
use Yiisoft\ErrorHandler\ThrowableRendererInterface;
use Yiisoft\ErrorHandler\ThrowableResponseFactory;
use Yiisoft\ErrorHandler\ThrowableResponseFactoryInterface;

final class ConfigTest extends TestCase
{
Expand All @@ -17,16 +21,19 @@ public function testDiWeb(): void
$container = $this->createContainer('web');

$throwableRenderer = $container->get(ThrowableRendererInterface::class);

$this->assertInstanceOf(HtmlRenderer::class, $throwableRenderer);

$throwableResponseFactory = $container->get(ThrowableResponseFactoryInterface::class);
$this->assertInstanceOf(ThrowableResponseFactory::class, $throwableResponseFactory);
}

private function createContainer(?string $postfix = null): Container
{
return new Container(
ContainerConfig::create()->withDefinitions(
$this->getDiConfig($postfix)
)
ContainerConfig::create()->withDefinitions([
ResponseFactoryInterface::class => ResponseFactory::class,
...$this->getDiConfig($postfix),
])
);
}

Expand Down
18 changes: 16 additions & 2 deletions tests/ErrorHandlerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,14 @@ protected function setUp(): void
{
$this->loggerMock = $this->createMock(LoggerInterface::class);
$this->throwableRendererMock = $this->createMock(ThrowableRendererInterface::class);
$this->errorHandler = new ErrorHandler($this->loggerMock, $this->throwableRendererMock);
$this->errorHandler = new ErrorHandler($this->throwableRendererMock, $this->loggerMock);
$this->errorHandler->memoryReserveSize(0);
}

public function testHandleThrowableCallsDefaultRendererWhenNonePassed(): void
public function testHandleThrowableCallsDefaultRendererWithoutLogger(): void
{
$errorHandler = new ErrorHandler($this->throwableRendererMock);
$errorHandler->memoryReserveSize(0);
$throwable = new RuntimeException();

$this
Expand All @@ -37,6 +39,18 @@ public function testHandleThrowableCallsDefaultRendererWhenNonePassed(): void
->method('render')
->with($throwable);

$errorHandler->handle($throwable);
}

public function testHandleThrowableCallsDefaultRendererWhenNonePassed(): void
{
$throwable = new RuntimeException();

$this
->throwableRendererMock
->expects($this->once())
->method('render')
->with($throwable);

$this->errorHandler->handle($throwable);
}
Expand Down
Loading
Loading