Skip to content
Merged
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
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,4 @@ jobs:
os: >-
['ubuntu-latest', 'windows-latest']
php: >-
['8.1', '8.2', '8.3']
['8.1', '8.2', '8.3', '8.4']
2 changes: 1 addition & 1 deletion .github/workflows/composer-require-checker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,4 @@ jobs:
os: >-
['ubuntu-latest']
php: >-
['8.1', '8.2', '8.3']
['8.1', '8.2', '8.3', '8.4']
2 changes: 1 addition & 1 deletion .github/workflows/rector.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,4 @@ jobs:
os: >-
['ubuntu-latest']
php: >-
['8.3']
['8.4']
2 changes: 1 addition & 1 deletion .github/workflows/static.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,4 @@ jobs:
os: >-
['ubuntu-latest']
php: >-
['8.1', '8.2', '8.3']
['8.1', '8.2', '8.3', '8.4']
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
class (@olegbaturin)
- Chg #137: Add separate parameters for each of `HtmlRenderer` settings in constructor. Mark `$settings` parameter as
deprecated (@vjik)
- Enh #138: Raise the minimum PHP version to 8.1 and minor refactoring (@vjik)
- Enh #138, #139: Raise the minimum PHP version to 8.1 and minor refactoring (@vjik)
- Chg #139: Change PHP constraint in `composer.json` to `~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0` (@vjik)
- Bug #139: Explicitly mark nullable parameters (@vjik)

## 3.3.0 July 11, 2024

Expand Down
16 changes: 11 additions & 5 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
}
],
"require": {
"php": "^8.1",
"php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0",
"ext-dom": "*",
"ext-mbstring": "*",
"alexkart/curl-builder": "^1.0",
Expand All @@ -47,14 +47,14 @@
"yiisoft/injector": "^1.0"
},
"require-dev": {
"bamarni/composer-bin-plugin": "^1.8",
"httpsoft/http-message": "^1.1.6",
"maglnet/composer-require-checker": "^4.7.1",
"phpunit/phpunit": "^10.5.44",
"psr/event-dispatcher": "^1.0",
"rector/rector": "^2.0.7",
"roave/infection-static-analysis-plugin": "^1.35",
"spatie/phpunit-watcher": "^1.24",
"vimeo/psalm": "^5.26.1|^6",
"vimeo/psalm": "^5.26.1|^6.2",
"yiisoft/di": "^1.3",
"yiisoft/test-support": "^3.0.1"
},
Expand All @@ -69,6 +69,11 @@
}
},
"extra": {
"bamarni-bin": {
"bin-links": true,
"target-directory": "tools",
"forward-command": true
},
"config-plugin-options": {
"source-directory": "config"
},
Expand All @@ -80,8 +85,9 @@
"sort-packages": true,
"bump-after-update": "dev",
"allow-plugins": {
"infection/extension-installer": true,
"composer/package-versions-deprecated": true
"bamarni/composer-bin-plugin": true,
"composer/package-versions-deprecated": true,
"infection/extension-installer": true
}
},
"scripts": {
Expand Down
1 change: 1 addition & 0 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
executionOrder="random"
failOnRisky="true"
failOnWarning="true"
failOnDeprecation="true"
stopOnFailure="false"
colors="true"
displayDetailsOnPhpunitDeprecations="true"
Expand Down
2 changes: 2 additions & 0 deletions rector.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Rector\Config\RectorConfig;
use Rector\Php71\Rector\FuncCall\RemoveExtraParametersRector;
use Rector\Php74\Rector\Closure\ClosureToArrowFunctionRector;
use Rector\Php81\Rector\ClassMethod\NewInInitializerRector;
use Rector\Php81\Rector\FuncCall\NullToStrictStringFuncCallArgRector;

return RectorConfig::configure()
Expand All @@ -21,4 +22,5 @@
ClosureToArrowFunctionRector::class,
NullToStrictStringFuncCallArgRector::class,
RemoveExtraParametersRector::class,
NewInInitializerRector::class,
]);
7 changes: 5 additions & 2 deletions src/ErrorHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
private readonly LoggerInterface $logger,
private readonly ThrowableRendererInterface $defaultRenderer,
private readonly ?EventDispatcherInterface $eventDispatcher = null,
private readonly int $exitShutdownHandlerDepth = 2

