Skip to content

Commit

Permalink
feat: added HandleExceptions::class for handle php error (#368)
Browse files Browse the repository at this point in the history
* feat: add `HandleProviders::class` for handle php error

* test 1 of 3

* test 2 of 3 (1 is skipped)

* formatting

* revent: skip cli error render

* rename file name
  • Loading branch information
SonyPradana authored Sep 3, 2024
1 parent caa1f51 commit 939e0f7
Show file tree
Hide file tree
Showing 3 changed files with 216 additions and 0 deletions.
96 changes: 96 additions & 0 deletions src/System/Integrate/Bootstrap/HandleExceptions.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<?php

declare(strict_types=1);

namespace System\Integrate\Bootstrap;

use System\Integrate\Application;
use System\Integrate\Exceptions\Handler;

class HandleExceptions
{
private Application $app;
public static ?string $reserveMemory = null;

public function bootstrap(Application $app): void
{
self::$reserveMemory = str_repeat('x', 32_768);

$this->app = $app;

error_reporting(E_ALL);

/* @phpstan-ignore-next-line */
set_error_handler([$this, 'handleError']);

set_exception_handler([$this, 'handleException']);

register_shutdown_function([$this, 'handleShutdown']);

if ('testing' !== $app->environment()) {
ini_set('display_errors', 'Off');
}
}

public function handleError(int $level, string $message, string $file = '', ?int $line = 0): void
{
if ($this->isDeprecation($level)) {
$this->handleDeprecationError($message, $file, $line, $level);
}

if (error_reporting() & $level) {
throw new \ErrorException($message, 0, $level, $file, $line);
}
}

private function handleDeprecationError(string $message, string $file, int $line, int $level): void
{
$this->log($level, $message);
}

public function handleException(\Throwable $th): void
{
self::$reserveMemory = null;

$handler = $this->getHandler();
$handler->report($th);
if (php_sapi_name() !== 'cli') {
$handler->render($this->app['request'], $th)->send();
}
}

public function handleShutdown(): void
{
self::$reserveMemory = null;
$error = error_get_last();
if ($error && $this->isFatal($error['type'])) {
$this->handleException(new \ErrorException($error['message'], 0, $error['type'], $error['file'], $error['line']));
}
}

private function log(int $level, string $message): bool
{
if ($this->app->has('log')) {
$this->app['log']->log($level, $message);

return true;
}

return false;
}

private function getHandler(): Handler
{
return $this->app[Handler::class];
}

private function isDeprecation(int $level): bool
{
return in_array($level, [E_DEPRECATED, E_USER_DEPRECATED]);
}

private function isFatal(int $level): bool
{
return in_array($level, [E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_PARSE]);
}
}
2 changes: 2 additions & 0 deletions src/System/Integrate/Http/Karnel.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use System\Integrate\Application;
use System\Integrate\Bootstrap\BootProviders;
use System\Integrate\Bootstrap\ConfigProviders;
use System\Integrate\Bootstrap\HandleExceptions;
use System\Integrate\Bootstrap\RegisterFacades;
use System\Integrate\Bootstrap\RegisterProviders;
use System\Integrate\Exceptions\Handler;
Expand All @@ -33,6 +34,7 @@ class Karnel
/** @var array<int, class-string> Apllication bootstrap register. */
protected array $bootstrappers = [
ConfigProviders::class,
HandleExceptions::class,
RegisterFacades::class,
RegisterProviders::class,
BootProviders::class,
Expand Down
118 changes: 118 additions & 0 deletions tests/Integrate/Bootstrap/HandleExceptionsTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
<?php

declare(strict_types=1);

namespace System\Integrate\Bootstrap;

use PHPUnit\Framework\Assert;
use PHPUnit\Framework\TestCase;
use System\Http\Request;
use System\Integrate\Application;
use System\Integrate\Exceptions\Handler;

class HandleExceptionsTest extends TestCase
{
/**
* @test
*/
public function itCanHandleError()
{
$app = new Application(dirname(__DIR__) . '/assets/app2');
$app->set('environment', 'testing');

$handle = new HandleExceptions();
$handle->bootstrap($app);

$this->expectException(\ErrorException::class);
$this->expectExceptionMessage(__CLASS__);
$handle->handleError(E_ERROR, __CLASS__, __FILE__, __LINE__);

$app->flush();
}

/**
* @test
*/
public function itCanHandleErrorDeprecation()
{
$app = new Application(dirname(__DIR__) . '/assets/app2');
$app->set('environment', 'testing');
$app->set(Handler::class, fn () => new TestHandleExceptions($app));
$app->set('log', fn () => new TestLog());

$handle = new HandleExceptions();
$handle->bootstrap($app);

$app[Handler::class]->deprecated();
$this->expectException(\ErrorException::class);
$this->expectExceptionMessage('deprecation');
$handle->handleError(E_USER_DEPRECATED, 'deprecation', __FILE__, __LINE__);

$app->flush();
}

/**
* @test
*/
public function itCanHandleException()
{
$app = new Application(dirname(__DIR__) . '/assets/app2');
$app->set('request', fn (): Request => new Request('/'));
$app->set('environment', 'testing');
$app->set(Handler::class, fn () => new TestHandleExceptions($app));

$handle = new HandleExceptions();
$handle->bootstrap($app);

try {
throw new \ErrorException('testing');
} catch (\Throwable $th) {
$handle->handleException($th);
}
$app->flush();
}

/**
* @test
*/
public function itCanHandleShutdown()
{
$this->markTestSkipped('dont how to test, but its work');

$app = new Application(dirname(__DIR__) . '/assets/app2');
$app->set('environment', 'testing');
$app->set(Handler::class, fn () => new TestHandleExceptions($app));

$handle = new HandleExceptions();
$handle->bootstrap($app);
$handle->handleShutdown();

$app->flush();
}
}

class TestHandleExceptions extends Handler
{
public function report(\Throwable $th): void
{
Assert::assertTrue($th->getMessage() === 'testing', 'tesing helper');
}

/**
* Summary of deprecated.
*
* @deprecated message
*/
public function deprecated(): void
{
}
}

class TestLog
{
public function log(int $level, string $message): void
{
Assert::assertEquals($level, 16384);
Assert::assertEquals($message, 'deprecation');
}
}

0 comments on commit 939e0f7

Please sign in to comment.