-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
598 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,6 +4,11 @@ | |
"name": "aide-enum", | ||
"directory": "src/Enum", | ||
"target": "[email protected]:bakame-php/aide-enum.git" | ||
}, | ||
{ | ||
"name": "aide-error", | ||
"directory": "src/Error", | ||
"target": "[email protected]:bakame-php/aide-error.git" | ||
} | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,7 +9,7 @@ jobs: | |
- uses: frankdejonge/[email protected] | ||
with: | ||
close_pr: 'yes' | ||
target_branch_match: '^(?!master).+$' | ||
target_branch_match: '^(?!main).+$' | ||
message: | | ||
Hi :wave:, | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
* text=auto | ||
|
||
.github export-ignore | ||
.gitattributes export-ignore | ||
README.md export-ignore | ||
**/*Test.php export-ignore |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
github: [nyamsprod] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
on: | ||
schedule: | ||
- cron: '30 7 * * *' | ||
jobs: | ||
close_subsplit_prs: | ||
runs-on: ubuntu-latest | ||
name: Close sub-split PRs | ||
steps: | ||
- uses: frankdejonge/[email protected] | ||
with: | ||
close_pr: 'yes' | ||
target_branch_match: '^(?!main).+$' | ||
message: | | ||
Hi :wave:, | ||
Thank you for contributing to Aide. Unfortunately, you've sent a PR to a read-only sub-split repository. | ||
All pull requests should be directed towards: https://github.com/bakame-php/aide |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,184 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Bakame\Aide\Error; | ||
|
||
use Closure; | ||
use ErrorException; | ||
|
||
use const E_ALL; | ||
use const E_DEPRECATED; | ||
use const E_NOTICE; | ||
use const E_STRICT; | ||
use const E_USER_DEPRECATED; | ||
use const E_USER_NOTICE; | ||
use const E_USER_WARNING; | ||
use const E_WARNING; | ||
|
||
class Cloak | ||
{ | ||
public const FORCE_NOTHING = 0; | ||
public const SILENCE_ERROR = 1; | ||
public const THROW_ON_ERROR = 2; | ||
|
||
protected static bool $useException = false; | ||
protected ?ErrorException $exception = null; | ||
|
||
public function __construct( | ||
protected readonly Closure $closure, | ||
public readonly int $errorLevel = E_WARNING, | ||
public readonly int $behaviour = self::FORCE_NOTHING | ||
) { | ||
} | ||
|
||
public static function throwOnError(): void | ||
{ | ||
self::$useException = true; | ||
} | ||
|
||
public static function silenceError(): void | ||
{ | ||
self::$useException = false; | ||
} | ||
|
||
public static function warning(Closure $closure, int $behaviour = self::FORCE_NOTHING): self | ||
{ | ||
return new self($closure, E_WARNING, $behaviour); | ||
} | ||
|
||
public static function notice(Closure $closure, int $behaviour = self::FORCE_NOTHING): self | ||
{ | ||
return new self($closure, E_NOTICE, $behaviour); | ||
} | ||
|
||
public static function deprecated(Closure $closure, int $behaviour = self::FORCE_NOTHING): self | ||
{ | ||
return new self($closure, E_DEPRECATED, $behaviour); | ||
} | ||
|
||
public static function strict(Closure $closure, int $behaviour = self::FORCE_NOTHING): self | ||
{ | ||
return new self($closure, E_STRICT, $behaviour); | ||
} | ||
|
||
public static function userWarning(Closure $closure, int $behaviour = self::FORCE_NOTHING): self | ||
{ | ||
return new self($closure, E_USER_WARNING, $behaviour); | ||
} | ||
|
||
public static function userNotice(Closure $closure, int $behaviour = self::FORCE_NOTHING): self | ||
{ | ||
return new self($closure, E_USER_NOTICE, $behaviour); | ||
} | ||
|
||
public static function userDeprecated(Closure $closure, int $behaviour = self::FORCE_NOTHING): self | ||
{ | ||
return new self($closure, E_USER_DEPRECATED, $behaviour); | ||
} | ||
|
||
public static function all(Closure $closure, int $behaviour = self::FORCE_NOTHING): self | ||
{ | ||
return new self($closure, E_ALL, $behaviour); | ||
} | ||
|
||
/** | ||
* @throws ErrorException | ||
*/ | ||
public function __invoke(mixed ...$arguments): mixed | ||
{ | ||
$errorHandler = function (int $errno, string $errstr, string $errfile, int $errline): bool { | ||
if (0 === (error_reporting() & $errno)) { | ||
return false; | ||
} | ||
|
||
$this->exception = new ErrorException($errstr, 0, $errno, $errfile, $errline); | ||
|
||
return true; | ||
}; | ||
|
||
$this->exception = null; | ||
set_error_handler($errorHandler, $this->errorLevel); | ||
$result = ($this->closure)(...$arguments); | ||
restore_error_handler(); | ||
|
||
if (null === $this->exception) { /* @phpstan-ignore-line */ | ||
return $result; | ||
} | ||
|
||
if (self::THROW_ON_ERROR === $this->behaviour) { /* @phpstan-ignore-line */ | ||
throw $this->exception; | ||
} | ||
|
||
if (self::SILENCE_ERROR === $this->behaviour) { | ||
return $result; | ||
} | ||
|
||
if (true === self::$useException) { | ||
throw $this->exception; | ||
} | ||
|
||
return $result; | ||
} | ||
|
||
public function lastError(): ?ErrorException | ||
{ | ||
return $this->exception; | ||
} | ||
|
||
public function errorsAreSilenced(): bool | ||
{ | ||
return !$this->errorsAreThrown(); | ||
} | ||
|
||
public function errorsAreThrown(): bool | ||
{ | ||
return self::THROW_ON_ERROR === $this->behaviour | ||
|| (self::SILENCE_ERROR !== $this->behaviour && true === self::$useException); | ||
} | ||
|
||
public function suppressAll(): bool | ||
{ | ||
return $this->suppress(E_ALL); | ||
} | ||
|
||
public function suppressWarning(): bool | ||
{ | ||
return $this->suppress(E_WARNING); | ||
} | ||
|
||
public function suppressNotice(): bool | ||
{ | ||
return $this->suppress(E_NOTICE); | ||
} | ||
|
||
public function suppressDeprecated(): bool | ||
{ | ||
return $this->suppress(E_DEPRECATED); | ||
} | ||
|
||
public function suppressStrict(): bool | ||
{ | ||
return $this->suppress(E_STRICT); | ||
} | ||
|
||
public function suppressUserWarning(): bool | ||
{ | ||
return $this->suppress(E_USER_WARNING); | ||
} | ||
|
||
public function suppressUserNotice(): bool | ||
{ | ||
return $this->suppress(E_USER_NOTICE); | ||
} | ||
|
||
public function suppressUserDeprecated(): bool | ||
{ | ||
return $this->suppress(E_USER_DEPRECATED); | ||
} | ||
|
||
public function suppress(int $errorLevel): bool | ||
{ | ||
return 0 !== ($errorLevel & $this->errorLevel); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Bakame\Aide\Error; | ||
|
||
use ErrorException; | ||
use PHPUnit\Framework\Attributes\Test; | ||
use PHPUnit\Framework\TestCase; | ||
|
||
use const E_DEPRECATED; | ||
use const E_WARNING; | ||
|
||
final class CloakTest extends TestCase | ||
{ | ||
protected function setUp(): void | ||
{ | ||
parent::setUp(); | ||
|
||
Cloak::silenceError(); | ||
} | ||
|
||
#[Test] | ||
public function it_returns_information_about_its_error_reporting_level(): void | ||
{ | ||
$lambda = Cloak::warning(touch(...)); | ||
$res = $lambda('/foo'); | ||
|
||
self::assertFalse($res); | ||
self::assertSame(E_WARNING, $lambda->errorLevel); | ||
self::assertTrue($lambda->suppressWarning()); | ||
self::assertFalse($lambda->suppressNotice()); | ||
self::assertInstanceOf(ErrorException::class, $lambda->lastError()); | ||
} | ||
|
||
#[Test] | ||
public function it_will_suppress_nothing_in_case_of_success(): void | ||
{ | ||
$lambda = Cloak::userWarning(strtoupper(...)); | ||
$res = $lambda('foo'); | ||
|
||
self::assertSame('FOO', $res); | ||
self::assertNull($lambda->lastError()); | ||
} | ||
|
||
public function testGetErrorReporting(): void | ||
{ | ||
$lambda = Cloak::deprecated(strtoupper(...)); | ||
|
||
self::assertSame(E_DEPRECATED, $lambda->errorLevel); | ||
} | ||
|
||
public function testCapturesTriggeredError(): void | ||
{ | ||
$lambda = Cloak::all(trigger_error(...)); | ||
$lambda('foo'); | ||
|
||
self::assertSame('foo', $lambda->lastError()?->getMessage()); | ||
} | ||
|
||
public function testCapturesSilencedError(): void | ||
{ | ||
$lambda = Cloak::notice(function (string $x) { | ||
@trigger_error($x); | ||
}); | ||
$lambda('foo'); | ||
|
||
self::assertNull($lambda->lastError()); | ||
} | ||
|
||
public function testObjectDoesntInteractWithExistingErrorHandlers(): void | ||
{ | ||
$count = 0; | ||
set_error_handler(function (int $errno, string $errstr, string $errfile, int $errline) use (&$count): bool { | ||
$count++; | ||
|
||
return true; | ||
}); | ||
trigger_error('foo'); | ||
self::assertSame(1, $count); | ||
|
||
$lambda = Cloak::warning(trigger_error(...)); | ||
$lambda('foo'); | ||
self::assertSame(1, $count); /* @phpstan-ignore-line */ | ||
|
||
trigger_error('foo'); | ||
self::assertSame(2, $count); /* @phpstan-ignore-line */ | ||
} | ||
|
||
public function testErrorTransformedIntoARuntimeException(): void | ||
{ | ||
$this->expectException(ErrorException::class); | ||
|
||
Cloak::throwOnError(); | ||
$touch = Cloak::warning(touch(...)); | ||
$touch('/foo'); | ||
} | ||
|
||
public function testErrorTransformedIntoAnInvalidArgumentException(): void | ||
{ | ||
Cloak::throwOnError(); | ||
$this->expectException(ErrorException::class); | ||
|
||
$touch = Cloak::all(touch(...)); | ||
$touch('/foo'); | ||
} | ||
|
||
public function testSpecificBehaviourOverrideGeneralErrorSetting(): void | ||
{ | ||
Cloak::throwOnError(); | ||
|
||
$touch = Cloak::all(touch(...), Cloak::SILENCE_ERROR); | ||
$touch('/foo'); | ||
|
||
self::assertInstanceOf(ErrorException::class, $touch->lastError()); | ||
} | ||
|
||
public function testCaptureNothingThrowNoException(): void | ||
{ | ||
Cloak::throwOnError(); | ||
$strtoupper = Cloak::strict(strtoupper(...)); | ||
|
||
self::assertSame('FOO', $strtoupper('foo')); | ||
} | ||
|
||
#[Test] | ||
public function it_can_detect_the_level_to_suppress(): void | ||
{ | ||
$touch = new Cloak( | ||
touch(...), | ||
E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED, | ||
Cloak::THROW_ON_ERROR | ||
); | ||
|
||
self::assertTrue($touch->suppressAll()); | ||
self::assertFalse($touch->suppressStrict()); | ||
self::assertFalse($touch->suppressDeprecated()); | ||
self::assertFalse($touch->suppressNotice()); | ||
self::assertTrue($touch->suppressUserNotice()); | ||
self::assertTrue($touch->suppressUserDeprecated()); | ||
self::assertTrue($touch->suppressUserWarning()); | ||
self::assertTrue($touch->errorsAreThrown()); | ||
self::assertFalse($touch->errorsAreSilenced()); | ||
} | ||
} |
Oops, something went wrong.