Check warning on line 53 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 LoggerInterface $logger, private readonly ThrowableRendererInterface $defaultRenderer, private readonly ?EventDispatcherInterface $eventDispatcher = null, private readonly int $exitShutdownHandlerDepth = 2) + public function __construct(private readonly LoggerInterface $logger, private readonly ThrowableRendererInterface $defaultRenderer, private readonly ?EventDispatcherInterface $eventDispatcher = null, private readonly int $exitShutdownHandlerDepth = 1) { } /**

Check warning on line 53 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 LoggerInterface $logger, private readonly ThrowableRendererInterface $defaultRenderer, private readonly ?EventDispatcherInterface $eventDispatcher = null, private readonly int $exitShutdownHandlerDepth = 2) + public function __construct(private readonly LoggerInterface $logger, private readonly ThrowableRendererInterface $defaultRenderer, private readonly ?EventDispatcherInterface $eventDispatcher = null, private readonly int $exitShutdownHandlerDepth = 3) { } /**
) {
}

Expand All @@ -62,13 +62,13 @@
*/
public function handle(
Throwable $t,
ThrowableRendererInterface $renderer = null,
ServerRequestInterface $request = null
?ThrowableRendererInterface $renderer = null,
?ServerRequestInterface $request = null
): ErrorData {
$renderer ??= $this->defaultRenderer;

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

Check warning on line 71 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);

Check warning on line 71 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);
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 +108,14 @@
return;
}

if ($this->memoryReserveSize > 0) {

Check warning on line 111 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();

Check warning on line 111 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();
$this->memoryReserve = str_repeat('x', $this->memoryReserveSize);
}

$this->initializeOnce();

Check warning on line 115 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 118 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 +129,12 @@
return false;
}

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

Check warning on line 132 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 137 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 Expand Up @@ -192,6 +192,9 @@
});

if (!(PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg')) {
/**
* @var string
*/
$this->workingDirectory = getcwd();
}

Expand Down
6 changes: 3 additions & 3 deletions src/Exception/ErrorException.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,14 @@
E_USER_ERROR => 'PHP User Error',
E_USER_WARNING => 'PHP User Warning',
E_USER_NOTICE => 'PHP User Notice',
E_STRICT => 'PHP Strict Warning',
2048 => 'PHP Strict Warning', // E_STRICT
E_RECOVERABLE_ERROR => 'PHP Recoverable Error',
E_DEPRECATED => 'PHP Deprecated Warning',
E_USER_DEPRECATED => 'PHP User Deprecated Warning',
];

/** @psalm-param DebugBacktraceType $backtrace */
public function __construct(string $message = '', int $code = 0, int $severity = 1, string $filename = __FILE__, int $line = __LINE__, Exception $previous = null, private readonly array $backtrace = [])
public function __construct(string $message = '', int $code = 0, int $severity = 1, string $filename = __FILE__, int $line = __LINE__, ?Exception $previous = null, private readonly array $backtrace = [])
{
parent::__construct($message, $code, $severity, $filename, $line, $previous);
$this->addXDebugTraceToFatalIfAvailable();
Expand Down Expand Up @@ -149,6 +149,6 @@
}

// Xdebug 3 and later, proper mode is required
return str_contains(ini_get('xdebug.mode'), 'develop');
return str_contains((string) \ini_get('xdebug.mode'), 'develop');

Check warning on line 152 in src/Exception/ErrorException.php

View check run for this annotation

Codecov / codecov/patch

src/Exception/ErrorException.php#L152

