Skip to content

Commit

Permalink
支持在控制器方法中使用枚举类型的属性值 (#675)
Browse files Browse the repository at this point in the history
* 支持在控制器方法中使用枚举类型的属性值

* 修复测试
  • Loading branch information
Yurunsoft authored Feb 7, 2024
1 parent a0f93ff commit b9aae3b
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

declare(strict_types=1);

namespace Imi\Swoole\Test\HttpServer\ApiServer\Controller;

if (\PHP_VERSION_ID >= 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);
}
29 changes: 29 additions & 0 deletions src/Components/swoole/tests/unit/HttpServer/Tests/RequestTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
}
15 changes: 15 additions & 0 deletions src/Server/Http/Middleware/ActionMiddleware.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
49 changes: 48 additions & 1 deletion src/Server/Http/Struct/ActionMethodItem.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ class ActionMethodItem
*/
protected ?string $type = null;

/**
* 类型类名.
*/
protected ?string $typeClass = null;

/**
* @param mixed $default
*/
Expand All @@ -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;
}
}
}
}

Expand Down Expand Up @@ -89,4 +128,12 @@ public function allowNull(): bool
{
return $this->allowNull;
}

/**
* 获取类型类名.
*/
public function getTypeClass(): ?string
{
return $this->typeClass;
}
}
7 changes: 6 additions & 1 deletion tests/unit/Component/Enum/TestEnumBean.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

0 comments on commit b9aae3b

Please sign in to comment.