Dustin Wilson
7 months ago
commit
7e912be190
15 changed files with 3393 additions and 0 deletions
@ -0,0 +1,77 @@ |
|||||
|
# Catcher-specific |
||||
|
/test*.html |
||||
|
/test*.php |
||||
|
/test/ |
||||
|
/test/* |
||||
|
|
||||
|
# General |
||||
|
*.DS_Store |
||||
|
.AppleDouble |
||||
|
.LSOverride |
||||
|
|
||||
|
# Icon must end with two \r |
||||
|
Icon |
||||
|
|
||||
|
|
||||
|
# Thumbnails |
||||
|
._* |
||||
|
|
||||
|
# Files that might appear in the root of a volume |
||||
|
.DocumentRevisions-V100 |
||||
|
.fseventsd |
||||
|
.Spotlight-V100 |
||||
|
.TemporaryItems |
||||
|
.Trashes |
||||
|
.VolumeIcon.icns |
||||
|
.com.apple.timemachine.donotpresent |
||||
|
|
||||
|
# Directories potentially created on remote AFP share |
||||
|
.AppleDB |
||||
|
.AppleDesktop |
||||
|
Network Trash Folder |
||||
|
Temporary Items |
||||
|
.apdisk |
||||
|
|
||||
|
# Windows thumbnail cache files |
||||
|
Thumbs.db |
||||
|
ehthumbs.db |
||||
|
ehthumbs_vista.db |
||||
|
|
||||
|
# Dump file |
||||
|
*.stackdump |
||||
|
|
||||
|
# Folder config file |
||||
|
Desktop.ini |
||||
|
|
||||
|
# Recycle Bin used on file shares |
||||
|
$RECYCLE.BIN/ |
||||
|
|
||||
|
# Windows Installer files |
||||
|
*.cab |
||||
|
*.msi |
||||
|
*.msm |
||||
|
*.msp |
||||
|
|
||||
|
# Windows shortcuts |
||||
|
*.lnk |
||||
|
|
||||
|
*~ |
||||
|
|
||||
|
# temporary files which can be created if a process still has a handle open of a deleted file |
||||
|
.fuse_hidden* |
||||
|
|
||||
|
# KDE directory preferences |
||||
|
.directory |
||||
|
|
||||
|
# Linux trash folder which might appear on any partition or disk |
||||
|
.Trash-* |
||||
|
|
||||
|
# .nfs files are created when an open file is removed but is still being accessed |
||||
|
.nfs* |
||||
|
|
||||
|
/vendor/ |
||||
|
/vendor-bin/*/vendor |
||||
|
/tests/html5lib-tests |
||||
|
/tests/.phpunit.cache |
||||
|
/tests/coverage |
||||
|
cachegrind.out.* |
@ -0,0 +1,4 @@ |
|||||
|
Project leads |
||||
|
------------- |
||||
|
Dustin Wilson https://dustinwilson.com/ |
||||
|
J. King https://jkingweb.ca/ |
@ -0,0 +1,22 @@ |
|||||
|
Copyright (c) 2022 Dustin Wilson, J. King |
||||
|
|
||||
|
Permission is hereby granted, free of charge, to any person |
||||
|
obtaining a copy of this software and associated documentation |
||||
|
files (the "Software"), to deal in the Software without |
||||
|
restriction, including without limitation the rights to use, |
||||
|
copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
|
copies of the Software, and to permit persons to whom the |
||||
|
Software is furnished to do so, subject to the following |
||||
|
conditions: |
||||
|
|
||||
|
The above copyright notice and this permission notice shall be |
||||
|
included in all copies or substantial portions of the Software. |
||||
|
|
||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES |
||||
|
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT |
||||
|
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, |
||||
|
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
||||
|
OTHER DEALINGS IN THE SOFTWARE. |
@ -0,0 +1,87 @@ |
|||||
|
[a]: https://code.mensbeam.com/MensBeam/Catcher |
||||
|
[b]: https://packagist.org/packages/aws/aws-sdk-php |
||||
|
[c]: https://github.com/symfony/yaml |
||||
|
[d]: https://www.php.net/manual/en/function.pcntl-fork.php |
||||
|
[e]: https://www.php.net/manual/en/function.print-r.php |
||||
|
[f]: https://github.com/symfony/var-exporter |
||||
|
[g]: https://github.com/php-fig/log |
||||
|
|
||||
|
# AWSSNSHandler # |
||||
|
|
||||
|
_AWSSNSHandler_ is a Throwable handler for use in [_Catcher_][a], a Throwable and error handling library for PHP. It sends throwables and errors to Amazon SNS topics. Right now _AWSSNSHandler_ only supports sending to email topics. SMS messages aren't supported at this time. |
||||
|
|
||||
|
|
||||
|
## Requirements ## |
||||
|
|
||||
|
* PHP >= 8.1 |
||||
|
* [mensbeam/catcher][a] ^2.1.0 |
||||
|
* [aws/aws-sdk-php][b] ^3.283 |
||||
|
|
||||
|
|
||||
|
## Installation ## |
||||
|
|
||||
|
```shell |
||||
|
composer require mensbeam/catcher-awssnshandler |
||||
|
``` |
||||
|
|
||||
|
|
||||
|
## Usage ## |
||||
|
|
||||
|
For most use cases this library requires no configuration and little effort to integrate into non-complex environments: |
||||
|
|
||||
|
```php |
||||
|
use MensBeam\Catcher, |
||||
|
MensBeam\Catcher\AWSSNSHandler, |
||||
|
Aws\Sns\SnsClient; |
||||
|
|
||||
|
$client = new SnsClient([ |
||||
|
'version' => 'latest', |
||||
|
'region' => 'us-west-2', |
||||
|
'credentials' => [ |
||||
|
'key' => 'AKIAFIMBZAFZZQL42RMH', |
||||
|
'secret' => 'qZsoLN4aZ0PzCVMEZ68M1aSA6lsa5D3V5v5LApPK' |
||||
|
] |
||||
|
]); |
||||
|
$catcher = new Catcher(new AWSSNSHandler($client, 'arn:aws:sns:us-west-2:701867229025:ook_eek')); |
||||
|
``` |
||||
|
|
||||
|
That's it. It will automatically register Catcher as an exception, error, and shutdown handler and use `AWSSNSHandler` as its sole handler. Like other _Catcher_ handlers, _AWSSNSHandler_ can be configured with a logger. When logging it behaves identically to _JSONHandler_. See the [_Catcher_][a] documentation for more info on how to configure a logger. |
||||
|
|
||||
|
## Documentation ## |
||||
|
|
||||
|
### MensBeam\Catcher\AWSSNSHandler ### |
||||
|
|
||||
|
```php |
||||
|
namespace MensBeam\Catcher; |
||||
|
use Aws\Sns\SnsClient; |
||||
|
|
||||
|
|
||||
|
class AWSSNSHandler extends JSONHandler { |
||||
|
protected SnsClient $client; |
||||
|
protected string $topicARN; |
||||
|
|
||||
|
|
||||
|
public function __construct(SnsClient $client, string $topicARN, array $options = []); |
||||
|
|
||||
|
public function getClient(): SnsClient; |
||||
|
public function setClient(SnsClient $client): void; |
||||
|
public function getTopicARN(): string; |
||||
|
public function setTopicARN(string $topicARN): void; |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
#### MensBeam\Catcher\AWSSNSHandler::getClient #### |
||||
|
|
||||
|
Returns the `Aws\Sns\SnsClient` the handler uses |
||||
|
|
||||
|
#### MensBeam\Catcher\AWSSNSHandler::getTopicARN #### |
||||
|
|
||||
|
Returns the AWS SNS topic ARN the handler sends messages to |
||||
|
|
||||
|
#### MensBeam\Catcher\AWSSNSHandler::setClient #### |
||||
|
|
||||
|
Replaces the `Aws\Sns\SnsClient` the handler uses with one specified |
||||
|
|
||||
|
#### MensBeam\Catcher\AWSSNSHandler::setTopicARN #### |
||||
|
|
||||
|
Replaces the AWS SNS topic ARN the handler sends messages to with one specified |
@ -0,0 +1,32 @@ |
|||||
|
{ |
||||
|
"name": "mensbeam/catcher-awsnshandler", |
||||
|
"description": "Amazon AWS SNS Message Handler for MensBeam's Catcher", |
||||
|
"type": "library", |
||||
|
"license": "MIT", |
||||
|
"autoload": { |
||||
|
"psr-4": { |
||||
|
"MensBeam\\Catcher\\": "lib/" |
||||
|
} |
||||
|
}, |
||||
|
"autoload-dev": { |
||||
|
"psr-4": { |
||||
|
"MensBeam\\Catcher\\AWSSNSHandler\\Test\\": "tests/lib/" |
||||
|
} |
||||
|
}, |
||||
|
"authors": [ |
||||
|
{ |
||||
|
"name": "Dustin Wilson", |
||||
|
"email": "dustin@dustinwilson.com" |
||||
|
} |
||||
|
], |
||||
|
"require": { |
||||
|
"php": ">=8.1", |
||||
|
"mensbeam/catcher": "^2.1.2", |
||||
|
"aws/aws-sdk-php": "^3.283" |
||||
|
}, |
||||
|
"require-dev": { |
||||
|
"phpunit/phpunit": "^10", |
||||
|
"phake/phake": "^4.4", |
||||
|
"psr/log": "^3.0" |
||||
|
} |
||||
|
} |
File diff suppressed because it is too large
@ -0,0 +1,66 @@ |
|||||
|
<?php |
||||
|
/** |
||||
|
* @license MIT |
||||
|
* Copyright 2022 Dustin Wilson, et al. |
||||
|
* See LICENSE and AUTHORS files for details |
||||
|
*/ |
||||
|
|
||||
|
declare(strict_types=1); |
||||
|
namespace MensBeam\Catcher; |
||||
|
use Aws\Sns\SnsClient; |
||||
|
|
||||
|
|
||||
|
class AWSSNSHandler extends JSONHandler { |
||||
|
protected SnsClient $client; |
||||
|
protected string $topicARN; |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
public function __construct(SnsClient $client, string $topicARN, array $options = []) { |
||||
|
$this->client = $client; |
||||
|
$this->topicARN = $topicARN; |
||||
|
parent::__construct($options); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
public function getClient(): SnsClient { |
||||
|
return $this->client; |
||||
|
} |
||||
|
|
||||
|
public function getTopicARN(): string { |
||||
|
return $this->topicARN; |
||||
|
} |
||||
|
|
||||
|
public function setClient(SnsClient $client): void { |
||||
|
$this->client = $client; |
||||
|
} |
||||
|
|
||||
|
public function setTopicARN(string $topicARN): void { |
||||
|
$this->topicARN = $topicARN; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
protected function handleCallback(array $output): array { |
||||
|
return $output; |
||||
|
} |
||||
|
|
||||
|
protected function invokeCallback(): void { |
||||
|
foreach ($this->outputBuffer as $o) { |
||||
|
if (($o['code'] & self::OUTPUT) === 0) { |
||||
|
if ($o['code'] & self::LOG) { |
||||
|
$this->serializeOutputThrowable($o); |
||||
|
} |
||||
|
|
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
$this->client->publish([ |
||||
|
'Message' => $this->serializeOutputThrowable($o), |
||||
|
'TopicArn' => $this->topicARN |
||||
|
]); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,38 @@ |
|||||
|
#!/usr/bin/env php |
||||
|
<?php |
||||
|
/** |
||||
|
* @license MIT |
||||
|
* Copyright 2022 Dustin Wilson, et al. |
||||
|
* See LICENSE and AUTHORS files for details |
||||
|
*/ |
||||
|
|
||||
|
$dir = ini_get('extension_dir'); |
||||
|
$php = escapeshellarg(\PHP_BINARY); |
||||
|
$code = escapeshellarg(__DIR__ . '/lib'); |
||||
|
|
||||
|
|
||||
|
array_shift($argv); |
||||
|
foreach ($argv as $k => $v) { |
||||
|
if (in_array($v, ['--coverage', '--coverage-html'])) { |
||||
|
$argv[$k] = '--coverage-html tests/coverage'; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
$cmd = [ |
||||
|
$php, |
||||
|
'-d opcache.enable_cli=0', |
||||
|
]; |
||||
|
|
||||
|
if (!extension_loaded('xdebug')) { |
||||
|
$cmd[] = '-d zend_extension=xdebug.so'; |
||||
|
} |
||||
|
|
||||
|
$cmd = implode(' ', [ |
||||
|
...$cmd, |
||||
|
'-d xdebug.mode=coverage,develop,trace', |
||||
|
escapeshellarg(__DIR__ . '/vendor/bin/phpunit'), |
||||
|
'--configuration tests/phpunit.xml', |
||||
|
...$argv, |
||||
|
'--display-deprecations' |
||||
|
]); |
||||
|
passthru($cmd); |
@ -0,0 +1,18 @@ |
|||||
|
<?php |
||||
|
declare(strict_types=1); |
||||
|
namespace MensBeam\Catcher\Test; |
||||
|
|
||||
|
ini_set('memory_limit', '2G'); |
||||
|
ini_set('zend.assertions', '1'); |
||||
|
ini_set('assert.exception', 'true'); |
||||
|
error_reporting(\E_ALL); |
||||
|
define('CWD', dirname(__DIR__)); |
||||
|
require_once CWD . '/vendor/autoload.php'; |
||||
|
|
||||
|
if (function_exists('xdebug_set_filter')) { |
||||
|
if (defined('XDEBUG_PATH_INCLUDE')) { |
||||
|
xdebug_set_filter(\XDEBUG_FILTER_CODE_COVERAGE, \XDEBUG_PATH_INCLUDE, [ CWD . '/lib/' ]); |
||||
|
} else { |
||||
|
xdebug_set_filter(\XDEBUG_FILTER_CODE_COVERAGE, \XDEBUG_PATH_WHITELIST, [ CWD . '/lib/' ]); |
||||
|
} |
||||
|
} |
@ -0,0 +1,126 @@ |
|||||
|
<?php |
||||
|
/** |
||||
|
* @license MIT |
||||
|
* Copyright 2022 Dustin Wilson, et al. |
||||
|
* See LICENSE and AUTHORS files for details |
||||
|
*/ |
||||
|
|
||||
|
declare(strict_types=1); |
||||
|
namespace MensBeam\Catcher\Test; |
||||
|
use MensBeam\Catcher\{ |
||||
|
Error, |
||||
|
Handler, |
||||
|
ThrowableController, |
||||
|
AWSSNSHandler |
||||
|
}; |
||||
|
use Aws\Sns\SnsClient, |
||||
|
Psr\Log\LoggerInterface, |
||||
|
Phake; |
||||
|
|
||||
|
|
||||
|
/** @covers \MensBeam\Catcher\AWSSNSHandler */ |
||||
|
class TestAWSSNSHandler extends \PHPUnit\Framework\TestCase { |
||||
|
protected ?SnsClient $client = null; |
||||
|
protected ?Handler $handler = null; |
||||
|
|
||||
|
|
||||
|
public function setUp(): void { |
||||
|
parent::setUp(); |
||||
|
|
||||
|
$this->client = Phake::mock(SnsClient::class); |
||||
|
Phake::when($this->client)->publish()->thenReturn(true); |
||||
|
|
||||
|
$this->handler = new AWSSNSHandler($this->client, 'ook', [ |
||||
|
'outputBacktrace' => true, |
||||
|
'silent' => true |
||||
|
]); |
||||
|
} |
||||
|
|
||||
|
public function testGettingSettingProps() { |
||||
|
$c = $this->handler->getClient(); |
||||
|
$this->assertTrue($c instanceof SnsClient); |
||||
|
$c2 = Phake::mock(SnsClient::class); |
||||
|
Phake::when($c2)->publish()->thenReturn(true); |
||||
|
$this->handler->setClient($c2); |
||||
|
$this->assertNotEquals($c, $this->handler->getClient()); |
||||
|
$this->assertTrue($this->handler->getClient() instanceof SnsClient); |
||||
|
|
||||
|
$this->assertSame('ook', $this->handler->getTopicARN()); |
||||
|
$this->handler->setTopicARN('eek'); |
||||
|
$this->assertSame('eek', $this->handler->getTopicARN()); |
||||
|
} |
||||
|
|
||||
|
/** @dataProvider provideInvocationTests */ |
||||
|
public function testInvocation(\Throwable $throwable, bool $silent, bool $log, ?string $logMethodName, ?array $ignore, int $line): void { |
||||
|
$this->handler->setOption('outputToStderr', false); |
||||
|
|
||||
|
if (!$silent) { |
||||
|
$this->handler->setOption('silent', false); |
||||
|
} |
||||
|
if ($log) { |
||||
|
$l = Phake::mock(LoggerInterface::class); |
||||
|
$this->handler->setOption('logger', $l); |
||||
|
} |
||||
|
if ($ignore !== null) { |
||||
|
$this->handler->setOption('ignore', $ignore); |
||||
|
} |
||||
|
|
||||
|
$o = $this->handler->handle(new ThrowableController($throwable)); |
||||
|
$c = $o['class'] ?? null; |
||||
|
|
||||
|
$h = $this->handler; |
||||
|
$h(); |
||||
|
$u = $h->getLastOutputThrowable(); |
||||
|
|
||||
|
if ($ignore === null) { |
||||
|
$this->assertNotNull($u); |
||||
|
$this->assertEquals($c, $u['class']); |
||||
|
$this->assertEquals(__FILE__, $u['file']); |
||||
|
$this->assertEquals($line, $u['line']); |
||||
|
|
||||
|
if (!$silent) { |
||||
|
Phake::verify($this->client, Phake::times(1))->publish; |
||||
|
} |
||||
|
} else { |
||||
|
$this->assertNull($u); |
||||
|
} |
||||
|
|
||||
|
if ($log) { |
||||
|
Phake::verify($l, Phake::times((int)(count($ignore ?? []) === 0)))->$logMethodName; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
public static function provideHandlingTests(): iterable { |
||||
|
$options = [ |
||||
|
[ new \Exception('Ook!'), Handler::BUBBLES | Handler::EXIT, [ 'forceExit' => true ] ], |
||||
|
[ new \Error('Ook!'), Handler::BUBBLES ], |
||||
|
[ new Error('Ook!', \E_ERROR, '/dev/null', 42, new \Error('Eek!')), Handler::BUBBLES | Handler::NOW, [ 'forceOutputNow' => true ] ], |
||||
|
[ new \Exception('Ook!'), Handler::BUBBLES, [ 'logger' => Phake::mock(LoggerInterface::class), 'logWhenSilent' => false ] ], |
||||
|
[ new \Error('Ook!'), Handler::BUBBLES | Handler::LOG, [ 'forceOutputNow' => true, 'logger' => Phake::mock(LoggerInterface::class) ] ] |
||||
|
]; |
||||
|
|
||||
|
foreach ($options as $o) { |
||||
|
$o[1] |= Handler::NOW; |
||||
|
yield $o; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static function provideInvocationTests(): iterable { |
||||
|
$options = [ |
||||
|
[ new \Exception('Ook!'), false, true, 'critical', null ], |
||||
|
[ new \Exception('Ook!'), false, true, 'critical', [ \Exception::class ] ], |
||||
|
[ new \Error('Ook!'), true, false, null, null ], |
||||
|
[ new \Error('Ook!'), true, false, null, [ \Error::class ] ], |
||||
|
[ new Error('Ook!', \E_ERROR, __FILE__, __LINE__), false, true, 'error', null ], |
||||
|
[ new Error('Ook!', \E_ERROR, __FILE__, __LINE__), false, true, 'error', [ \E_ERROR ] ], |
||||
|
[ new \Exception(message: 'Ook!', previous: new \Error(message: 'Eek!', previous: new \ParseError('Ack!'))), true, true, 'critical', null ], |
||||
|
[ new \Exception(message: 'Ook!', previous: new \Error(message: 'Eek!', previous: new \ParseError('Ack!'))), true, true, 'critical', [ \Exception::class ] ] |
||||
|
]; |
||||
|
|
||||
|
$l = count($options); |
||||
|
foreach ($options as $k => $o) { |
||||
|
yield [ ...$o, __LINE__ - 4 - $l + $k ]; |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,11 @@ |
|||||
|
<?php |
||||
|
/** |
||||
|
* @license MIT |
||||
|
* Copyright 2022 Dustin Wilson, et al. |
||||
|
* See LICENSE and AUTHORS files for details |
||||
|
*/ |
||||
|
|
||||
|
declare(strict_types=1); |
||||
|
namespace MensBeam\Catcher\Test; |
||||
|
|
||||
|
class Error extends \Error {} |
@ -0,0 +1,31 @@ |
|||||
|
<?php |
||||
|
/** |
||||
|
* @license MIT |
||||
|
* Copyright 2022 Dustin Wilson, et al. |
||||
|
* See LICENSE and AUTHORS files for details |
||||
|
*/ |
||||
|
|
||||
|
declare(strict_types=1); |
||||
|
namespace MensBeam\Catcher\Test; |
||||
|
|
||||
|
|
||||
|
class ErrorHandlingTestCase extends \PHPUnit\Framework\TestCase { |
||||
|
protected ?Error $lastError = null; |
||||
|
|
||||
|
|
||||
|
public function setUp(): void { |
||||
|
set_error_handler([ $this, 'handleError' ]); |
||||
|
} |
||||
|
|
||||
|
public function tearDown(): void { |
||||
|
restore_error_handler(); |
||||
|
} |
||||
|
|
||||
|
public function handleError(int $code, string $message, string $file, int $line): void { |
||||
|
$e = new Error($message, $code); |
||||
|
$this->lastError = $e; |
||||
|
if (in_array($code, [ \E_ERROR, \E_PARSE, \E_CORE_ERROR, \E_COMPILE_ERROR, \E_USER_ERROR, \E_RECOVERABLE_ERROR ])) { |
||||
|
throw $e; |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,29 @@ |
|||||
|
<?php |
||||
|
/** |
||||
|
* @license MIT |
||||
|
* Copyright 2022 Dustin Wilson, et al. |
||||
|
* See LICENSE and AUTHORS files for details |
||||
|
*/ |
||||
|
|
||||
|
declare(strict_types=1); |
||||
|
namespace MensBeam\Catcher\Test; |
||||
|
use org\bovigo\vfs\vfsStream, |
||||
|
Psr\Log\LoggerInterface, |
||||
|
Psr\Log\LoggerTrait; |
||||
|
|
||||
|
|
||||
|
class FailLogger implements LoggerInterface { |
||||
|
use LoggerTrait; |
||||
|
|
||||
|
public function log($level, string|\Stringable $message, array $context = []): void { |
||||
|
$v = vfsStream::setup('ook'); |
||||
|
$d = vfsStream::newDirectory('ook', 0777)->at($v); |
||||
|
file_put_contents($d->url(), $message); |
||||
|
|
||||
|
echo "Ook!\n"; |
||||
|
} |
||||
|
|
||||
|
public function error(string|\Stringable $message, array $context = []): void { |
||||
|
trigger_error('Ook!', \E_USER_ERROR); |
||||
|
} |
||||
|
} |
@ -0,0 +1,66 @@ |
|||||
|
<?php |
||||
|
/** |
||||
|
* @license MIT |
||||
|
* Copyright 2022 Dustin Wilson, et al. |
||||
|
* See LICENSE and AUTHORS files for details |
||||
|
*/ |
||||
|
|
||||
|
declare(strict_types=1); |
||||
|
namespace MensBeam\Catcher\Test; |
||||
|
use MensBeam\Catcher\Handler; |
||||
|
|
||||
|
|
||||
|
class TestingHandler extends Handler { |
||||
|
public array $output = []; |
||||
|
|
||||
|
protected ?string $_name = null; |
||||
|
// Could just use silent option instead, but we need to test Handler::SILENT |
||||
|
protected bool $_print = false; |
||||
|
// When printing output, print it as JSON |
||||
|
protected bool $_printJSON = true; |
||||
|
|
||||
|
|
||||
|
protected function handleCallback(array $output): array { |
||||
|
$output['code'] = (\PHP_SAPI === 'cli') ? $output['code'] | self::NOW : $output['code']; |
||||
|
return $output; |
||||
|
} |
||||
|
|
||||
|
protected function invokeCallback(): void { |
||||
|
foreach ($this->outputBuffer as $o) { |
||||
|
if ($o['code'] & self::LOG) { |
||||
|
$this->log($o['controller']->getThrowable(), json_encode([ |
||||
|
'class' => $o['class'], |
||||
|
'code' => $o['code'], |
||||
|
'file' => $o['file'], |
||||
|
'line' => $o['line'], |
||||
|
'message' => $o['message'] |
||||
|
])); |
||||
|
} |
||||
|
|
||||
|
if (($o['code'] & self::OUTPUT) === 0) { |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
$o = $this->cleanOutputThrowable($o); |
||||
|
|
||||
|
if ($this->_print) { |
||||
|
if (!$this->_printJSON) { |
||||
|
$oo = ''; |
||||
|
foreach ($o['frames'] as $f) { |
||||
|
if (!isset($f['args'])) { |
||||
|
continue; |
||||
|
} |
||||
|
$oo .= $this->serializeArgs($f['args']) . "\n"; |
||||
|
} |
||||
|
$oo = rtrim($oo); |
||||
|
} else { |
||||
|
$oo = json_encode($o, \JSON_THROW_ON_ERROR); |
||||
|
} |
||||
|
|
||||
|
$this->print($oo); |
||||
|
} |
||||
|
|
||||
|
$this->output[] = $o; |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,22 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.2/phpunit.xsd" |
||||
|
beStrictAboutOutputDuringTests="true" |
||||
|
beStrictAboutTestsThatDoNotTestAnything="true" |
||||
|
bootstrap="bootstrap.php" |
||||
|
cacheDirectory=".phpunit.cache" |
||||
|
colors="true" |
||||
|
executionOrder="defects" |
||||
|
requireCoverageMetadata="true" |
||||
|
> |
||||
|
<testsuites> |
||||
|
<testsuite name="Main"> |
||||
|
<directory prefix="Test" suffix=".php">./cases</directory> |
||||
|
</testsuite> |
||||
|
</testsuites> |
||||
|
<coverage/> |
||||
|
<source> |
||||
|
<include> |
||||
|
<directory suffix=".php">../lib</directory> |
||||
|
</include> |
||||
|
</source> |
||||
|
</phpunit> |
Loading…
Reference in new issue