Added line #L152 was not covered by tests
}
}
8 changes: 4 additions & 4 deletions src/Factory/ThrowableResponseFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,10 @@ final class ThrowableResponseFactory implements ThrowableResponseFactoryInterfac
private ?string $contentType = null;

public function __construct(
private ResponseFactoryInterface $responseFactory,
private ErrorHandler $errorHandler,
private ContainerInterface $container,
HeadersProvider $headersProvider = null,
private readonly ResponseFactoryInterface $responseFactory,
private readonly ErrorHandler $errorHandler,
private readonly ContainerInterface $container,
?HeadersProvider $headersProvider = null,
) {
$this->headersProvider = $headersProvider ?? new HeadersProvider();
}
Expand Down
4 changes: 2 additions & 2 deletions src/Renderer/HeaderRenderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@
*/
final class HeaderRenderer implements ThrowableRendererInterface
{
public function render(Throwable $t, ServerRequestInterface $request = null): ErrorData
public function render(Throwable $t, ?ServerRequestInterface $request = null): ErrorData
{
return new ErrorData('', ['X-Error-Message' => self::DEFAULT_ERROR_MESSAGE]);
}

public function renderVerbose(Throwable $t, ServerRequestInterface $request = null): ErrorData
public function renderVerbose(Throwable $t, ?ServerRequestInterface $request = null): ErrorData
{
return new ErrorData('', [
'X-Error-Type' => $t::class,
Expand Down
6 changes: 3 additions & 3 deletions src/Renderer/HtmlRenderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -156,15 +156,15 @@ public function __construct(
?? null;
}

public function render(Throwable $t, ServerRequestInterface $request = null): ErrorData
public function render(Throwable $t, ?ServerRequestInterface $request = null): ErrorData
{
return new ErrorData($this->renderTemplate($this->template, [
'request' => $request,
'throwable' => $t,
]));
}

public function renderVerbose(Throwable $t, ServerRequestInterface $request = null): ErrorData
public function renderVerbose(Throwable $t, ?ServerRequestInterface $request = null): ErrorData
{
return new ErrorData($this->renderTemplate($this->verboseTemplate, [
'request' => $request,
Expand Down Expand Up @@ -530,7 +530,7 @@ private function renderTemplate(string $path, array $parameters): string
try {
/** @psalm-suppress PossiblyNullFunctionCall */
$renderer->bindTo($this)($path, $parameters);
return ob_get_clean();
return (string) ob_get_clean();
} catch (Throwable $e) {
while (ob_get_level() > $obInitialLevel) {
if (!@ob_end_clean()) {
Expand Down
6 changes: 3 additions & 3 deletions src/Renderer/JsonRenderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
*/
final class JsonRenderer implements ThrowableRendererInterface
{
public function render(Throwable $t, ServerRequestInterface $request = null): ErrorData
public function render(Throwable $t, ?ServerRequestInterface $request = null): ErrorData
{
return new ErrorData(
json_encode(
Expand All @@ -28,7 +28,7 @@ public function render(Throwable $t, ServerRequestInterface $request = null): Er
);
}

public function renderVerbose(Throwable $t, ServerRequestInterface $request = null): ErrorData
public function renderVerbose(Throwable $t, ?ServerRequestInterface $request = null): ErrorData
{
return new ErrorData(
json_encode(
Expand All @@ -40,7 +40,7 @@ public function renderVerbose(Throwable $t, ServerRequestInterface $request = nu
'line' => $t->getLine(),
'trace' => $t->getTrace(),
],
JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_INVALID_UTF8_SUBSTITUTE | JSON_PARTIAL_OUTPUT_ON_ERROR
JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_INVALID_UTF8_SUBSTITUTE | JSON_PARTIAL_OUTPUT_ON_ERROR
)
);
}
Expand Down
4 changes: 2 additions & 2 deletions src/Renderer/PlainTextRenderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@
*/
final class PlainTextRenderer implements ThrowableRendererInterface
{
public function render(Throwable $t, ServerRequestInterface $request = null): ErrorData
public function render(Throwable $t, ?ServerRequestInterface $request = null): ErrorData
{
return new ErrorData(self::DEFAULT_ERROR_MESSAGE);
}

public function renderVerbose(Throwable $t, ServerRequestInterface $request = null): ErrorData
public function renderVerbose(Throwable $t, ?ServerRequestInterface $request = null): ErrorData
{
return new ErrorData(
self::throwableToString($t)
Expand Down
4 changes: 2 additions & 2 deletions src/Renderer/XmlRenderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
*/
final class XmlRenderer implements ThrowableRendererInterface
{
public function render(Throwable $t, ServerRequestInterface $request = null): ErrorData
public function render(Throwable $t, ?ServerRequestInterface $request = null): ErrorData
{
$content = '<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>';
$content .= "\n<error>\n";
Expand All @@ -25,7 +25,7 @@ public function render(Throwable $t, ServerRequestInterface $request = null): Er
return new ErrorData($content);
}

public function renderVerbose(Throwable $t, ServerRequestInterface $request = null): ErrorData
public function renderVerbose(Throwable $t, ?ServerRequestInterface $request = null): ErrorData
{
$content = '<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>';
$content .= "\n<error>\n";
Expand Down
4 changes: 2 additions & 2 deletions src/ThrowableRendererInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,13 @@ interface ThrowableRendererInterface
* @param ServerRequestInterface|null $request
* @return ErrorData
*/
public function render(Throwable $t, ServerRequestInterface $request = null): ErrorData;
public function render(Throwable $t, ?ServerRequestInterface $request = null): ErrorData;

/**
* Returns error data suitable for adding it to response in development environment.
*
* @param ServerRequestInterface|null $request
* @return ErrorData
*/
public function renderVerbose(Throwable $t, ServerRequestInterface $request = null): ErrorData;
public function renderVerbose(Throwable $t, ?ServerRequestInterface $request = null): ErrorData;
}
2 changes: 1 addition & 1 deletion tests/Factory/ThrowableResponseFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ public function testAddedHeaders(): void
}

private function createThrowableResponseFactory(
HeadersProvider $provider = null,
?HeadersProvider $provider = null,
): ThrowableResponseFactoryInterface {
$container = new SimpleContainer([], fn (string $className): object => new $className());
return new ThrowableResponseFactory(
Expand Down
16 changes: 14 additions & 2 deletions tests/Renderer/HtmlRendererTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Exception;
use HttpSoft\Message\Uri;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\WithoutErrorHandler;
use PHPUnit\Framework\TestCase;
use Psr\Http\Message\ServerRequestInterface;
use ReflectionClass;
Expand Down Expand Up @@ -131,9 +132,16 @@ public function testRenderCallStack(): void
);
}

#[WithoutErrorHandler]
public function testRenderCallStackItemIfFileIsNotExistAndLineMoreZero(): void
{
$this->assertEmpty($this->invokeMethod(new HtmlRenderer(), 'renderCallStackItem', [
$errorMessage = null;
set_error_handler(
static function (int $code, string $message) use (&$errorMessage) {
$errorMessage = $message;
}
);
$result = $this->invokeMethod(new HtmlRenderer(), 'renderCallStackItem', [
'file' => 'not-exist',
'line' => 1,
'class' => null,
Expand All @@ -142,7 +150,11 @@ public function testRenderCallStackItemIfFileIsNotExistAndLineMoreZero(): void
'index' => 1,
'isVendorFile' => false,
'reflectionParameters' => [],
]));
]);
restore_error_handler();

$this->assertSame('', $result);
$this->assertSame('file(not-exist): Failed to open stream: No such file or directory', $errorMessage);
}

public function testRenderRequest(): void
Expand Down
2 changes: 2 additions & 0 deletions tools/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/*/vendor
/*/composer.lock
8 changes: 8 additions & 0 deletions tools/composer-require-checker/composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"require-dev": {
"maglnet/composer-require-checker": "^4.7.1"
},
"config": {
"bump-after-update": "dev"
}
}
Loading