Skip to content
Open
Show file tree
Hide file tree
Changes from all 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->create($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 create(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 create(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