Skip to content

Commit d3cb325

Browse files
authored
支持枚举和联合类型的控制器方法参数 (#676)
1 parent b9aae3b commit d3cb325

File tree

7 files changed

+217
-88
lines changed

7 files changed

+217
-88
lines changed

phpstan.neon

+4
Original file line numberDiff line numberDiff line change
@@ -51,14 +51,18 @@ parameters:
5151
- dev/PHPStan/FileFinder.php
5252
- '#Class UnitEnum not found#'
5353
- '#Class BackedEnum not found#'
54+
- '#Class ReflectionEnum not found#i'
5455
- '#unknown class UnitEnum#'
5556
- '#unknown class BackedEnum#'
57+
- '#unknown class ReflectionEnum#'
5658
- '#Call to static method .+\(\) on an unknown class (Backed|Unit)Enum#'
5759
- '#Method Imi\\Util\\EnumUtil::.+\(\) has invalid return type (Backed|Unit)Enum#'
5860
- '#unknown class Imi\\Test\\Component\\Bean\\EnumBean#'
5961
- '#unknown class Imi\\Test\\Component\\Enum\\TestEnumBean#'
6062
- '#unknown class Imi\\Test\\Component\\Enum\\TestEnumBeanBacked#'
63+
- '#unknown class Imi\\Test\\Component\\Enum\\TestEnumBeanBackedInt#'
6164
- '#Class Imi\\Test\\Component\\Bean\\EnumBean not found#'
6265
- '#Class Imi\\Test\\Component\\Enum\\TestEnumBean not found#'
6366
- '#Class Imi\\Test\\Component\\Enum\\TestEnumBeanBacked not found#'
67+
- '#Class Imi\\Test\\Component\\Enum\\TestEnumBeanBackedInt not found#'
6468
services:

src/Components/grpc/src/Middleware/ActionMiddleware.php

+52-12
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use Imi\Server\Session\Session;
1717
use Imi\Server\View\View;
1818
use Imi\Util\DelayServerBeanCallable;
19+
use Imi\Util\EnumUtil;
1920
use Imi\Util\Http\Consts\MediaType;
2021
use Imi\Util\Http\Consts\RequestHeader;
2122
use Imi\Util\Http\Consts\ResponseHeader;
@@ -262,9 +263,10 @@ private function prepareActionParams(Request $request, RouteResult $routeResult)
262263
}
263264
elseif ($actionMethodCacheItem->hasDefault())
264265
{
265-
$value = $actionMethodCacheItem->getDefault();
266+
$result[] = $actionMethodCacheItem->getDefault();
267+
continue;
266268
}
267-
elseif (null !== ($type = $actionMethodCacheItem->getType()) && class_exists($type))
269+
elseif (($types = $actionMethodCacheItem->getTypes()) && ActionMethodItem::TYPE_COMMON === $types[0]['type'] && class_exists($type = $types[0]['name']))
268270
{
269271
if (is_subclass_of($type, \Google\Protobuf\Internal\Message::class))
270272
{
@@ -290,17 +292,55 @@ private function prepareActionParams(Request $request, RouteResult $routeResult)
290292
}
291293
if (null !== $value)
292294
{
293-
switch ($actionMethodCacheItem->getType())
295+
foreach ($actionMethodCacheItem->getTypes() as $type)
294296
{
295-
case 'int':
296-
$value = (int) $value;
297-
break;
298-
case 'float':
299-
$value = (float) $value;
300-
break;
301-
case 'bool':
302-
$value = (bool) $value;
303-
break;
297+
switch ($type['name'])
298+
{
299+
case 'int':
300+
$value = (int) $value;
301+
break 2;
302+
case 'float':
303+
$value = (float) $value;
304+
break 2;
305+
case 'bool':
306+
$value = (bool) $value;
307+
break 2;
308+
default:
309+
switch ($type['type'])
310+
{
311+
case ActionMethodItem::TYPE_UNIT_ENUM:
312+
$newValue = EnumUtil::tryFromName($type['name'], $value);
313+
if (null !== $newValue)
314+
{
315+
$value = $newValue;
316+
break 3;
317+
}
318+
break;
319+
case ActionMethodItem::TYPE_BACKED_ENUM:
320+
if ('int' === $type['enumBackingType'])
321+
{
322+
if (filter_var($value, \FILTER_VALIDATE_INT))
323+
{
324+
$newValue = $type['name']::tryFrom((int) $value);
325+
if (null !== $newValue)
326+
{
327+
$value = $newValue;
328+
break 3;
329+
}
330+
}
331+
}
332+
else
333+
{
334+
$newValue = $type['name']::tryFrom($value);
335+
if (null !== $newValue)
336+
{
337+
$value = $newValue;
338+
break 3;
339+
}
340+
}
341+
break;
342+
}
343+
}
304344
}
305345
}
306346
$result[] = $value;

src/Components/swoole/tests/unit/HttpServer/ApiServer/Controller/EnumController.php

+9-6
Original file line numberDiff line numberDiff line change
@@ -13,25 +13,28 @@
1313
use Imi\Server\Http\Route\Annotation\Controller;
1414
use Imi\Test\Component\Enum\TestEnumBean;
1515
use Imi\Test\Component\Enum\TestEnumBeanBacked;
16+
use Imi\Test\Component\Enum\TestEnumBeanBackedInt;
1617
1718
#[Controller(prefix: '/enum/')]
1819
class EnumController extends HttpController
1920
{
2021
#[Action]
21-
public function test1(TestEnumBean $enum, TestEnumBeanBacked $enumBacked): array
22+
public function test1(TestEnumBean $enum, TestEnumBeanBacked $enumBacked, TestEnumBeanBackedInt $enumBackedInt): array
2223
{
2324
return [
24-
'enum' => $enum,
25-
'enumBacked' => $enumBacked,
25+
'enum' => $enum,
26+
'enumBacked' => $enumBacked,
27+
'enumBackedInt' => $enumBackedInt,
2628
];
2729
}
2830
2931
#[Action]
30-
public function test2(TestEnumBean|string $enum = '', TestEnumBeanBacked|string $enumBacked = ''): array
32+
public function test2(TestEnumBean|string $enum = '', TestEnumBeanBacked|string $enumBacked = '', TestEnumBeanBackedInt|int $enumBackedInt = 0): array
3133
{
3234
return [
33-
'enum' => $enum,
34-
'enumBacked' => $enumBacked,
35+
'enum' => $enum,
36+
'enumBacked' => $enumBacked,
37+
'enumBackedInt' => $enumBackedInt,
3538
];
3639
}
3740
}

src/Components/swoole/tests/unit/HttpServer/Tests/RequestTest.php

+15-11
Original file line numberDiff line numberDiff line change
@@ -392,25 +392,29 @@ public function testEnum(): void
392392
$this->markTestSkipped();
393393
}
394394
$http = new HttpRequest();
395-
$response = $http->get($this->host . 'enum/test1?enum=A&enumBacked=imi');
395+
$response = $http->get($this->host . 'enum/test1?enum=A&enumBacked=imi&enumBackedInt=1');
396396
$this->assertEquals([
397-
'enum' => 'A',
398-
'enumBacked' => 'imi',
397+
'enum' => 'A',
398+
'enumBacked' => 'imi',
399+
'enumBackedInt' => 1,
399400
], $response->json(true));
400401
$response = $http->get($this->host . 'enum/test2');
401402
$this->assertEquals([
402-
'enum' => '',
403-
'enumBacked' => '',
403+
'enum' => '',
404+
'enumBacked' => '',
405+
'enumBackedInt' => 0,
404406
], $response->json(true));
405-
$response = $http->get($this->host . 'enum/test2?enum=A&enumBacked=imi');
407+
$response = $http->get($this->host . 'enum/test2?enum=A&enumBacked=imi&enumBackedInt=1');
406408
$this->assertEquals([
407-
'enum' => 'A',
408-
'enumBacked' => 'imi',
409+
'enum' => 'A',
410+
'enumBacked' => 'imi',
411+
'enumBackedInt' => 1,
409412
], $response->json(true));
410-
$response = $http->get($this->host . 'enum/test2?enum=x&enumBacked=x');
413+
$response = $http->get($this->host . 'enum/test2?enum=x&enumBacked=x&enumBackedInt=0');
411414
$this->assertEquals([
412-
'enum' => 'x',
413-
'enumBacked' => 'x',
415+
'enum' => 'x',
416+
'enumBacked' => 'x',
417+
'enumBackedInt' => 0,
414418
], $response->json(true));
415419
}
416420
}

