Added annotation ExceptionHandler. (#1245)

Co-authored-by: ccb <ccb412727@qq.com>
Co-authored-by: 李铭昕 <715557344@qq.com>
This commit is contained in:
ccb1900 2020-06-18 23:54:04 -05:00 committed by GitHub
parent a6073bf151
commit bf859acbad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 237 additions and 0 deletions

View File

@ -39,6 +39,8 @@
## Added
- [#1245](https://github.com/hyperf/hyperf/pull/1245) Added Annotation `ExceptionHandler`.
- [#1245](https://github.com/hyperf/hyperf/pull/1245) Exception handler's config and annotation support priority.
- [#1819](https://github.com/hyperf/hyperf/pull/1819) Added `hyperf/signal` component.
- [#1844](https://github.com/hyperf/hyperf/pull/1844) Support type `\DateInterval` for `ttl` in `model-cache`.
- [#1855](https://github.com/hyperf/hyperf/pull/1855) Added `ConstantFrequency` to flush one connection, when it is idle connection for the interval of time.

View File

@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://doc.hyperf.io
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
namespace Hyperf\ExceptionHandler\Annotation;
use Hyperf\Di\Annotation\AbstractAnnotation;
/**
* @Annotation
* @Target({"CLASS"})
*/
class ExceptionHandler extends AbstractAnnotation
{
/**
* @var string
*/
public $server = 'http';
/**
* @var int
*/
public $priority = 0;
}

View File

@ -13,6 +13,7 @@ namespace Hyperf\ExceptionHandler;
use Hyperf\ExceptionHandler\Formatter\DefaultFormatter;
use Hyperf\ExceptionHandler\Formatter\FormatterInterface;
use Hyperf\ExceptionHandler\Listener\ExceptionHandlerListener;
class ConfigProvider
{
@ -22,6 +23,9 @@ class ConfigProvider
'dependencies' => [
FormatterInterface::class => DefaultFormatter::class,
],
'listeners' => [
ExceptionHandlerListener::class,
],
'annotations' => [
'scan' => [
'paths' => [

View File

@ -37,6 +37,7 @@ class ExceptionHandlerDispatcher extends AbstractDispatcher
*/
[$throwable, $handlers] = $params;
$response = Context::get(ResponseInterface::class);
foreach ($handlers as $handler) {
if (! $this->container->has($handler)) {
throw new \InvalidArgumentException(sprintf('Invalid exception handler %s.', $handler));

View File

@ -0,0 +1,83 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://doc.hyperf.io
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
namespace Hyperf\ExceptionHandler\Listener;
use Hyperf\Contract\ConfigInterface;
use Hyperf\Di\Annotation\AnnotationCollector;
use Hyperf\Event\Contract\ListenerInterface;
use Hyperf\ExceptionHandler\Annotation\ExceptionHandler;
use Hyperf\Framework\Event\BootApplication;
use SplPriorityQueue;
class ExceptionHandlerListener implements ListenerInterface
{
const HANDLER_KEY = 'exceptions.handler';
/**
* @var ConfigInterface
*/
private $config;
/**
* @var int
*/
private $serial = PHP_INT_MAX;
public function __construct(ConfigInterface $config)
{
$this->config = $config;
}
public function listen(): array
{
return [
BootApplication::class,
];
}
public function process(object $event)
{
$queue = new SplPriorityQueue();
$handlers = $this->config->get(self::HANDLER_KEY, []);
foreach ($handlers as $server => $items) {
foreach ($items as $handler => $priority) {
if (! is_numeric($priority)) {
$handler = $priority;
$priority = 0;
}
$queue->insert([$server, $handler], [$priority, $this->serial--]);
}
}
$annotations = AnnotationCollector::getClassesByAnnotation(ExceptionHandler::class);
/**
* @var string $handler
* @var ExceptionHandler $annotation
*/
foreach ($annotations as $handler => $annotation) {
$queue->insert([$annotation->server, $handler], [$annotation->priority, $this->serial--]);
}
$this->config->set(self::HANDLER_KEY, $this->formatExceptionHandlers($queue));
}
protected function formatExceptionHandlers(SplPriorityQueue $queue): array
{
$result = [];
foreach ($queue as $item) {
[$server, $handler] = $item;
$result[$server][] = $handler;
$result[$server] = array_values(array_unique($result[$server]));
}
return $result;
}
}

View File

@ -0,0 +1,116 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://doc.hyperf.io
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
namespace HyperfTest\ExceptionHandler;
use Hyperf\Config\Config;
use Hyperf\Di\Annotation\AnnotationCollector;
use Hyperf\ExceptionHandler\Annotation\ExceptionHandler;
use Hyperf\ExceptionHandler\Listener\ExceptionHandlerListener;
use Mockery;
use PHPUnit\Framework\TestCase;
/**
* @internal
* @coversNothing
*/
class ExceptionHandlerListenerTest extends TestCase
{
protected function tearDown()
{
Mockery::close();
AnnotationCollector::clear();
}
public function testConfig()
{
$config = new Config([
'exceptions' => [
'handler' => [
'http' => $http = [
'Foo', 'Bar',
],
'ws' => $ws = [
'Foo', 'Tar', 'Bar',
],
],
],
]);
$listener = new ExceptionHandlerListener($config);
$listener->process(new \stdClass());
$this->assertSame($http, $config->get('exceptions.handler', [])['http']);
$this->assertSame($ws, $config->get('exceptions.handler', [])['ws']);
}
public function testAnnotation()
{
$config = new Config([
'exceptions' => [
'handler' => [
'http' => [
'Foo', 'Bar',
],
],
],
]);
AnnotationCollector::collectClass('Bar1', ExceptionHandler::class, new ExceptionHandler(['server' => 'http', 'priority' => 1]));
$listener = new ExceptionHandlerListener($config);
$listener->process(new \stdClass());
$this->assertSame([
'http' => [
'Bar1', 'Foo', 'Bar',
],
], $config->get('exceptions.handler', []));
}
public function testAnnotationWithSamePriotity()
{
$config = new Config([
'exceptions' => [
'handler' => [
'http' => [
'Foo', 'Bar',
],
'ws' => [
'Foo',
],
],
],
]);
AnnotationCollector::collectClass('Bar1', ExceptionHandler::class, new ExceptionHandler(['server' => 'http', 'priority' => 0]));
AnnotationCollector::collectClass('Bar', ExceptionHandler::class, new ExceptionHandler(['server' => 'ws', 'priority' => 1]));
$listener = new ExceptionHandlerListener($config);
$listener->process(new \stdClass());
$this->assertEquals(['Foo', 'Bar', 'Bar1'], $config->get('exceptions.handler', [])['http']);
$this->assertEquals(['Bar', 'Foo'], $config->get('exceptions.handler', [])['ws']);
}
public function testTheSameHandler()
{
$config = new Config([
'exceptions' => [
'handler' => [
'http' => [
'Foo', 'Bar', 'Bar', 'Tar',
],
],
],
]);
AnnotationCollector::collectClass('Tar', ExceptionHandler::class, new ExceptionHandler(['server' => 'http', 'priority' => 1]));
$listener = new ExceptionHandlerListener($config);
$listener->process(new \stdClass());
$this->assertSame([
'http' => [
'Tar', 'Foo', 'Bar',
],
], $config->get('exceptions.handler', []));
}
}