Browse Source

Added JSONHandler

2.1.0
Dustin Wilson 6 months ago
parent
commit
f2617be261
  1. 1
      lib/Catcher/Handler.php
  2. 68
      lib/Catcher/JSONHandler.php
  3. 108
      tests/cases/TestJSONHandler.php
  4. 14
      tests/cases/TestPlainTextHandler.php

1
lib/Catcher/Handler.php

@ -242,7 +242,6 @@ abstract class Handler {
protected function cleanOutputThrowable(array $outputThrowable): array {
unset($outputThrowable['controller']);
unset($outputThrowable['code']);
if (isset($outputThrowable['previous'])) {
$outputThrowable['previous'] = $this->cleanOutputThrowable($outputThrowable['previous']);

68
lib/Catcher/JSONHandler.php

@ -0,0 +1,68 @@
<?php
/**
* @license MIT
* Copyright 2022 Dustin Wilson, et al.
* See LICENSE and AUTHORS files for details
*/
declare(strict_types=1);
namespace MensBeam\Catcher;
class JSONHandler extends Handler {
public const CONTENT_TYPE = 'application/json';
/** If true the handler will pretty print JSON output; defaults to true */
protected bool $_prettyPrint = 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::OUTPUT) === 0) {
if ($o['code'] & self::LOG) {
$this->serializeOutputThrowable($o);
}
continue;
}
$this->print($this->serializeOutputThrowable($o));
}
}
protected function serializeOutputThrowable(array $outputThrowable, bool $previous = false): array|string {
$controller = $outputThrowable['controller'];
$output = $this->cleanOutputThrowable($outputThrowable);
$jsonFlags = \JSON_THROW_ON_ERROR | \JSON_UNESCAPED_SLASHES | \JSON_UNESCAPED_UNICODE;
if ($this->_prettyPrint) {
$jsonFlags |= \JSON_PRETTY_PRINT;
}
if (isset($outputThrowable['previous'])) {
$output['previous'] = $this->serializeOutputThrowable($outputThrowable['previous'], true);
}
if (!$previous) {
if (isset($outputThrowable['frames']) && is_array($outputThrowable['frames']) && count($outputThrowable['frames']) > 0) {
$output['frames'] = $outputThrowable['frames'];
}
// The log message shouldn't have the timestamp added to it.
if ($outputThrowable['code'] & self::LOG) {
$o = $output;
unset($o['time']);
$this->log($controller->getThrowable(), json_encode($o, $jsonFlags));
}
$output = json_encode($output, $jsonFlags);
}
return $output;
}
}

108
tests/cases/TestJSONHandler.php

@ -0,0 +1,108 @@
<?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,
JSONHandler,
ThrowableController
};
use Psr\Log\LoggerInterface,
Phake;
/** @covers \MensBeam\Catcher\JSONHandler */
class TestJSONHandler extends \PHPUnit\Framework\TestCase {
protected ?Handler $handler = null;
public function setUp(): void {
parent::setUp();
$this->handler = new JSONHandler([
'outputBacktrace' => true,
'silent' => true
]);
}
///** @dataProvider provideHandlingTests */
/*public function testHandling(\Throwable $throwable, int $expectedCode, array $options = []): void {
foreach ($options as $k => $v) {
$this->handler->setOption($k, $v);
}
$o = $this->handler->handle(new ThrowableController($throwable));
$this->assertSame($throwable::class, $o['controller']->getThrowable()::class);
$this->assertEquals($expectedCode, $o['code']);
}*/
/** @dataProvider provideInvocationTests */
public function testInvocation(\Throwable $throwable, bool $silent, bool $log, ?string $logMethodName, 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);
}
$o = $this->handler->handle(new ThrowableController($throwable));
$c = $o['class'] ?? null;
$h = $this->handler;
ob_start();
$h();
$u = ob_get_clean();
if (!$silent) {
$u = json_decode($u, true);
$this->assertEquals($c, $u['class']);
$this->assertEquals(__FILE__, $u['file']);
$this->assertEquals($line, $u['line']);
} else {
$this->assertSame('', $u);
}
if ($log) {
Phake::verify($l, Phake::times(1))->$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' ],
[ new \Error('Ook!'), true, false, null ],
[ new Error('Ook!', \E_ERROR, __FILE__, __LINE__), false, true, 'error' ],
[ new \Exception(message: 'Ook!', previous: new \Error(message: 'Eek!', previous: new \ParseError('Ack!'))), true, true, 'critical' ]
];
$l = count($options);
foreach ($options as $k => $o) {
yield [ ...$o, __LINE__ - 4 - $l + $k ];
}
}
}

14
tests/cases/TestPlainTextHandler.php

@ -11,8 +11,7 @@ use MensBeam\Catcher\{
Error,
Handler,
PlainTextHandler,
ThrowableController,
UnderflowException
ThrowableController
};
use Psr\Log\LoggerInterface,
Phake;
@ -32,17 +31,6 @@ class TestPlainTextHandler extends \PHPUnit\Framework\TestCase {
]);
}
///** @dataProvider provideHandlingTests */
/*public function testHandling(\Throwable $throwable, int $expectedCode, array $options = []): void {
foreach ($options as $k => $v) {
$this->handler->setOption($k, $v);
}
$o = $this->handler->handle(new ThrowableController($throwable));
$this->assertSame($throwable::class, $o['controller']->getThrowable()::class);
$this->assertEquals($expectedCode, $o['code']);
}*/
/** @dataProvider provideInvocationTests */
public function testInvocation(\Throwable $throwable, bool $silent, bool $log, ?string $logMethodName, int $line): void {
$this->handler->setOption('outputToStderr', false);

Loading…
Cancel
Save