src/Server/Http/Middleware/ActionMiddleware.php

+53-26
Original file line numberDiff line numberDiff line change
@@ -249,13 +249,14 @@ private function prepareActionParams(Request $request, RouteResult $routeResult)
249249
}
250250
elseif ($actionMethodCacheItem->hasDefault())
251251
{
252-
$value = $actionMethodCacheItem->getDefault();
252+
$result[] = $actionMethodCacheItem->getDefault();
253+
continue;
253254
}
254255
elseif ($actionMethodCacheItem->allowNull())
255256
{
256257
$value = null;
257258
}
258-
elseif (($type = $actionMethodCacheItem->getType()) && (UploadedFileInterface::class === $type || is_subclass_of($type, UploadedFileInterface::class)))
259+
elseif (($types = $actionMethodCacheItem->getTypes()) && ActionMethodItem::TYPE_UPLOADED_FILE === $types[0]['type'])
259260
{
260261
$uploadedFiles ??= $request->getUploadedFiles();
261262
if (!isset($uploadedFiles[$paramName]))
@@ -268,38 +269,64 @@ private function prepareActionParams(Request $request, RouteResult $routeResult)
268269
{
269270
throw new \RuntimeException(sprintf('Upload file failed. error:%d', $value->getError()));
270271
}
272+
$result[] = $value;
273+
continue;
271274
}
272275
else
273276
{
274277
throw new \InvalidArgumentException(sprintf('Missing parameter: %s', $paramName));
275278
}
276279
if (null !== $value)
277280
{
278-
switch ($actionMethodCacheItem->getType())
281+
foreach ($actionMethodCacheItem->getTypes() as $type)
279282
{
280-
case 'int':
281-
$value = (int) $value;
282-
break;
283-
case 'float':
284-
$value = (float) $value;
285-
break;
286-
case 'bool':
287-
$value = (bool) $value;
288-
break;
289-
case \UnitEnum::class:
290-
$newValue = EnumUtil::tryFromName($actionMethodCacheItem->getTypeClass(), $value);
291-
if (null !== $newValue)
292-
{
293-
$value = $newValue;
294-
}
295-
break;
296-
case \BackedEnum::class:
297-
$newValue = $actionMethodCacheItem->getTypeClass()::tryFrom($value);
298-
if (null !== $newValue)
299-
{
300-
$value = $newValue;
301-
}
302-
break;
283+
switch ($type['name'])
284+
{
285+
case 'int':
286+
$value = (int) $value;
287+
break 2;
288+
case 'float':
289+
$value = (float) $value;
290+
break 2;
291+
case 'bool':
292+
$value = (bool) $value;
293+
break 2;
294+
default:
295+
switch ($type['type'])
296+
{
297+
case ActionMethodItem::TYPE_UNIT_ENUM:
298+
$newValue = EnumUtil::tryFromName($type['name'], $value);
299+
if (null !== $newValue)
300+
{
301+
$value = $newValue;
302+
break 3;
303+
}
304+
break;
305+
case ActionMethodItem::TYPE_BACKED_ENUM:
306+
if ('int' === $type['enumBackingType'])
307+
{
308+
if (filter_var($value, \FILTER_VALIDATE_INT))
309+
{
310+
$newValue = $type['name']::tryFrom((int) $value);
311+
if (null !== $newValue)
312+
{
313+
$value = $newValue;
314+
break 3;
315+
}
316+
}
317+
}
318+
else
319+
{
320+
$newValue = $type['name']::tryFrom($value);
321+
if (null !== $newValue)
322+
{
323+
$value = $newValue;
324+
break 3;
325+
}
326+
}
327+
break;
328+
}
329+
}
303330
}
304331
}
305332
$result[] = $value;

0 commit comments

Comments
 (0)