diff --git a/src/Components/swoole/tests/unit/HttpServer/ApiServer/Controller/EnumController.php b/src/Components/swoole/tests/unit/HttpServer/ApiServer/Controller/EnumController.php new file mode 100644 index 0000000000..e0f6dceef8 --- /dev/null +++ b/src/Components/swoole/tests/unit/HttpServer/ApiServer/Controller/EnumController.php @@ -0,0 +1,39 @@ += 80100 && !class_exists(EnumController::class, false)) +{ + eval(<<<'PHP' + namespace Imi\Swoole\Test\HttpServer\ApiServer\Controller; + use Imi\Server\Http\Controller\HttpController; + use Imi\Server\Http\Route\Annotation\Action; + use Imi\Server\Http\Route\Annotation\Controller; + use Imi\Test\Component\Enum\TestEnumBean; + use Imi\Test\Component\Enum\TestEnumBeanBacked; + + #[Controller(prefix: '/enum/')] + class EnumController extends HttpController + { + #[Action] + public function test1(TestEnumBean $enum, TestEnumBeanBacked $enumBacked): array + { + return [ + 'enum' => $enum, + 'enumBacked' => $enumBacked, + ]; + } + + #[Action] + public function test2(TestEnumBean|string $enum = '', TestEnumBeanBacked|string $enumBacked = ''): array + { + return [ + 'enum' => $enum, + 'enumBacked' => $enumBacked, + ]; + } + } + PHP); +} diff --git a/src/Components/swoole/tests/unit/HttpServer/Tests/RequestTest.php b/src/Components/swoole/tests/unit/HttpServer/Tests/RequestTest.php index 23aa3cb271..6130615f17 100644 --- a/src/Components/swoole/tests/unit/HttpServer/Tests/RequestTest.php +++ b/src/Components/swoole/tests/unit/HttpServer/Tests/RequestTest.php @@ -384,4 +384,33 @@ public function testDomain(): void 'value' => 'host', ], $response->json(true)); } + + public function testEnum(): void + { + if (\PHP_VERSION_ID < 80100) + { + $this->markTestSkipped(); + } + $http = new HttpRequest(); + $response = $http->get($this->host . 'enum/test1?enum=A&enumBacked=imi'); + $this->assertEquals([ + 'enum' => 'A', + 'enumBacked' => 'imi', + ], $response->json(true)); + $response = $http->get($this->host . 'enum/test2'); + $this->assertEquals([ + 'enum' => '', + 'enumBacked' => '', + ], $response->json(true)); + $response = $http->get($this->host . 'enum/test2?enum=A&enumBacked=imi'); + $this->assertEquals([ + 'enum' => 'A', + 'enumBacked' => 'imi', + ], $response->json(true)); + $response = $http->get($this->host . 'enum/test2?enum=x&enumBacked=x'); + $this->assertEquals([ + 'enum' => 'x', + 'enumBacked' => 'x', + ], $response->json(true)); + } } diff --git a/src/Server/Http/Middleware/ActionMiddleware.php b/src/Server/Http/Middleware/ActionMiddleware.php index 77bc172d4b..bf8e50c787 100644 --- a/src/Server/Http/Middleware/ActionMiddleware.php +++ b/src/Server/Http/Middleware/ActionMiddleware.php @@ -16,6 +16,7 @@ use Imi\Server\Session\Session; use Imi\Server\View\View; use Imi\Util\DelayServerBeanCallable; +use Imi\Util\EnumUtil; use Imi\Util\ObjectArrayHelper; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; @@ -285,6 +286,20 @@ private function prepareActionParams(Request $request, RouteResult $routeResult) case 'bool': $value = (bool) $value; break; + case \UnitEnum::class: + $newValue = EnumUtil::tryFromName($actionMethodCacheItem->getTypeClass(), $value); + if (null !== $newValue) + { + $value = $newValue; + } + break; + case \BackedEnum::class: + $newValue = $actionMethodCacheItem->getTypeClass()::tryFrom($value); + if (null !== $newValue) + { + $value = $newValue; + } + break; } } $result[] = $value; diff --git a/src/Server/Http/Struct/ActionMethodItem.php b/src/Server/Http/Struct/ActionMethodItem.php index 9b3ada2627..650844f915 100644 --- a/src/Server/Http/Struct/ActionMethodItem.php +++ b/src/Server/Http/Struct/ActionMethodItem.php @@ -33,6 +33,11 @@ class ActionMethodItem */ protected ?string $type = null; + /** + * 类型类名. + */ + protected ?string $typeClass = null; + /** * @param mixed $default */ @@ -44,7 +49,41 @@ public function __construct(string $name, bool $hasDefault, $default, bool $allo $this->allowNull = $allowNull; if ($type instanceof \ReflectionNamedType) { - $this->type = $type->getName(); + if (is_subclass_of($typeClass = $type->getName(), \UnitEnum::class)) + { + $this->typeClass = $typeClass; + if (is_subclass_of($typeClass, \BackedEnum::class)) + { + $this->type = \BackedEnum::class; + } + else + { + $this->type = \UnitEnum::class; + } + } + else + { + $this->type = $type->getName(); + } + } + elseif ($type instanceof \ReflectionUnionType) + { + foreach ($type->getTypes() as $type) + { + if (is_subclass_of($typeClass = $type->getName(), \UnitEnum::class)) + { + $this->typeClass = $typeClass; + if (is_subclass_of($typeClass, \BackedEnum::class)) + { + $this->type = \BackedEnum::class; + } + else + { + $this->type = \UnitEnum::class; + } + break; + } + } } } @@ -89,4 +128,12 @@ public function allowNull(): bool { return $this->allowNull; } + + /** + * 获取类型类名. + */ + public function getTypeClass(): ?string + { + return $this->typeClass; + } } diff --git a/tests/unit/Component/Enum/TestEnumBean.php b/tests/unit/Component/Enum/TestEnumBean.php index 6bdb997dc2..45ec5e3b74 100644 --- a/tests/unit/Component/Enum/TestEnumBean.php +++ b/tests/unit/Component/Enum/TestEnumBean.php @@ -8,10 +8,15 @@ { eval(<<<'PHP' namespace Imi\Test\Component\Enum; - enum TestEnumBean + enum TestEnumBean implements \JsonSerializable { case A; case B; + + public function jsonSerialize(): string + { + return $this->name; + } } PHP); }