Skip to content

Commit

Permalink
[3.0] 重构枚举,使用原生枚举 (#648)
Browse files Browse the repository at this point in the history
* 重构 AMQP 枚举

* 重构 pgsql 枚举

* 重构 CronTaskType

* 重构 Imi\Cron\Consts\UniqueLevel 枚举

* 重构 Imi\Db\Mysql\Consts\LogicalOperator

* 重构 Imi\Db\Mysql\Query\FullText\SearchModifier

* 修复

* 重构 Imi\Model\IdGenerator\UUIDGeneratorType

* 更新文档

* 修复

* 重构 Imi\Server\WebSocket\Enum::NonControlFrameType 枚举

* PHP 原生枚举深度支持 (#646)

* beans 配置注入支持枚举

* InEnum 验证器支持原生枚举

* 修复

* 修复

* 修复一个类有多个Bean名称时,beans 注入的属性值不正确

* 完善测试

* 修复

* 修复
# Conflicts:
#	phpstan.neon
#	src/Bean/BeanProxy.php
#	src/Validate/ValidatorHelper.php

* 修复

* 修复

* 调整命名
  • Loading branch information
Yurunsoft authored Nov 14, 2023
1 parent c1b2518 commit cb07e0e
Show file tree
Hide file tree
Showing 64 changed files with 610 additions and 335 deletions.
4 changes: 2 additions & 2 deletions doc/base/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,8 @@ return [
// 参考: http://wiki.swoole.com/#/server/setting
// 参考: http://wiki.swoole.com/#/websocket_server?id=%e9%80%89%e9%a1%b9
// 参考: http://wiki.swoole.com/#/http_server?id=%e9%85%8d%e7%bd%ae%e9%80%89%e9%a1%b9
'nonControlFrameType' => \Imi\Server\WebSocket\Enum\NonControlFrameType::TEXT, // 配置 WebSocket 纯文本通信协议
// 'nonControlFrameType' => \Imi\Server\WebSocket\Enum\NonControlFrameType::BINARY, // 配置 WebSocket 二进制通信协议
'nonControlFrameType' => \Imi\Server\WebSocket\Enum\NonControlFrameType::Text, // 配置 WebSocket 纯文本通信协议
// 'nonControlFrameType' => \Imi\Server\WebSocket\Enum\NonControlFrameType::Binary, // 配置 WebSocket 二进制通信协议
'configs' => [
'reactor_num' => 8,
'worker_num' => 8,
Expand Down
14 changes: 14 additions & 0 deletions doc/base/version/2.1-3.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,16 @@ return [

* 重构注解类写法

* 定时任务 `Imi\Cron\Annotation\Cron` 注解的 `unique` 值类型更改为 `\Imi\Cron\Consts\UniqueLevel` 枚举,大小写有所变化

### 模型

* UUID 发号器的 `type` 类型改为枚举,大小写有所变化

### WebSocket

* 重构 `Imi\Server\WebSocket\Enum::NonControlFrameType` 为枚举

### imi-access-control

废弃并移出主仓库,代码仓库:<https://github.com/imiphp/imi-access-control>
Expand All @@ -81,6 +91,10 @@ return [

* 废弃 `Imi\AMQP\Swoole\AMQPSwooleConnection` 客户端类

* 消费者类 `Imi\AMQP\Base\BaseConsumer::consume()` 方法返回值类型应设置为 `\Imi\AMQP\Enum\ConsumerResult`,且必须返回枚举值

* `Imi\Queue\Enum\QueueType` 改为原生注解,如需自定义请编写枚举并实现 `Imi\Queue\Enum\QueueType\IQueueType` 接口

### imi-macro

废弃并移出主仓库,代码仓库:<https://github.com/imiphp/imi-macro>
Expand Down
8 changes: 4 additions & 4 deletions doc/components/orm/RDModel/definition.md
Original file line number Diff line number Diff line change
Expand Up @@ -550,19 +550,19 @@ ID 生成器指定参数:

`#[Id(generator: \Imi\Model\IdGenerator\UUIDGenerator::class)]`

`#[Id(generator: \Imi\Model\IdGenerator\UUIDGenerator::class, generatorOptions: ["type" => \Imi\Model\IdGenerator\UUIDGeneratorType::TIME])]`
`#[Id(generator: \Imi\Model\IdGenerator\UUIDGenerator::class, generatorOptions: ["type" => \Imi\Model\IdGenerator\UUIDGeneratorType::Time])]`

**随机算法:**`#[Id(generator: \Imi\Model\IdGenerator\UUIDGenerator::class, generatorOptions: ["type" => \Imi\Model\IdGenerator\UUIDGeneratorType::RANDOM])]`
**随机算法:**`#[Id(generator: \Imi\Model\IdGenerator\UUIDGenerator::class, generatorOptions: ["type" => \Imi\Model\IdGenerator\UUIDGeneratorType::Random])]`

**MD5、Sha1:**

使用方法基本一样,差别就是 `generatorOptions.type` 值不同。

`#[Id(index: false, generator: \Imi\Model\IdGenerator\UUIDGenerator::class, generatorOptions: ["type" => \Imi\Model\IdGenerator\UUIDGeneratorType::MD5, "ns" => "99e4edaf-8363-466e-bddf-7254db57675c", "nameField" => "名称字段名,该字段值必须唯一,否则会重复"])]`
`#[Id(index: false, generator: \Imi\Model\IdGenerator\UUIDGenerator::class, generatorOptions: ["type" => \Imi\Model\IdGenerator\UUIDGeneratorType::Md5, "ns" => "99e4edaf-8363-466e-bddf-7254db57675c", "nameField" => "名称字段名,该字段值必须唯一,否则会重复"])]`

> `ns` 必须是一个合法的 UUID
`#[Id(index: false, generator: \Imi\Model\IdGenerator\UUIDGenerator::class, generatorOptions: ["type" => \Imi\Model\IdGenerator\UUIDGeneratorType::SHA1, "nsField" => "命名空间字段名", "name" => "直接指定名称"])]`
`#[Id(index: false, generator: \Imi\Model\IdGenerator\UUIDGenerator::class, generatorOptions: ["type" => \Imi\Model\IdGenerator\UUIDGeneratorType::Sha1, "nsField" => "命名空间字段名", "name" => "直接指定名称"])]`

> 注意:`ns``name` 的值如果一样,生成的 UUID 也会一样!!!
Expand Down
4 changes: 3 additions & 1 deletion doc/components/struct/enum.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

[toc]

由于 PHP 本身不支持枚举类型,imi 特别基于注解实现了枚举类。
由于 PHP < 8.1 本身不支持枚举类型,imi 特别基于注解实现了枚举类。

> 此功能在 3.0 不会被内置,需要手动安装 `imi-old-enum` 组件。强烈建议使用 PHP 原生枚举!
## 枚举类定义

Expand Down
6 changes: 3 additions & 3 deletions doc/components/task/cron.md
Original file line number Diff line number Diff line change
Expand Up @@ -339,9 +339,9 @@ class TaskProcess implements IProcess
**unique:**

定时任务唯一性设置
当前实例唯一: current
所有实例唯一: all
不唯一: null
当前实例唯一: `\Imi\Cron\Consts\UniqueLevel::Current`
所有实例唯一: `\Imi\Cron\Consts\UniqueLevel::All`
不唯一: `null`

**redisPool:**

Expand Down
4 changes: 2 additions & 2 deletions doc/container/workerman.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ imi v2.0 版本开始,支持运行在 Workerman 环境中。
'namespace' => 'Imi\WorkermanGateway\Test\AppServer\Gateway',
'type' => Imi\WorkermanGateway\Workerman\Server\Type::GATEWAY,
'socketName' => 'websocket://0.0.0.0:8081', // 网关监听的地址
'nonControlFrameType' => \Imi\Server\WebSocket\Enum\NonControlFrameType::TEXT, // 配置 WebSocket 纯文本通信协议
// 'nonControlFrameType' => \Imi\Server\WebSocket\Enum\NonControlFrameType::BINARY, // 配置 WebSocket 二进制通信协议
'nonControlFrameType' => \Imi\Server\WebSocket\Enum\NonControlFrameType::Text, // 配置 WebSocket 纯文本通信协议
// 'nonControlFrameType' => \Imi\Server\WebSocket\Enum\NonControlFrameType::Binary, // 配置 WebSocket 二进制通信协议
'configs' => [
'lanIp' => '127.0.0.1',
'startPort' => 12900,
Expand Down
4 changes: 2 additions & 2 deletions doc/container/workerman/serverConfig.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ return [
'host' => '0.0.0.0',
'port' => 13002,
'shareWorker' => 'http', // 与名字叫 http 的服务,共享 worker 进程
'nonControlFrameType' => \Imi\Server\WebSocket\Enum\NonControlFrameType::TEXT, // 配置 WebSocket 纯文本通信协议
// 'nonControlFrameType' => \Imi\Server\WebSocket\Enum\NonControlFrameType::BINARY, // 配置 WebSocket 二进制通信协议
'nonControlFrameType' => \Imi\Server\WebSocket\Enum\NonControlFrameType::Text, // 配置 WebSocket 纯文本通信协议
// 'nonControlFrameType' => \Imi\Server\WebSocket\Enum\NonControlFrameType::Binary, // 配置 WebSocket 二进制通信协议
'configs' => [
],
],
Expand Down
4 changes: 2 additions & 2 deletions doc/core/long-connection-distributed.md
Original file line number Diff line number Diff line change
Expand Up @@ -242,8 +242,8 @@ Workerman Gateway 是一个成熟的实现方案,可以实现分布式消息
'namespace' => 'Imi\WorkermanGateway\Test\AppServer\Gateway',
'type' => Imi\WorkermanGateway\Workerman\Server\Type::GATEWAY,
'socketName' => 'websocket://0.0.0.0:13002',
'nonControlFrameType' => \Imi\Server\WebSocket\Enum\NonControlFrameType::TEXT, // 配置 WebSocket 纯文本通信协议
// 'nonControlFrameType' => \Imi\Server\WebSocket\Enum\NonControlFrameType::BINARY, // 配置 WebSocket 二进制通信协议
'nonControlFrameType' => \Imi\Server\WebSocket\Enum\NonControlFrameType::Text, // 配置 WebSocket 纯文本通信协议
// 'nonControlFrameType' => \Imi\Server\WebSocket\Enum\NonControlFrameType::Binary, // 配置 WebSocket 二进制通信协议
'configs' => [
'lanIp' => '127.0.0.1',
'startPort' => 12900,
Expand Down
4 changes: 2 additions & 2 deletions doc/core/subServer.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ http 和 WebSocket 同时做在一个项目中,代码之间没有隔阂,可
'sockType' => SWOOLE_SOCK_TCP,
// 同步连接,当连接事件执行完后,才执行 receive 事件。仅 TCP、WebSocket 且 SWOOLE_BASE 模式有效
'syncConnect' => true,
'nonControlFrameType' => \Imi\Server\WebSocket\Enum\NonControlFrameType::TEXT, // 配置 WebSocket 纯文本通信协议
// 'nonControlFrameType' => \Imi\Server\WebSocket\Enum\NonControlFrameType::BINARY, // 配置 WebSocket 二进制通信协议
'nonControlFrameType' => \Imi\Server\WebSocket\Enum\NonControlFrameType::Text, // 配置 WebSocket 纯文本通信协议
// 'nonControlFrameType' => \Imi\Server\WebSocket\Enum\NonControlFrameType::Binary, // 配置 WebSocket 二进制通信协议
// 服务器配置,参数用法同\Swoole\Server->set($configs)
'configs' => [
],
Expand Down
42 changes: 33 additions & 9 deletions src/Bean/BeanFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,20 @@ class BeanFactory
*/
public static function newInstance(string $class, mixed ...$args): mixed
{
$object = self::newInstanceNoInit($class, ...$args);
static::initInstance($object, $args);
return static::newBeanInstance($class, null, ...$args);
}

/**
* 实例化.
*
* @template T
*
* @param class-string<T> $class
*/
public static function newBeanInstance(string $class, ?string $beanName = null, mixed ...$args): mixed
{
$object = static::newInstanceNoInit($class, ...$args);
static::initInstance($object, $args, $beanName);

return $object;
}
Expand All @@ -61,7 +73,7 @@ public static function newInstanceNoInit(string $class, mixed ...$args): mixed
}
else
{
if (self::$enableFileCache)
if (static::$enableFileCache)
{
static::parseEvalName($class, $fileName, $className);
if (is_file($fileName))
Expand Down Expand Up @@ -100,8 +112,20 @@ public static function newInstanceNoInit(string $class, mixed ...$args): mixed
*/
public static function newInstanceEx(string $class, array $args = []): mixed
{
$object = self::newInstanceExNoInit($class, $args, $resultArgs);
static::initInstance($object, $resultArgs);
return static::newBeanInstanceEx($class, null, $args);
}

/**
* 增强实例化.
*
* @template T
*
* @param class-string<T> $class
*/
public static function newBeanInstanceEx(string $class, ?string $beanName = null, array $args = []): mixed
{
$object = static::newInstanceExNoInit($class, $args, $resultArgs);
static::initInstance($object, $resultArgs, $beanName);

return $object;
}
Expand Down Expand Up @@ -135,16 +159,16 @@ public static function newInstanceExNoInit(string $class, array $args, ?array &$
}
}

return self::newInstanceNoInit($class, ...$resultArgs);
return static::newInstanceNoInit($class, ...$resultArgs);
}

/**
* 初始化Bean对象
*/
public static function initInstance(object $object, array $args = []): void
public static function initInstance(object $object, array $args = [], ?string $beanName = null): void
{
$class = self::getObjectClass($object);
BeanProxy::injectProps($object, $class);
$class = static::getObjectClass($object);
BeanProxy::injectProps($object, $class, false, $beanName);
$ref = ReflectionContainer::getClassReflection($class);
if ($ref->hasMethod('__init'))
{
Expand Down
120 changes: 88 additions & 32 deletions src/Bean/BeanProxy.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
use Imi\Aop\JoinPoint;
use Imi\Aop\Model\AopItem;
use Imi\Config;
use Imi\Util\EnumUtil;
use Imi\Util\Imi;

class BeanProxy
Expand Down Expand Up @@ -135,9 +136,9 @@ public static function &call(object $object, string $className, string $method,
/**
* 注入属性.
*/
public static function injectProps(object $object, string $className, bool $reInit = false): void
public static function injectProps(object $object, string $className, bool $reInit = false, ?string $beanName = null): void
{
[$injects, $configs] = static::getInjects($className);
[$injects, $configs] = static::getInjects($className, $beanName);
if (!$injects && !$configs)
{
return;
Expand Down Expand Up @@ -169,6 +170,44 @@ public static function injectProps(object $object, string $className, bool $reIn
{
$propRef = $refClass->getProperty($name);
$propRef->setAccessible(true);
if ($propRef->hasType())
{
$type = $propRef->getType();
foreach ((static function () use ($type) {
if ($type instanceof \ReflectionNamedType)
{
if (is_subclass_of($typeName = $type->getName(), \UnitEnum::class))
{
yield $typeName;
}
}
elseif ($type instanceof \ReflectionUnionType)
{
foreach ($type->getTypes() as $type)
{
if (is_subclass_of($typeName = $type->getName(), \UnitEnum::class))
{
yield $typeName;
}
}
}
})() as $enumType)
{
if (is_subclass_of($enumType, \BackedEnum::class))
{
$case = $enumType::tryFrom($value);
}
else
{
$case = EnumUtil::tryFromName($enumType, $value);
}
if ($case)
{
$value = $case;
break;
}
}
}
$propRef->setValue($object, $value);
}
}
Expand All @@ -177,37 +216,54 @@ public static function injectProps(object $object, string $className, bool $reIn
/**
* 获取注入属性的配置们.
*/
public static function getConfigInjects(string $className): array
public static function getConfigInjects(string $className, ?string $beanName = null): array
{
// 配置文件注入
$beanData = BeanManager::get($className);
if ($beanData)
{
$beanName = $beanData['beanName'];
}
else
{
$beanName = $className;
}
$beans = Config::get('@currentServer.beans');
if (isset($beans[$beanName]))
$originBeanName = $beanName;
$count = 2;
while ($count--)
{
return $beans[$beanName];
}
elseif ($beanName !== $className && isset($beans[$className]))
{
return $beans[$className];
}
else
{
$beans = Config::get('@app.beans');
if (isset($beans[$beanName]))
// 配置文件注入
if (null === $beanName)
{
$beanData = BeanManager::get($className);
if ($beanData)
{
$beanName = $beanData['beanName'];
}
else
{
$beanName = $className;
}
}
$serverBeans ??= Config::get('@currentServer.beans');
if (isset($serverBeans[$beanName]))
{
return $beans[$beanName];
return $serverBeans[$beanName];
}
elseif ($beanName !== $className && isset($beans[$className]))
elseif ($beanName !== $className && isset($serverBeans[$className]))
{
return $serverBeans[$className];
}
else
{
$appBeans ??= Config::get('@app.beans');
if (isset($appBeans[$beanName]))
{
return $appBeans[$beanName];
}
elseif ($beanName !== $className && isset($appBeans[$className]))
{
return $appBeans[$className];
}
}
if (null === $originBeanName)
{
break;
}
else
{
return $beans[$className];
// 下次循环会根据类名尝试获取注入配置
$beanName = null;
}
}

Expand All @@ -219,9 +275,9 @@ public static function getConfigInjects(string $className): array
*
* 返回:[$annotations, $configs]
*/
public static function getInjects(string $className): array
public static function getInjects(string $className, ?string $beanName = null): array
{
$configs = static::getConfigInjects($className);
$configs = static::getConfigInjects($className, $beanName);
$injects = BeanManager::getPropertyInjects($className);
if ($configs && $injects)
{
Expand Down Expand Up @@ -301,9 +357,9 @@ private static function doAspect(string $className, string $method, string $poin
/**
* 获取注入类属性的值
*/
public static function getInjectValue(string $className, string $propertyName): mixed
public static function getInjectValue(string $className, string $propertyName, ?string $beanName = null): mixed
{
[$annotations, $configs] = static::getInjects($className);
[$annotations, $configs] = static::getInjects($className, $beanName);
if (isset($configs[$propertyName]))
{
return $configs[$propertyName];
Expand Down
2 changes: 1 addition & 1 deletion src/Bean/Container.php
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ private function __newInstance(string $id, array $params, bool $allowStore): mix
if ($data['recursion'] ?? true)
{
// @phpstan-ignore-next-line
BeanFactory::initInstance($object, $params);
BeanFactory::initInstance($object, $params, $originId);
if ($stored && $object !== $beanObjects[$originId])
{
// 防止类 __init() 方法有协程上下文切换,导致单例被覆盖
Expand Down
4 changes: 2 additions & 2 deletions src/Components/amqp/example/AMQP/Test/TestConsumer.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ class TestConsumer extends BaseConsumer
*
* @param \AMQPApp\AMQP\Test\TestMessage $message
*/
protected function consume(IMessage $message): mixed
protected function consume(IMessage $message): ConsumerResult
{
var_dump(__CLASS__, $message->getBody(), $message::class);
Redis::set('imi-amqp:consume:1:' . $message->getMemberId(), $message->getBody());

return ConsumerResult::ACK;
return ConsumerResult::Ack;
}
}
Loading

0 comments on commit cb07e0e

Please sign in to comment.