diff --git a/CHANGELOG-3.1.md b/CHANGELOG-3.1.md index db1432f99..de36efb5b 100644 --- a/CHANGELOG-3.1.md +++ b/CHANGELOG-3.1.md @@ -11,6 +11,7 @@ - [#6342](https://github.com/hyperf/hyperf/pull/6342) Added `Coroutine::fork()` method and `Coroutine::pid()` method. - [#6360](https://github.com/hyperf/hyperf/pull/6360/files) Added response `content-type` header for swagger server. +- [#6363](https://github.com/hyperf/hyperf/pull/6363) Add callable type support to the fallback property of CircuitBreaker Attribute. # v3.1.0 - 2023-12-01 diff --git a/docs/en/circuit-breaker.md b/docs/en/circuit-breaker.md index e743786c8..0a2352b12 100644 --- a/docs/en/circuit-breaker.md +++ b/docs/en/circuit-breaker.md @@ -36,7 +36,7 @@ class UserService #[Inject] private UserServiceClient $client; - #[CircuitBreaker(timeout: 0.05, failCounter: 1, successCounter: 1, fallback: "App\UserService::searchFallback")] + #[CircuitBreaker(options: ['timeout' => 0.05], failCounter: 1, successCounter: 1, fallback: [UserService::class, 'searchFallback'])] public function search($offset, $limit) { return $this->client->users($offset, $limit); diff --git a/docs/zh-cn/circuit-breaker.md b/docs/zh-cn/circuit-breaker.md index 9d4ce32b5..86fa8403c 100644 --- a/docs/zh-cn/circuit-breaker.md +++ b/docs/zh-cn/circuit-breaker.md @@ -30,7 +30,7 @@ class UserService #[Inject] private UserServiceClient $client; - #[CircuitBreaker(options: ['timeout' => 0.05], failCounter: 1, successCounter: 1, fallback: "App\Service\UserService::searchFallback")] + #[CircuitBreaker(options: ['timeout' => 0.05], failCounter: 1, successCounter: 1, fallback: [UserService::class, 'searchFallback'])] public function search($offset, $limit) { return $this->client->users($offset, $limit); diff --git a/docs/zh-hk/circuit-breaker.md b/docs/zh-hk/circuit-breaker.md index 51e97ebe4..fb8111117 100644 --- a/docs/zh-hk/circuit-breaker.md +++ b/docs/zh-hk/circuit-breaker.md @@ -30,7 +30,7 @@ class UserService #[Inject] private UserServiceClient $client; - #[CircuitBreaker(options: ['timeout' => 0.05], failCounter: 1, successCounter: 1, fallback: "App\Service\UserService::searchFallback")] + #[CircuitBreaker(options: ['timeout' => 0.05], failCounter: 1, successCounter: 1, fallback: [UserService::class, 'searchFallback'])] public function search($offset, $limit) { return $this->client->users($offset, $limit); diff --git a/docs/zh-tw/circuit-breaker.md b/docs/zh-tw/circuit-breaker.md index c34ad9f8b..548ae5111 100644 --- a/docs/zh-tw/circuit-breaker.md +++ b/docs/zh-tw/circuit-breaker.md @@ -30,7 +30,7 @@ class UserService #[Inject] private UserServiceClient $client; - #[CircuitBreaker(options: ['timeout' => 0.05], failCounter: 1, successCounter: 1, fallback: "App\Service\UserService::searchFallback")] + #[CircuitBreaker(options: ['timeout' => 0.05], failCounter: 1, successCounter: 1, fallback: [UserService::class, 'searchFallback'])] public function search($offset, $limit) { return $this->client->users($offset, $limit); diff --git a/src/circuit-breaker/src/Annotation/CircuitBreaker.php b/src/circuit-breaker/src/Annotation/CircuitBreaker.php index acdc27b9a..d608b3cbc 100644 --- a/src/circuit-breaker/src/Annotation/CircuitBreaker.php +++ b/src/circuit-breaker/src/Annotation/CircuitBreaker.php @@ -29,7 +29,7 @@ class CircuitBreaker extends AbstractAnnotation */ public function __construct( public string $handler = TimeoutHandler::class, - public ?string $fallback = null, + public string|array $fallback = [], public float $duration = 10.0, public int $successCounter = 10, public int $failCounter = 10, diff --git a/src/circuit-breaker/src/Exception/BadFallbackException.php b/src/circuit-breaker/src/Exception/BadFallbackException.php new file mode 100644 index 000000000..a5baf94b6 --- /dev/null +++ b/src/circuit-breaker/src/Exception/BadFallbackException.php @@ -0,0 +1,18 @@ +prepareHandler($annotation->fallback); + [$class, $method] = $this->prepareHandler($annotation->fallback, $proceedingJoinPoint); - $class = $this->container->get($className); - if ($class instanceof FallbackInterface) { - return $class->fallback($proceedingJoinPoint); + $instance = $this->container->get($class); + if ($instance instanceof FallbackInterface) { + return $instance->fallback($proceedingJoinPoint); } - $argument = $proceedingJoinPoint->getArguments(); + $arguments = $proceedingJoinPoint->getArguments(); - return $class->{$methodName}(...$argument); + return $instance->{$method}(...$arguments); } abstract protected function process(ProceedingJoinPoint $proceedingJoinPoint, CircuitBreakerInterface $breaker, Annotation $annotation); - protected function prepareHandler(string $fallback): array + protected function prepareHandler(string|array $fallback, ProceedingJoinPoint $proceedingJoinPoint): array { - $result = explode('::', $fallback); + if (is_string($fallback)) { + $fallback = explode('::', $fallback); + if (! isset($fallback[1]) && is_callable([$proceedingJoinPoint->className, $fallback[0]])) { + return [$proceedingJoinPoint->className, $fallback[0]]; + } + $fallback[1] ??= 'fallback'; + } - return [ - $result[0], - $result[1] ?? 'fallback', - ]; + if (is_array($fallback) && is_callable($fallback)) { + return $fallback; + } + + throw new BadFallbackException('The fallback is invalid.'); } }