From df25aebd60ab9060257d11e1cafdaf8ff73ee7a3 Mon Sep 17 00:00:00 2001 From: Jerry Date: Fri, 10 Feb 2023 13:12:20 +0800 Subject: [PATCH] add timer tick --- docs/components/common/class-alias.md | 2 +- src/Globals/global_class_alias.php | 1 + src/Globals/global_functions.php | 24 ++++++++++++++ src/ZM/Annotation/AnnotationParser.php | 11 ++++++- src/ZM/Annotation/Framework/Tick.php | 25 +++++++++++++++ src/ZM/Event/Listener/WorkerEventListener.php | 4 +-- src/ZM/Framework.php | 2 +- src/ZM/Plugin/PluginManager.php | 7 +++- src/ZM/Plugin/Traits/TickTrait.php | 29 +++++++++++++++++ src/ZM/Plugin/ZMPlugin.php | 1 + src/ZM/Schedule/Timer.php | 32 +++++++++++++++++++ 11 files changed, 132 insertions(+), 6 deletions(-) create mode 100644 src/ZM/Annotation/Framework/Tick.php create mode 100644 src/ZM/Plugin/Traits/TickTrait.php create mode 100644 src/ZM/Schedule/Timer.php diff --git a/docs/components/common/class-alias.md b/docs/components/common/class-alias.md index 3aeff976..cc5cf7de 100644 --- a/docs/components/common/class-alias.md +++ b/docs/components/common/class-alias.md @@ -12,13 +12,13 @@ `\ZM\Annotation\OneBot\BotCommand` 注解事件类,在经过全局别名后,你也可以使用 `\BotCommand` 作为注解事件,效果相同。 ## 别名列表 - | 全类名 | 别名 | |--------------------------------------------------------|-------------------------| | `\ZM\Annotation\Framework\BindEvent` | `BindEvent` | | `\ZM\Annotation\Framework\Cron` | `Cron` | | `\ZM\Annotation\Framework\Init` | `Init` | | `\ZM\Annotation\Framework\Setup` | `Setup` | +| `\ZM\Annotation\Framework\Tick` | `Tick` | | `\ZM\Annotation\Http\Controller` | `Controller` | | `\ZM\Annotation\Http\Route` | `Route` | | `\ZM\Annotation\Middleware\Middleware` | `Middleware` | diff --git a/src/Globals/global_class_alias.php b/src/Globals/global_class_alias.php index 2e1b6123..d1df817b 100644 --- a/src/Globals/global_class_alias.php +++ b/src/Globals/global_class_alias.php @@ -6,6 +6,7 @@ class_alias(\ZM\Annotation\Framework\BindEvent::class, 'BindEvent'); class_alias(\ZM\Annotation\Framework\Cron::class, 'Cron'); class_alias(\ZM\Annotation\Framework\Init::class, 'Init'); class_alias(\ZM\Annotation\Framework\Setup::class, 'Setup'); +class_alias(\ZM\Annotation\Framework\Tick::class, 'Tick'); class_alias(\ZM\Annotation\Http\Controller::class, 'Controller'); class_alias(\ZM\Annotation\Http\Route::class, 'Route'); diff --git a/src/Globals/global_functions.php b/src/Globals/global_functions.php index 2f458192..d972167e 100644 --- a/src/Globals/global_functions.php +++ b/src/Globals/global_functions.php @@ -13,6 +13,7 @@ use ZM\Config\ZMConfig; use ZM\Container\ContainerHolder; use ZM\Logger\ConsoleLogger; use ZM\Middleware\MiddlewareHandler; +use ZM\Schedule\Timer; use ZM\Store\Database\DBException; use ZM\Store\Database\DBQueryBuilder; use ZM\Store\Database\DBWrapper; @@ -55,6 +56,29 @@ function zm_sleep(float|int $time) Adaptive::sleep($time); } +/** + * 创建一个计时器(Timer::tick() 的别名) + * + * @param int $ms 时间(毫秒) + * @param callable $callback 回调函数 + * @param int $times 重复次数(如果为 0 或 -1,则永久循环,其他大于 0 的数为限定次数) + */ +function zm_timer_tick(int $ms, callable $callback, int $times = 0): int +{ + return Timer::tick($ms, $callback, $times); +} + +/** + * 创建一个延后一次性计时器,只在指定毫秒后执行一次即销毁(Timer::after() 的别名) + * + * @param int $ms 时间(毫秒) + * @param callable $callback 回调函数 + */ +function zm_timer_after(int $ms, callable $callback): int +{ + return Timer::after($ms, $callback); +} + /** * 获取协程接口 */ diff --git a/src/ZM/Annotation/AnnotationParser.php b/src/ZM/Annotation/AnnotationParser.php index 8390c6d1..9e7b3c68 100644 --- a/src/ZM/Annotation/AnnotationParser.php +++ b/src/ZM/Annotation/AnnotationParser.php @@ -8,11 +8,13 @@ use Doctrine\Common\Annotations\AnnotationReader; use Koriym\Attributes\AttributeReader; use Koriym\Attributes\DualReader; use ZM\Annotation\Framework\Cron; +use ZM\Annotation\Framework\Tick; use ZM\Annotation\Http\Controller; use ZM\Annotation\Http\Route; use ZM\Annotation\Interfaces\ErgodicAnnotation; use ZM\Annotation\Middleware\Middleware; use ZM\Schedule\Schedule; +use ZM\Schedule\Timer; use ZM\Store\FileSystem; use ZM\Utils\HttpUtil; @@ -62,6 +64,7 @@ class AnnotationParser Route::class => [[$this, 'addRouteAnnotation']], Closed::class => [fn () => false], Cron::class => [[resolve(Schedule::class), 'addSchedule']], + Tick::class => [[Timer::class, 'registerTick']], ]; } } @@ -240,7 +243,13 @@ class AnnotationParser return $o; } - public function parseSpecial($annotation, $same_method_annotations = null): ?bool + /** + * 单独解析特殊注解 + * + * @param object|string $annotation 注解对象 + * @param null|array $same_method_annotations 相同方法下的其他注解列表(可为数组或 null) + */ + public function parseSpecial(object|string $annotation, ?array $same_method_annotations = null): ?bool { foreach (($this->special_parsers[$annotation::class] ?? []) as $parser) { $result = $parser($annotation, $same_method_annotations); diff --git a/src/ZM/Annotation/Framework/Tick.php b/src/ZM/Annotation/Framework/Tick.php new file mode 100644 index 00000000..aaef187a --- /dev/null +++ b/src/ZM/Annotation/Framework/Tick.php @@ -0,0 +1,25 @@ +tick_ms = $tick_ms; + } +} diff --git a/src/ZM/Event/Listener/WorkerEventListener.php b/src/ZM/Event/Listener/WorkerEventListener.php index adb2d20b..3213819d 100644 --- a/src/ZM/Event/Listener/WorkerEventListener.php +++ b/src/ZM/Event/Listener/WorkerEventListener.php @@ -199,14 +199,14 @@ class WorkerEventListener // 从 plugins 目录加载插件,包含 phar 和文件夹形式 $count = PluginManager::addPluginsFromDir($load_dir); if ($count !== 0) { - logger()->info('Loaded ' . $count . ' user plugins from plugin dir'); + logger()->debug('已加载 ' . $count . ' 个普通插件'); } // 从 composer 依赖加载插件 if (config('global.plugin.composer_plugin_enable', true)) { $count = PluginManager::addPluginsFromComposer(); if ($count !== 0) { - logger()->info('Loaded ' . $count . ' user plugins from composer'); + logger()->info('已加载 ' . $count . ' 个 Composer 插件'); } } diff --git a/src/ZM/Framework.php b/src/ZM/Framework.php index a7baefca..1ac8eedf 100644 --- a/src/ZM/Framework.php +++ b/src/ZM/Framework.php @@ -46,7 +46,7 @@ class Framework public const VERSION_ID = 681; /** @var string 版本名称 */ - public const VERSION = '3.0.0-beta7'; + public const VERSION = '3.0.0-beta8'; /** @var array 传入的参数 */ protected array $argv; diff --git a/src/ZM/Plugin/PluginManager.php b/src/ZM/Plugin/PluginManager.php index b4791636..5dac5274 100644 --- a/src/ZM/Plugin/PluginManager.php +++ b/src/ZM/Plugin/PluginManager.php @@ -208,7 +208,7 @@ class PluginManager foreach (self::$plugins as $name => $meta) { // 除了内建插件外,输出 log 告知启动插件 if ($meta->getPluginType() !== ZM_PLUGIN_TYPE_NATIVE) { - logger()->info('Enabling plugin: ' . $name); + logger()->info('正在启用插件 ' . $name); } // 先判断依赖关系,如果声明了依赖,但依赖不合规则报错崩溃 foreach ($meta->getDependencies() as $dep_name => $dep_version) { @@ -257,6 +257,11 @@ class PluginManager foreach ($entity->getInits() as $init) { AnnotationMap::addSingleAnnotation($init); } + // 设置 TimerTick 注解 + foreach ($entity->getTimerTicks() as $tick) { + AnnotationMap::addSingleAnnotation($tick); + $parser->parseSpecial($tick); + } } // 如果设置了 Autoload file,那么将会把 psr-4 的加载路径丢进 parser foreach ($meta->getAutoloadPsr4() as $namespace => $path) { diff --git a/src/ZM/Plugin/Traits/TickTrait.php b/src/ZM/Plugin/Traits/TickTrait.php new file mode 100644 index 00000000..123847c0 --- /dev/null +++ b/src/ZM/Plugin/Traits/TickTrait.php @@ -0,0 +1,29 @@ +class = $callback[0]; + $tick->method = $callback[1]; + } elseif ($callback instanceof \Closure) { + $tick->method = $callback; + } + $this->ticks[] = $tick; + } + + public function getTimerTicks(): array + { + return $this->ticks; + } +} diff --git a/src/ZM/Plugin/ZMPlugin.php b/src/ZM/Plugin/ZMPlugin.php index f487d273..0cd87582 100644 --- a/src/ZM/Plugin/ZMPlugin.php +++ b/src/ZM/Plugin/ZMPlugin.php @@ -18,4 +18,5 @@ class ZMPlugin use Traits\PluginLoadTrait; use Traits\RouteTrait; use Traits\PluginPackTrait; + use Traits\TickTrait; } diff --git a/src/ZM/Schedule/Timer.php b/src/ZM/Schedule/Timer.php new file mode 100644 index 00000000..ab3c2c2f --- /dev/null +++ b/src/ZM/Schedule/Timer.php @@ -0,0 +1,32 @@ +getDriver()->getEventLoop()->addTimer($ms, $callback, $times); + } + + public static function after(int $ms, callable $callback): int + { + return Framework::getInstance()->getDriver()->getEventLoop()->addTimer($ms, $callback); + } + + public static function registerTick(Tick $v): void + { + if ($v->class !== '' && $v->method !== '') { + self::tick($v->tick_ms, [resolve($v->class), $v->method]); + } elseif (is_callable($v->method)) { + self::tick($v->tick_ms, $v->method); + } else { + logger()->warning('注册的 Tick 定时器回调函数错误'); + } + } +}