mirror of
https://github.com/zhamao-robot/zhamao-framework.git
synced 2026-03-17 20:54:52 +08:00
update to 3.0.0-alpha2 (build 610): refactor driver
This commit is contained in:
parent
f2a12634a4
commit
41b058aeaf
2
.gitignore
vendored
2
.gitignore
vendored
@ -78,3 +78,5 @@ package-lock.json
|
|||||||
|
|
||||||
### ASDF ###
|
### ASDF ###
|
||||||
/.tool-version
|
/.tool-version
|
||||||
|
|
||||||
|
.DS_Store
|
||||||
|
|||||||
@ -15,22 +15,20 @@
|
|||||||
"require": {
|
"require": {
|
||||||
"php": "^7.2 || ^7.3 || ^7.4 || ^8.0 || ^8.1",
|
"php": "^7.2 || ^7.3 || ^7.4 || ^8.0 || ^8.1",
|
||||||
"ext-json": "*",
|
"ext-json": "*",
|
||||||
"ext-posix": "*",
|
|
||||||
"doctrine/dbal": "^2.13.1",
|
"doctrine/dbal": "^2.13.1",
|
||||||
"dragonmantank/cron-expression": "^3.3",
|
"dragonmantank/cron-expression": "^3.3",
|
||||||
"jelix/version": "^2.0",
|
"jelix/version": "^2.0",
|
||||||
"koriym/attributes": "^1.0",
|
"koriym/attributes": "^1.0",
|
||||||
"psr/container": "^2.0",
|
"psr/container": "^2.0",
|
||||||
"psy/psysh": "^0.11.2",
|
"psy/psysh": "^0.11.2",
|
||||||
"symfony/console": "~6.0 || ~5.0 || ~4.0 || ~3.0",
|
"symfony/console": "~6.0 || ~5.0 || ~4.0",
|
||||||
"symfony/polyfill-ctype": "^1.19",
|
"symfony/polyfill-ctype": "^1.19",
|
||||||
"symfony/polyfill-mbstring": "^1.19",
|
"symfony/polyfill-mbstring": "^1.19",
|
||||||
"symfony/polyfill-php80": "^1.16",
|
"symfony/polyfill-php80": "^1.16",
|
||||||
"symfony/routing": "~6.0 || ~5.0 || ~4.0 || ~3.0",
|
"symfony/routing": "~6.0 || ~5.0 || ~4.0",
|
||||||
"zhamao/connection-manager": "^1.0",
|
|
||||||
"zhamao/console": "^1.0",
|
|
||||||
"zhamao/logger": "dev-master",
|
"zhamao/logger": "dev-master",
|
||||||
"zhamao/request": "^1.1"
|
"zhamao/request": "^1.1",
|
||||||
|
"onebot/libonebot": "*@dev"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"brainmaestro/composer-git-hooks": "^2.8",
|
"brainmaestro/composer-git-hooks": "^2.8",
|
||||||
@ -58,7 +56,8 @@
|
|||||||
"ZM\\": "src/ZM"
|
"ZM\\": "src/ZM"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"src/ZM/global_functions.php"
|
"src/Globals/global_functions.php",
|
||||||
|
"src/Globals/global_defines_app.php"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"autoload-dev": {
|
"autoload-dev": {
|
||||||
@ -106,5 +105,11 @@
|
|||||||
"analyse": "phpstan analyse --memory-limit 300M",
|
"analyse": "phpstan analyse --memory-limit 300M",
|
||||||
"cs-fix": "php-cs-fixer fix",
|
"cs-fix": "php-cs-fixer fix",
|
||||||
"test": "bin/phpunit-swoole --no-coverage"
|
"test": "bin/phpunit-swoole --no-coverage"
|
||||||
|
},
|
||||||
|
"repositories": [
|
||||||
|
{
|
||||||
|
"type": "path",
|
||||||
|
"url": "/Users/jerry/project/onebot/php-libonebot"
|
||||||
}
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,152 +1,60 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
/** @noinspection PhpComposerExtensionStubsInspection */
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
/* bind host */
|
/* 启动框架的底层驱动(原生支持 swoole、workerman 两种) */
|
||||||
$config['host'] = '0.0.0.0';
|
$config['driver'] = 'workerman';
|
||||||
|
|
||||||
/* bind port */
|
/* 要启动的服务器监听端口及协议 */
|
||||||
$config['port'] = 20001;
|
$config['servers'] = [
|
||||||
|
[
|
||||||
/* 框架开到公网或外部的HTTP访问链接,通过 DataProvider::getFrameworkLink() 获取 */
|
'host' => '0.0.0.0',
|
||||||
$config['http_reverse_link'] = 'http://127.0.0.1:' . $config['port'];
|
'port' => 20001,
|
||||||
|
'type' => 'websocket',
|
||||||
/* 框架是否启动debug模式,当debug模式为true时,启用热更新(需要安装inotify扩展) */
|
],
|
||||||
$config['debug_mode'] = false;
|
[
|
||||||
|
'host' => '0.0.0.0',
|
||||||
/* 存放框架内文件数据的目录 */
|
'port' => 20002,
|
||||||
$config['zm_data'] = realpath(WORKING_DIR) . '/zm_data/';
|
'type' => 'http',
|
||||||
|
'flag' => 20002,
|
||||||
/* 存放各个模块配置文件的目录 */
|
],
|
||||||
$config['config_dir'] = $config['zm_data'] . 'config/';
|
[
|
||||||
|
'host' => '0.0.0.0',
|
||||||
/* 存放崩溃和运行日志的目录 */
|
'port' => 20003,
|
||||||
$config['crash_dir'] = $config['zm_data'] . 'crash/';
|
'type' => 'http',
|
||||||
|
'flag' => 20003,
|
||||||
/* 对应swoole的server->set参数 */
|
],
|
||||||
$config['swoole'] = [
|
|
||||||
'log_file' => $config['crash_dir'] . 'swoole_error.log',
|
|
||||||
// 'worker_num' => swoole_cpu_num(), //如果你只有一个 OneBot 实例连接到框架并且代码没有复杂的CPU密集计算,则可把这里改为1使用全局变量
|
|
||||||
'dispatch_mode' => 2, // 包分配原则,见 https://wiki.swoole.com/#/server/setting?id=dispatch_mode
|
|
||||||
'max_coroutine' => 300000,
|
|
||||||
'max_wait_time' => 5,
|
|
||||||
// 'task_worker_num' => 4,
|
|
||||||
// 'task_enable_coroutine' => true
|
|
||||||
];
|
];
|
||||||
|
|
||||||
/* 一些框架与框架运行时设置的调整 */
|
/* Workerman 驱动相关配置 */
|
||||||
|
$config['workerman_options'] = [
|
||||||
|
'worker_num' => 1, // 如果你只有一个 OneBot 实例连接到框架并且代码没有复杂的CPU密集计算,则可把这里改为1使用全局变量
|
||||||
|
];
|
||||||
|
|
||||||
|
/* Swoole 驱动相关配置 */
|
||||||
|
$config['swoole_options'] = [
|
||||||
|
'coroutine_hook_flags' => SWOOLE_HOOK_ALL & (~SWOOLE_HOOK_CURL), // 协程 Hook 内容
|
||||||
|
'swoole_set' => [
|
||||||
|
'worker_num' => 1, // 如果你只有一个 OneBot 实例连接到框架并且代码没有复杂的CPU密集计算,则可把这里改为1使用全局变量
|
||||||
|
'dispatch_mode' => 2, // 包分配原则,见 https://wiki.swoole.com/#/server/setting?id=dispatch_mode
|
||||||
|
'max_coroutine' => 300000, // 允许最大的协程数
|
||||||
|
'max_wait_time' => 5, // 安全退出模式下允许等待 Worker 的最长秒数
|
||||||
|
// 'task_worker_num' => 4, // 启动 TaskWorker 进程的数量(默认不启动)
|
||||||
|
// 'task_enable_coroutine' => true // TaskWorker 是否开启协程
|
||||||
|
],
|
||||||
|
'swoole_server_mode' => SWOOLE_PROCESS, // Swoole Server 启动模式,默认为 SWOOLE_PROCESS
|
||||||
|
];
|
||||||
|
|
||||||
|
/* 框架本体运行时的一些可调配置 */
|
||||||
$config['runtime'] = [
|
$config['runtime'] = [
|
||||||
'swoole_coroutine_hook_flags' => SWOOLE_HOOK_ALL & (~SWOOLE_HOOK_CURL),
|
|
||||||
'swoole_server_mode' => SWOOLE_PROCESS,
|
|
||||||
'middleware_error_policy' => 1,
|
|
||||||
'reload_delay_time' => 800,
|
'reload_delay_time' => 800,
|
||||||
'global_middleware_binding' => [],
|
|
||||||
'save_console_log_file' => false, // 改为目标路径,则将 Console 输出的日志保存到文件
|
|
||||||
'annotation_reader_ignore' => [ // 设置注解解析器忽略的注解名或命名空间,防止解析到不该解析的
|
'annotation_reader_ignore' => [ // 设置注解解析器忽略的注解名或命名空间,防止解析到不该解析的
|
||||||
'name' => [
|
'name' => [
|
||||||
'mixin',
|
'mixin',
|
||||||
],
|
],
|
||||||
'namespace' => [],
|
'namespace' => [],
|
||||||
],
|
],
|
||||||
];
|
'timezone' => 'Asia/Shanghai',
|
||||||
|
|
||||||
/* 轻量字符串缓存,默认开启 */
|
|
||||||
$config['light_cache'] = [
|
|
||||||
'size' => 512, // 最多允许储存的条数(需要2的倍数)
|
|
||||||
'max_strlen' => 32768, // 单行字符串最大长度(需要2的倍数)
|
|
||||||
'hash_conflict_proportion' => 0.6, // Hash冲突率(越大越好,但是需要的内存更多)
|
|
||||||
'persistence_path' => $config['zm_data'] . '_cache.json',
|
|
||||||
'auto_save_interval' => 900,
|
|
||||||
];
|
|
||||||
|
|
||||||
/* 大容量跨进程变量存储(2.2.0可用) */
|
|
||||||
$config['worker_cache'] = [
|
|
||||||
'worker' => 0,
|
|
||||||
'transaction_timeout' => 30000,
|
|
||||||
];
|
|
||||||
|
|
||||||
/* MySQL数据库连接信息,host留空则启动时不创建sql连接池 */
|
|
||||||
$config['mysql_config'] = [
|
|
||||||
'host' => '',
|
|
||||||
'port' => 3306,
|
|
||||||
'unix_socket' => null,
|
|
||||||
'username' => 'root',
|
|
||||||
'password' => '123456',
|
|
||||||
'dbname' => '',
|
|
||||||
'charset' => 'utf8mb4',
|
|
||||||
'pool_size' => 64,
|
|
||||||
'options' => [
|
|
||||||
PDO::ATTR_STRINGIFY_FETCHES => false,
|
|
||||||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
|
||||||
],
|
|
||||||
];
|
|
||||||
|
|
||||||
/* Redis连接信息,host留空则启动时不创建Redis连接池 */
|
|
||||||
$config['redis_config'] = [
|
|
||||||
'host' => '',
|
|
||||||
'port' => 6379,
|
|
||||||
'timeout' => 1,
|
|
||||||
'db_index' => 0,
|
|
||||||
'auth' => '',
|
|
||||||
];
|
|
||||||
|
|
||||||
/* onebot连接约定的token */
|
|
||||||
$config['access_token'] = '';
|
|
||||||
|
|
||||||
/* HTTP服务器固定请求头的返回 */
|
|
||||||
$config['http_header'] = [
|
|
||||||
'Server' => 'zhamao-framework',
|
|
||||||
'Content-Type' => 'text/html; charset=utf-8',
|
|
||||||
];
|
|
||||||
|
|
||||||
/* HTTP服务器在指定状态码下回复的页面(默认) */
|
|
||||||
$config['http_default_code_page'] = [
|
|
||||||
'404' => '404.html',
|
|
||||||
];
|
|
||||||
|
|
||||||
/* zhamao-framework在框架启动时初始化的atomic们 */
|
|
||||||
$config['init_atomics'] = [
|
|
||||||
// 'custom_atomic_name' => 0, //自定义添加的Atomic
|
|
||||||
];
|
|
||||||
|
|
||||||
/* 终端日志显示等级(0-4) */
|
|
||||||
$config['info_level'] = 2;
|
|
||||||
|
|
||||||
/* 上下文接口类 implemented from ContextInterface */
|
|
||||||
$config['context_class'] = \ZM\Context\Context::class;
|
|
||||||
|
|
||||||
/* 静态文件访问 */
|
|
||||||
$config['static_file_server'] = [
|
|
||||||
'status' => false,
|
|
||||||
'document_root' => realpath(__DIR__ . '/../') . '/resources/html',
|
|
||||||
'document_index' => [
|
|
||||||
'index.html',
|
|
||||||
],
|
|
||||||
];
|
|
||||||
|
|
||||||
/* 机器人解析模块,关闭后无法使用如CQCommand等注解(上面的modules即将废弃) */
|
|
||||||
$config['onebot'] = [
|
|
||||||
'status' => true,
|
|
||||||
'single_bot_mode' => false,
|
|
||||||
'message_level' => 99,
|
|
||||||
'message_convert_string' => true,
|
|
||||||
'message_command_policy' => 'interrupt',
|
|
||||||
];
|
|
||||||
|
|
||||||
/* 一个远程简易终端,使用nc直接连接即可,但是不建议开放host为0.0.0.0(远程连接) */
|
|
||||||
$config['remote_terminal'] = [
|
|
||||||
'status' => false,
|
|
||||||
'host' => '127.0.0.1',
|
|
||||||
'port' => 20002,
|
|
||||||
'token' => '',
|
|
||||||
];
|
|
||||||
|
|
||||||
/* 模块(插件)加载器的相关设置 */
|
|
||||||
$config['module_loader'] = [
|
|
||||||
'enable_hotload' => false,
|
|
||||||
'load_path' => $config['zm_data'] . 'modules',
|
|
||||||
];
|
];
|
||||||
|
|
||||||
return $config;
|
return $config;
|
||||||
|
|||||||
152
config/global_old.php
Normal file
152
config/global_old.php
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/** @noinspection PhpComposerExtensionStubsInspection */
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/* bind host */
|
||||||
|
$config['host'] = '0.0.0.0';
|
||||||
|
|
||||||
|
/* bind port */
|
||||||
|
$config['port'] = 20001;
|
||||||
|
|
||||||
|
/* 框架开到公网或外部的HTTP访问链接,通过 DataProvider::getFrameworkLink() 获取 */
|
||||||
|
$config['http_reverse_link'] = 'http://127.0.0.1:' . $config['port'];
|
||||||
|
|
||||||
|
/* 框架是否启动debug模式,当debug模式为true时,启用热更新(需要安装inotify扩展) */
|
||||||
|
$config['debug_mode'] = false;
|
||||||
|
|
||||||
|
/* 存放框架内文件数据的目录 */
|
||||||
|
$config['zm_data'] = realpath(WORKING_DIR) . '/zm_data/';
|
||||||
|
|
||||||
|
/* 存放各个模块配置文件的目录 */
|
||||||
|
$config['config_dir'] = $config['zm_data'] . 'config/';
|
||||||
|
|
||||||
|
/* 存放崩溃和运行日志的目录 */
|
||||||
|
$config['crash_dir'] = $config['zm_data'] . 'crash/';
|
||||||
|
|
||||||
|
/* 对应swoole的server->set参数 */
|
||||||
|
$config['swoole'] = [
|
||||||
|
'log_file' => $config['crash_dir'] . 'swoole_error.log',
|
||||||
|
// 'worker_num' => swoole_cpu_num(), //如果你只有一个 OneBot 实例连接到框架并且代码没有复杂的CPU密集计算,则可把这里改为1使用全局变量
|
||||||
|
'dispatch_mode' => 2, // 包分配原则,见 https://wiki.swoole.com/#/server/setting?id=dispatch_mode
|
||||||
|
'max_coroutine' => 300000,
|
||||||
|
'max_wait_time' => 5,
|
||||||
|
// 'task_worker_num' => 4,
|
||||||
|
// 'task_enable_coroutine' => true
|
||||||
|
];
|
||||||
|
|
||||||
|
/* 一些框架与框架运行时设置的调整 */
|
||||||
|
$config['runtime'] = [
|
||||||
|
'swoole_coroutine_hook_flags' => SWOOLE_HOOK_ALL & (~SWOOLE_HOOK_CURL),
|
||||||
|
'swoole_server_mode' => SWOOLE_PROCESS,
|
||||||
|
'middleware_error_policy' => 1,
|
||||||
|
'reload_delay_time' => 800,
|
||||||
|
'global_middleware_binding' => [],
|
||||||
|
'save_console_log_file' => false, // 改为目标路径,则将 Console 输出的日志保存到文件
|
||||||
|
'annotation_reader_ignore' => [ // 设置注解解析器忽略的注解名或命名空间,防止解析到不该解析的
|
||||||
|
'name' => [
|
||||||
|
'mixin',
|
||||||
|
],
|
||||||
|
'namespace' => [],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
/* 轻量字符串缓存,默认开启 */
|
||||||
|
$config['light_cache'] = [
|
||||||
|
'size' => 512, // 最多允许储存的条数(需要2的倍数)
|
||||||
|
'max_strlen' => 32768, // 单行字符串最大长度(需要2的倍数)
|
||||||
|
'hash_conflict_proportion' => 0.6, // Hash冲突率(越大越好,但是需要的内存更多)
|
||||||
|
'persistence_path' => $config['zm_data'] . '_cache.json',
|
||||||
|
'auto_save_interval' => 900,
|
||||||
|
];
|
||||||
|
|
||||||
|
/* 大容量跨进程变量存储(2.2.0可用) */
|
||||||
|
$config['worker_cache'] = [
|
||||||
|
'worker' => 0,
|
||||||
|
'transaction_timeout' => 30000,
|
||||||
|
];
|
||||||
|
|
||||||
|
/* MySQL数据库连接信息,host留空则启动时不创建sql连接池 */
|
||||||
|
$config['mysql_config'] = [
|
||||||
|
'host' => '',
|
||||||
|
'port' => 3306,
|
||||||
|
'unix_socket' => null,
|
||||||
|
'username' => 'root',
|
||||||
|
'password' => '123456',
|
||||||
|
'dbname' => '',
|
||||||
|
'charset' => 'utf8mb4',
|
||||||
|
'pool_size' => 64,
|
||||||
|
'options' => [
|
||||||
|
PDO::ATTR_STRINGIFY_FETCHES => false,
|
||||||
|
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
/* Redis连接信息,host留空则启动时不创建Redis连接池 */
|
||||||
|
$config['redis_config'] = [
|
||||||
|
'host' => '',
|
||||||
|
'port' => 6379,
|
||||||
|
'timeout' => 1,
|
||||||
|
'db_index' => 0,
|
||||||
|
'auth' => '',
|
||||||
|
];
|
||||||
|
|
||||||
|
/* onebot连接约定的token */
|
||||||
|
$config['access_token'] = '';
|
||||||
|
|
||||||
|
/* HTTP服务器固定请求头的返回 */
|
||||||
|
$config['http_header'] = [
|
||||||
|
'Server' => 'zhamao-framework',
|
||||||
|
'Content-Type' => 'text/html; charset=utf-8',
|
||||||
|
];
|
||||||
|
|
||||||
|
/* HTTP服务器在指定状态码下回复的页面(默认) */
|
||||||
|
$config['http_default_code_page'] = [
|
||||||
|
'404' => '404.html',
|
||||||
|
];
|
||||||
|
|
||||||
|
/* zhamao-framework在框架启动时初始化的atomic们 */
|
||||||
|
$config['init_atomics'] = [
|
||||||
|
// 'custom_atomic_name' => 0, //自定义添加的Atomic
|
||||||
|
];
|
||||||
|
|
||||||
|
/* 终端日志显示等级(0-4) */
|
||||||
|
$config['info_level'] = 2;
|
||||||
|
|
||||||
|
/* 上下文接口类 implemented from ContextInterface */
|
||||||
|
$config['context_class'] = \ZM\Context\Context::class;
|
||||||
|
|
||||||
|
/* 静态文件访问 */
|
||||||
|
$config['static_file_server'] = [
|
||||||
|
'status' => false,
|
||||||
|
'document_root' => realpath(__DIR__ . '/../') . '/resources/html',
|
||||||
|
'document_index' => [
|
||||||
|
'index.html',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
/* 机器人解析模块,关闭后无法使用如CQCommand等注解(上面的modules即将废弃) */
|
||||||
|
$config['onebot'] = [
|
||||||
|
'status' => true,
|
||||||
|
'single_bot_mode' => false,
|
||||||
|
'message_level' => 99,
|
||||||
|
'message_convert_string' => true,
|
||||||
|
'message_command_policy' => 'interrupt',
|
||||||
|
];
|
||||||
|
|
||||||
|
/* 一个远程简易终端,使用nc直接连接即可,但是不建议开放host为0.0.0.0(远程连接) */
|
||||||
|
$config['remote_terminal'] = [
|
||||||
|
'status' => false,
|
||||||
|
'host' => '127.0.0.1',
|
||||||
|
'port' => 20002,
|
||||||
|
'token' => '',
|
||||||
|
];
|
||||||
|
|
||||||
|
/* 模块(插件)加载器的相关设置 */
|
||||||
|
$config['module_loader'] = [
|
||||||
|
'enable_hotload' => false,
|
||||||
|
'load_path' => $config['zm_data'] . 'modules',
|
||||||
|
];
|
||||||
|
|
||||||
|
return $config;
|
||||||
@ -12,3 +12,4 @@ parameters:
|
|||||||
- SWOOLE_VERSION
|
- SWOOLE_VERSION
|
||||||
- ZM_TEST_LOG_DEBUG
|
- ZM_TEST_LOG_DEBUG
|
||||||
- _PHAR_STUB_ID
|
- _PHAR_STUB_ID
|
||||||
|
- LOAD_MODE
|
||||||
|
|||||||
@ -1,29 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Custom\Annotation;
|
|
||||||
|
|
||||||
use Attribute;
|
|
||||||
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
|
|
||||||
use Doctrine\Common\Annotations\Annotation\Target;
|
|
||||||
use ZM\Annotation\AnnotationBase;
|
|
||||||
use ZM\Annotation\Interfaces\CustomAnnotation;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class CustomAnnotation
|
|
||||||
* @Annotation
|
|
||||||
* @NamedArgumentConstructor()
|
|
||||||
* @Target("ALL")
|
|
||||||
*/
|
|
||||||
#[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_ALL)]
|
|
||||||
class Example extends AnnotationBase implements CustomAnnotation
|
|
||||||
{
|
|
||||||
/** @var string */
|
|
||||||
public $str = '';
|
|
||||||
|
|
||||||
public function __construct($str = '')
|
|
||||||
{
|
|
||||||
$this->str = $str;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,10 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
use ZM\Utils\CoroutinePool;
|
|
||||||
|
|
||||||
function pgo(callable $func, string $name = 'default')
|
|
||||||
{
|
|
||||||
CoroutinePool::go($func, $name);
|
|
||||||
}
|
|
||||||
74
src/Globals/global_defines_app.php
Normal file
74
src/Globals/global_defines_app.php
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
此文件用于定义一些常量,是框架运行的前提常量,例如定义全局版本、全局方法等。
|
||||||
|
*/
|
||||||
|
|
||||||
|
use ZM\Framework;
|
||||||
|
|
||||||
|
/** 全局版本ID */
|
||||||
|
const ZM_VERSION_ID = Framework::VERSION_ID;
|
||||||
|
|
||||||
|
/** 全局版本名称 */
|
||||||
|
const ZM_VERSION = Framework::VERSION;
|
||||||
|
|
||||||
|
/** 机器人用的,用于判断二元语意 */
|
||||||
|
const TRUE_LIST = ['yes', 'y', 'true', 'on', '是', '对', true];
|
||||||
|
const FALSE_LIST = ['no', 'n', 'false', 'off', '否', '错', false];
|
||||||
|
|
||||||
|
/** 定义多进程的全局变量 */
|
||||||
|
const ZM_PROCESS_MASTER = ONEBOT_PROCESS_MASTER;
|
||||||
|
const ZM_PROCESS_MANAGER = ONEBOT_PROCESS_MANAGER;
|
||||||
|
const ZM_PROCESS_WORKER = ONEBOT_PROCESS_WORKER;
|
||||||
|
const ZM_PROCESS_USER = ONEBOT_PROCESS_USER;
|
||||||
|
const ZM_PROCESS_TASKWORKER = ONEBOT_PROCESS_TASKWORKER;
|
||||||
|
|
||||||
|
const ZM_PARSE_BEFORE_DRIVER = 0;
|
||||||
|
const ZM_PARSE_AFTER_DRIVER = 1;
|
||||||
|
const ZM_PARSE_BEFORE_START = 2;
|
||||||
|
|
||||||
|
/* 定义工作目录 */
|
||||||
|
define('WORKING_DIR', getcwd());
|
||||||
|
|
||||||
|
/* 定义源码根目录,如果是 Phar 打包框架运行的话,就是 Phar 文件本身 */
|
||||||
|
define('SOURCE_ROOT_DIR', Phar::running() !== '' ? Phar::running() : WORKING_DIR);
|
||||||
|
|
||||||
|
/* 定义启动模式,这里指的是框架本身的源码目录是通过 composer 加入 vendor 加载的还是直接放到 src 目录加载的,前者为 1,后者为 0 */
|
||||||
|
define('LOAD_MODE', is_dir(zm_dir(SOURCE_ROOT_DIR . '/src/ZM')) ? 0 : 1);
|
||||||
|
|
||||||
|
/* 定义框架本身所处的根目录,此处如果 LOAD_MODE 为 1 的话,框架自身的根目录在 vendor/zhamao/framework 子目录下 */
|
||||||
|
if (Phar::running() !== '') {
|
||||||
|
define('FRAMEWORK_ROOT_DIR', zm_dir(LOAD_MODE == 1 ? (SOURCE_ROOT_DIR . '/vendor/zhamao/framework') : SOURCE_ROOT_DIR));
|
||||||
|
} else {
|
||||||
|
define('FRAMEWORK_ROOT_DIR', realpath(zm_dir(__DIR__ . '/../../')));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 定义用于存放框架运行状态的目录(Windows 不可用) */
|
||||||
|
if (DIRECTORY_SEPARATOR !== '\\') {
|
||||||
|
define('ZM_PID_DIR', '/tmp/.zm_' . sha1(FRAMEWORK_ROOT_DIR));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 对 global.php 在 Windows 下的兼容性考虑,因为 Windows 或者无 Swoole 环境时候无法运行 */
|
||||||
|
!defined('SWOOLE_BASE') && define('SWOOLE_BASE', 1) && define('SWOOLE_PROCESS', 2);
|
||||||
|
|
||||||
|
!defined('SWOOLE_HOOK_ALL') && (
|
||||||
|
define('SWOOLE_HOOK_TCP', 2)
|
||||||
|
&& define('SWOOLE_HOOK_UDP', 4)
|
||||||
|
&& define('SWOOLE_HOOK_UNIX', 8)
|
||||||
|
&& define('SWOOLE_HOOK_UDG', 16)
|
||||||
|
&& define('SWOOLE_HOOK_SSL', 32)
|
||||||
|
&& define('SWOOLE_HOOK_TLS', 64)
|
||||||
|
&& define('SWOOLE_HOOK_STREAM_FUNCTION', 128)
|
||||||
|
&& define('SWOOLE_HOOK_STREAM_SELECT', 128)
|
||||||
|
&& define('SWOOLE_HOOK_FILE', 256)
|
||||||
|
&& define('SWOOLE_HOOK_STDIO', 32768)
|
||||||
|
&& define('SWOOLE_HOOK_SLEEP', 512)
|
||||||
|
&& define('SWOOLE_HOOK_PROC', 1024)
|
||||||
|
&& define('SWOOLE_HOOK_CURL', 2048)
|
||||||
|
&& define('SWOOLE_HOOK_NATIVE_CURL', 4096)
|
||||||
|
&& define('SWOOLE_HOOK_BLOCKING_FUNCTION', 8192)
|
||||||
|
&& define('SWOOLE_HOOK_SOCKETS', 16384)
|
||||||
|
&& define('SWOOLE_HOOK_ALL', 2147481599)
|
||||||
|
);
|
||||||
12
src/Globals/global_defines_framework.php
Normal file
12
src/Globals/global_defines_framework.php
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/** 定义炸毛框架初始启动时间 */
|
||||||
|
if (!defined('ZM_START_TIME')) {
|
||||||
|
define('ZM_START_TIME', microtime(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!defined('APP_VERSION')) {
|
||||||
|
define('APP_VERSION', LOAD_MODE == 1 ? (json_decode(file_get_contents(SOURCE_ROOT_DIR . '/composer.json'), true)['version'] ?? 'unknown') : 'unknown');
|
||||||
|
}
|
||||||
72
src/Globals/global_functions.php
Normal file
72
src/Globals/global_functions.php
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
use ZM\Logger\ConsoleLogger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据具体操作系统替换目录分隔符
|
||||||
|
*
|
||||||
|
* @param string $dir 目录
|
||||||
|
*/
|
||||||
|
function zm_dir(string $dir): string
|
||||||
|
{
|
||||||
|
if (strpos($dir, 'phar://') === 0) {
|
||||||
|
return $dir;
|
||||||
|
}
|
||||||
|
return str_replace('/', DIRECTORY_SEPARATOR, $dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取内部错误码
|
||||||
|
*
|
||||||
|
* @param int|string $code
|
||||||
|
*/
|
||||||
|
function zm_internal_errcode($code): string
|
||||||
|
{
|
||||||
|
return "[ErrCode:{$code}] ";
|
||||||
|
}
|
||||||
|
|
||||||
|
function zm_instance_id(): string
|
||||||
|
{
|
||||||
|
if (defined('ZM_INSTANCE_ID')) {
|
||||||
|
return ZM_INSTANCE_ID;
|
||||||
|
}
|
||||||
|
if (!defined('ZM_START_TIME')) {
|
||||||
|
define('ZM_START_TIME', microtime(true));
|
||||||
|
}
|
||||||
|
$instance_id = dechex(crc32(strval(ZM_START_TIME)));
|
||||||
|
define('ZM_INSTANCE_ID', $instance_id);
|
||||||
|
return ZM_INSTANCE_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 助手方法,返回一个 Logger 实例
|
||||||
|
*/
|
||||||
|
function logger(): LoggerInterface
|
||||||
|
{
|
||||||
|
global $ob_logger;
|
||||||
|
if ($ob_logger === null) {
|
||||||
|
return new ConsoleLogger();
|
||||||
|
}
|
||||||
|
return $ob_logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断传入的数组是否为关联数组
|
||||||
|
*/
|
||||||
|
function is_assoc_array(array $array): bool
|
||||||
|
{
|
||||||
|
return !empty($array) && array_keys($array) !== range(0, count($array) - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return object
|
||||||
|
*
|
||||||
|
* TODO: 等待完善DI
|
||||||
|
*/
|
||||||
|
function resolve(string $class)
|
||||||
|
{
|
||||||
|
return new $class();
|
||||||
|
}
|
||||||
71
src/Globals/script_setup_loader.php
Normal file
71
src/Globals/script_setup_loader.php
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use Doctrine\Common\Annotations\AnnotationReader;
|
||||||
|
use Koriym\Attributes\AttributeReader;
|
||||||
|
use Koriym\Attributes\DualReader;
|
||||||
|
use ZM\Annotation\Framework\OnSetup;
|
||||||
|
use ZM\ConsoleApplication;
|
||||||
|
use ZM\Exception\InitException;
|
||||||
|
use ZM\Store\FileSystem;
|
||||||
|
|
||||||
|
function _zm_setup_loader()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
try {
|
||||||
|
new ConsoleApplication('zhamao');
|
||||||
|
} catch (InitException $e) {
|
||||||
|
}
|
||||||
|
$base_path = SOURCE_ROOT_DIR;
|
||||||
|
$scan_paths = [];
|
||||||
|
$composer = json_decode(file_get_contents($base_path . '/composer.json'), true);
|
||||||
|
$exclude_annotations = array_merge($composer['extra']['exclude_annotate'] ?? [], $composer['extra']['zm']['exclude-annotation-path'] ?? []);
|
||||||
|
foreach (($composer['autoload']['psr-4'] ?? []) as $k => $v) {
|
||||||
|
if (is_dir($base_path . '/' . $v) && !in_array($v, $exclude_annotations)) {
|
||||||
|
$scan_paths[trim($k, '\\')] = $base_path . '/' . $v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach (($composer['autoload-dev']['psr-4'] ?? []) as $k => $v) {
|
||||||
|
if (is_dir($base_path . '/' . $v) && !in_array($v, $exclude_annotations)) {
|
||||||
|
$scan_paths[trim($k, '\\')] = $base_path . '/' . $v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$all_event_class = [];
|
||||||
|
foreach ($scan_paths as $namespace => $autoload_path) {
|
||||||
|
$all_event_class = array_merge($all_event_class, FileSystem::getClassesPsr4($autoload_path, $namespace));
|
||||||
|
}
|
||||||
|
|
||||||
|
$reader = new DualReader(new AnnotationReader(), new AttributeReader());
|
||||||
|
$event_list = [];
|
||||||
|
$setup_list = [];
|
||||||
|
foreach ($all_event_class as $v) {
|
||||||
|
$reflection_class = new ReflectionClass($v);
|
||||||
|
$methods = $reflection_class->getMethods(ReflectionMethod::IS_PUBLIC);
|
||||||
|
foreach ($methods as $vs) {
|
||||||
|
$method_annotations = $reader->getMethodAnnotations($vs);
|
||||||
|
if ($method_annotations != []) {
|
||||||
|
$annotation = $method_annotations[0];
|
||||||
|
if ($annotation instanceof OnSetup) {
|
||||||
|
$setup_list[] = [
|
||||||
|
'class' => $v,
|
||||||
|
'method' => $vs->getName(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return json_encode(['setup' => $setup_list, 'event' => $event_list]);
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
$stderr = fopen('php://stderr', 'w');
|
||||||
|
fwrite($stderr, zm_internal_errcode('E00031') . $e->getMessage() . ' in ' . $e->getFile() . ' at line ' . $e->getLine() . PHP_EOL);
|
||||||
|
fclose($stderr);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 在*nix等支持多进程环境的情况,可直接运行此文件,那么就执行
|
||||||
|
if (debug_backtrace() === []) {
|
||||||
|
require((!is_dir(__DIR__ . '/../../vendor')) ? getcwd() : (__DIR__ . '/../..')) . '/vendor/autoload.php';
|
||||||
|
echo _zm_setup_loader();
|
||||||
|
}
|
||||||
@ -1,264 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Module\Example;
|
|
||||||
|
|
||||||
use ZM\Annotation\CQ\CommandArgument;
|
|
||||||
use ZM\Annotation\CQ\CQBefore;
|
|
||||||
use ZM\Annotation\CQ\CQCommand;
|
|
||||||
use ZM\Annotation\CQ\CQMessage;
|
|
||||||
use ZM\Annotation\Http\Middleware;
|
|
||||||
use ZM\Annotation\Http\RequestMapping;
|
|
||||||
use ZM\Annotation\Swoole\OnCloseEvent;
|
|
||||||
use ZM\Annotation\Swoole\OnOpenEvent;
|
|
||||||
use ZM\Annotation\Swoole\OnRequestEvent;
|
|
||||||
use ZM\API\CQ;
|
|
||||||
use ZM\API\OneBotV11;
|
|
||||||
use ZM\API\TuringAPI;
|
|
||||||
use ZM\Config\ZMConfig;
|
|
||||||
use ZM\ConnectionManager\ConnectionObject;
|
|
||||||
use ZM\Context\Context;
|
|
||||||
use ZM\Event\EventDispatcher;
|
|
||||||
use ZM\Exception\InterruptException;
|
|
||||||
use ZM\Module\QQBot;
|
|
||||||
use ZM\Requests\ZMRequest;
|
|
||||||
use ZM\Utils\CommandInfoUtil;
|
|
||||||
use ZM\Utils\MessageUtil;
|
|
||||||
use ZM\Utils\ZMUtil;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class Hello
|
|
||||||
*
|
|
||||||
* @since 2.0
|
|
||||||
*/
|
|
||||||
class Hello
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* 默认的图片监听路由对应目录,如需要使用可取消下面的注释,把上面的 /* 换成 /**
|
|
||||||
* @OnStart(-1)
|
|
||||||
*/
|
|
||||||
// public function onStart() {
|
|
||||||
// \ZM\Http\RouteManager::addStaticFileRoute("/images/", \ZM\Utils\DataProvider::getWorkingDir()."/zm_data/images/");
|
|
||||||
// }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 使用命令 .reload 发给机器人远程重载,注意将 user_id 换成你自己的 QQ
|
|
||||||
* @CQCommand(".reload",user_id=627577391)
|
|
||||||
*/
|
|
||||||
public function reload()
|
|
||||||
{
|
|
||||||
ctx()->reply('重启中...');
|
|
||||||
ZMUtil::reload();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @CQCommand("我是谁")
|
|
||||||
*/
|
|
||||||
public function whoami()
|
|
||||||
{
|
|
||||||
$bot = ctx()->getRobot()->getLoginInfo();
|
|
||||||
$bot_id = $bot['data']['user_id'];
|
|
||||||
$r = OneBotV11::get($bot_id);
|
|
||||||
$QQid = ctx()->getUserId();
|
|
||||||
$nick = $r->getStrangerInfo($QQid)['data']['nickname'];
|
|
||||||
return '你是' . $nick . ',QQ号是' . $QQid;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 向机器人发送"你好啊",也可回复这句话
|
|
||||||
* @CQCommand(match="你好",alias={"你好啊","你是谁"})
|
|
||||||
*/
|
|
||||||
public function hello()
|
|
||||||
{
|
|
||||||
return '你好啊,我是由炸毛框架构建的机器人!';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 一个最基本的第三方 API 接口使用示例
|
|
||||||
* @CQCommand("一言")
|
|
||||||
*/
|
|
||||||
public function hitokoto()
|
|
||||||
{
|
|
||||||
$api_result = ZMRequest::get('https://v1.hitokoto.cn/');
|
|
||||||
if ($api_result === false) {
|
|
||||||
return '接口请求出错,请稍后再试!';
|
|
||||||
}
|
|
||||||
$obj = json_decode($api_result, true);
|
|
||||||
if ($obj === null) {
|
|
||||||
return '接口解析出错!可能返回了非法数据!';
|
|
||||||
}
|
|
||||||
return $obj['hitokoto'] . "\n----「" . $obj['from'] . '」';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 图灵机器人的内置实现,在tuling123.com申请一个apikey填入下方变量即可。
|
|
||||||
* @CQCommand(start_with="机器人",end_with="机器人",message_type="group")
|
|
||||||
* @CQMessage(message_type="private",level=1)
|
|
||||||
*/
|
|
||||||
public function turingAPI()
|
|
||||||
{
|
|
||||||
$user_id = ctx()->getUserId();
|
|
||||||
$api = ZMConfig::get('global', 'custom.turing_apikey') ?? ''; // 请在这里填入你的图灵机器人的apikey
|
|
||||||
if ($api === '') {
|
|
||||||
return false;
|
|
||||||
} // 如果没有填入apikey则此功能关闭
|
|
||||||
if (property_exists($this, '_running_annotation') && ($this->_running_annotation instanceof CQCommand)) {
|
|
||||||
$msg = ctx()->getFullArg('我在!有什么事吗?');
|
|
||||||
} else {
|
|
||||||
$msg = ctx()->getMessage();
|
|
||||||
}
|
|
||||||
ctx()->setMessage($msg);
|
|
||||||
if (MessageUtil::matchCommand($msg, ctx()->getData())->status === false) {
|
|
||||||
return TuringAPI::getTuringMsg($msg, $user_id, $api);
|
|
||||||
}
|
|
||||||
QQBot::getInstance()->handle(ctx()->getData(), ctx()->getCache('level') + 1);
|
|
||||||
// 执行嵌套消息,递归层级+1
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 响应at机器人的消息
|
|
||||||
* @CQBefore("message")
|
|
||||||
*/
|
|
||||||
public function changeAt()
|
|
||||||
{
|
|
||||||
if (MessageUtil::isAtMe(ctx()->getMessage(), ctx()->getRobotId())) {
|
|
||||||
$msg = str_replace(CQ::at(ctx()->getRobotId()), '', ctx()->getMessage());
|
|
||||||
ctx()->setMessage('机器人' . $msg);
|
|
||||||
logger()->info(ctx()->getMessage());
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 一个简单随机数的功能demo
|
|
||||||
* 问法1:随机数 1 20
|
|
||||||
* 问法2:从1到20的随机数
|
|
||||||
* @CQCommand("随机数")
|
|
||||||
* @CQCommand(pattern="*从*到*的随机数")
|
|
||||||
* @CommandArgument(name="num1",type="int",required=true)
|
|
||||||
* @CommandArgument(name="num2",type="int",required=true)
|
|
||||||
* @param mixed $num1
|
|
||||||
* @param mixed $num2
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function randNum($num1, $num2)
|
|
||||||
{
|
|
||||||
$a = min($num1, $num2);
|
|
||||||
$b = max($num1, $num2);
|
|
||||||
// 回复用户结果
|
|
||||||
return '从' . $a . '到' . $b . '的随机数是:' . mt_rand($a, $b);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 中间件测试的一个示例函数
|
|
||||||
* @RequestMapping("/httpTimer")
|
|
||||||
* @Middleware("timer")
|
|
||||||
*/
|
|
||||||
public function timer()
|
|
||||||
{
|
|
||||||
return 'This page is used as testing TimerMiddleware! Do not use it in production.';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 默认示例页面
|
|
||||||
* @RequestMapping("/index")
|
|
||||||
* @RequestMapping("/")
|
|
||||||
*/
|
|
||||||
public function index()
|
|
||||||
{
|
|
||||||
return 'Hello Zhamao!';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 使用自定义参数的路由参数
|
|
||||||
* @RequestMapping("/whoami/{name}")
|
|
||||||
*
|
|
||||||
* @param array $param 参数
|
|
||||||
* @return string 返回的 HTML Body
|
|
||||||
*/
|
|
||||||
public function paramGet(array $param = []): string
|
|
||||||
{
|
|
||||||
return 'Hello, ' . $param['name'];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 在机器人连接后向终端输出信息
|
|
||||||
* @OnOpenEvent("qq")
|
|
||||||
*
|
|
||||||
* @param ConnectionObject $conn WebSocket 连接对象
|
|
||||||
*/
|
|
||||||
public function onConnect(ConnectionObject $conn)
|
|
||||||
{
|
|
||||||
logger()->info('机器人 ' . $conn->getOption('connect_id') . ' 已连接!');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 在机器人断开连接后向终端输出信息
|
|
||||||
* @OnCloseEvent("qq")
|
|
||||||
*/
|
|
||||||
public function onDisconnect(ConnectionObject $conn)
|
|
||||||
{
|
|
||||||
logger()->info('机器人 ' . $conn->getOption('connect_id') . ' 已断开连接!');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 阻止 Chrome 自动请求 /favicon.ico 导致的多条请求并发和干扰
|
|
||||||
* @OnRequestEvent(rule="ctx()->getRequest()->server['request_uri'] == '/favicon.ico'",level=200)
|
|
||||||
*
|
|
||||||
* @throws InterruptException
|
|
||||||
*/
|
|
||||||
public function onRequest()
|
|
||||||
{
|
|
||||||
EventDispatcher::interrupt();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 框架会默认关闭未知的WebSocket链接,因为这个绑定的事件,你可以根据你自己的需求进行修改
|
|
||||||
* @OnOpenEvent("default")
|
|
||||||
*/
|
|
||||||
public function closeUnknownConn()
|
|
||||||
{
|
|
||||||
logger()->info('发现了未知的 Websocket 连接,正在断开');
|
|
||||||
server()->disconnect(ctx()->getConnection()->getFd());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 输出帮助信息
|
|
||||||
*
|
|
||||||
* @CQCommand("帮助")
|
|
||||||
*/
|
|
||||||
#[CQCommand('帮助')]
|
|
||||||
public function help(): string
|
|
||||||
{
|
|
||||||
$util = resolve(CommandInfoUtil::class);
|
|
||||||
$commands = $util->get();
|
|
||||||
$helps = array_map(static function ($command) use ($util) {
|
|
||||||
return $util->getHelp($command['id']);
|
|
||||||
}, $commands);
|
|
||||||
array_unshift($helps, '帮助:');
|
|
||||||
return implode("\n", $helps);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @CQCommand("proxy")
|
|
||||||
*/
|
|
||||||
#[CQCommand('proxy')]
|
|
||||||
public function proxy()
|
|
||||||
{
|
|
||||||
bot()->all()->allGroups()->sendGroupMsg(0, ctx()->getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 欢迎来到容器时代
|
|
||||||
*
|
|
||||||
* @param Context $context 通过依赖注入实现的
|
|
||||||
*
|
|
||||||
* @CQCommand("容器你好")
|
|
||||||
*/
|
|
||||||
public function welcomeToContainerAge(Context $context)
|
|
||||||
{
|
|
||||||
$context->reply('欢迎来到容器时代');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,49 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Module\Middleware;
|
|
||||||
|
|
||||||
use Exception;
|
|
||||||
use ZM\Annotation\Http\HandleAfter;
|
|
||||||
use ZM\Annotation\Http\HandleBefore;
|
|
||||||
use ZM\Annotation\Http\HandleException;
|
|
||||||
use ZM\Annotation\Http\MiddlewareClass;
|
|
||||||
use ZM\Http\MiddlewareInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class TimerMiddleware
|
|
||||||
* 示例中间件:用于统计路由函数运行时间用的
|
|
||||||
* @MiddlewareClass("timer")
|
|
||||||
*/
|
|
||||||
class TimerMiddleware implements MiddlewareInterface
|
|
||||||
{
|
|
||||||
private $starttime;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @HandleBefore()
|
|
||||||
*/
|
|
||||||
public function onBefore(): bool
|
|
||||||
{
|
|
||||||
$this->starttime = microtime(true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @HandleAfter()
|
|
||||||
*/
|
|
||||||
public function onAfter()
|
|
||||||
{
|
|
||||||
logger()->info('Using ' . round((microtime(true) - $this->starttime) * 1000, 3) . ' ms.');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @HandleException(\Exception::class)
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
public function onException(Exception $e)
|
|
||||||
{
|
|
||||||
logger()->error('Using ' . round((microtime(true) - $this->starttime) * 1000, 3) . ' ms but an Exception occurred.');
|
|
||||||
throw $e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,433 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\API;
|
|
||||||
|
|
||||||
use Stringable;
|
|
||||||
use ZM\Entity\CQObject;
|
|
||||||
|
|
||||||
class CQ
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* at一下QQ用户(仅在QQ群支持at全体)
|
|
||||||
* @param int|string $qq 用户QQ号/ID号
|
|
||||||
* @return string CQ码
|
|
||||||
*/
|
|
||||||
public static function at($qq): string
|
|
||||||
{
|
|
||||||
return self::buildCQ('at', ['qq' => $qq]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 发送QQ原生表情
|
|
||||||
* @param int|string $id 表情ID
|
|
||||||
* @return string CQ码
|
|
||||||
*/
|
|
||||||
public static function face($id): string
|
|
||||||
{
|
|
||||||
return self::buildCQ('face', ['id' => $id]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 发送图片
|
|
||||||
* @param string $file 文件的路径、URL或者base64编码的图片数据
|
|
||||||
* @param bool $cache 是否缓存(默认为true)
|
|
||||||
* @param bool $flash 是否闪照(默认为false)
|
|
||||||
* @param bool $proxy 是否使用代理(默认为true)
|
|
||||||
* @param int $timeout 超时时间(默认不超时)
|
|
||||||
* @return string CQ码
|
|
||||||
*/
|
|
||||||
public static function image(string $file, bool $cache = true, bool $flash = false, bool $proxy = true, int $timeout = -1): string
|
|
||||||
{
|
|
||||||
$optional_values = [
|
|
||||||
'cache' => !$cache ? 'cache=0' : '',
|
|
||||||
'flash' => $flash ? 'type=flash' : '',
|
|
||||||
'proxy' => !$proxy ? 'proxy=false' : '',
|
|
||||||
'timeout' => $timeout != -1 ? 'timeout=' . $timeout : '',
|
|
||||||
];
|
|
||||||
return self::buildCQ('image', ['file' => $file], $optional_values);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 发送语音
|
|
||||||
* @param string $file 文件的路径、URL或者base64编码的语音数据
|
|
||||||
* @param bool $magic 是否加特技(默认为false)
|
|
||||||
* @param bool $cache 是否缓存(默认为true)
|
|
||||||
* @param bool $proxy 是否使用代理(默认为true)
|
|
||||||
* @param int $timeout 超时时间(默认不超时)
|
|
||||||
* @return string CQ码
|
|
||||||
*/
|
|
||||||
public static function record(string $file, bool $magic = false, bool $cache = true, bool $proxy = true, int $timeout = -1): string
|
|
||||||
{
|
|
||||||
$optional_values = [
|
|
||||||
'magic' => $magic ? 'magic=true' : '',
|
|
||||||
'cache' => !$cache ? 'cache=0' : '',
|
|
||||||
'proxy' => !$proxy ? 'proxy=false' : '',
|
|
||||||
'timeout' => $timeout != -1 ? 'timeout=' . $timeout : '',
|
|
||||||
];
|
|
||||||
return self::buildCQ('record', ['file' => $file], $optional_values);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 发送短视频
|
|
||||||
* @param string $file 文件的路径、URL或者base64编码的短视频数据
|
|
||||||
* @param bool $cache 是否缓存(默认为true)
|
|
||||||
* @param bool $proxy 是否使用代理(默认为true)
|
|
||||||
* @param int $timeout 超时时间(默认不超时)
|
|
||||||
* @return string CQ码
|
|
||||||
*/
|
|
||||||
public static function video(string $file, bool $cache = true, bool $proxy = true, int $timeout = -1): string
|
|
||||||
{
|
|
||||||
$optional_values = [
|
|
||||||
'cache' => !$cache ? 'cache=0' : '',
|
|
||||||
'proxy' => !$proxy ? 'proxy=false' : '',
|
|
||||||
'timeout' => $timeout != -1 ? 'timeout=' . $timeout : '',
|
|
||||||
];
|
|
||||||
return self::buildCQ('video', ['file' => $file], $optional_values);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 发送投掷骰子(只能在单条回复中单独使用)
|
|
||||||
* @return string CQ码
|
|
||||||
*/
|
|
||||||
public static function rps(): string
|
|
||||||
{
|
|
||||||
return '[CQ:rps]';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 发送掷骰子表情(只能在单条回复中单独使用)
|
|
||||||
* @return string CQ码
|
|
||||||
*/
|
|
||||||
public static function dice(): string
|
|
||||||
{
|
|
||||||
return '[CQ:dice]';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 戳一戳(原窗口抖动,仅支持好友消息使用)
|
|
||||||
* @return string CQ码
|
|
||||||
*/
|
|
||||||
public static function shake(): string
|
|
||||||
{
|
|
||||||
return '[CQ:shake]';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 发送新的戳一戳
|
|
||||||
* @param int|string $type 焯一戳类型
|
|
||||||
* @param int|string $id 戳一戳ID号
|
|
||||||
* @param string $name 戳一戳名称(可选)
|
|
||||||
* @return string CQ码
|
|
||||||
*/
|
|
||||||
public static function poke($type, $id, string $name = ''): string
|
|
||||||
{
|
|
||||||
$optional_values = [
|
|
||||||
'name' => $name ? 'name=' . $name : '',
|
|
||||||
];
|
|
||||||
return self::buildCQ('poke', ['type' => $type, 'id' => $id], $optional_values);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 发送匿名消息
|
|
||||||
* @param int $ignore 是否忽略错误(默认为1,0表示不忽略错误)
|
|
||||||
* @return string CQ码
|
|
||||||
*/
|
|
||||||
public static function anonymous(int $ignore = 1): string
|
|
||||||
{
|
|
||||||
return self::buildCQ('anonymous', [], ['ignore' => $ignore != 1 ? 'ignore=0' : '']);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 发送链接分享(只能在单条回复中单独使用)
|
|
||||||
* @param string $url 分享地址
|
|
||||||
* @param string $title 标题
|
|
||||||
* @param null|string $content 卡片内容(可选)
|
|
||||||
* @param null|string $image 卡片图片(可选)
|
|
||||||
* @return string CQ码
|
|
||||||
*/
|
|
||||||
public static function share(string $url, string $title, ?string $content = null, ?string $image = null): string
|
|
||||||
{
|
|
||||||
$optional_values = [
|
|
||||||
'content' => $content ? 'content=' . self::encode($content, true) : '',
|
|
||||||
'image' => $image ? 'image=' . self::encode($image, true) : '',
|
|
||||||
];
|
|
||||||
return self::buildCQ('share', ['url' => $url, 'title' => $title], $optional_values);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 发送好友或群推荐名片
|
|
||||||
* @param int|string $type 名片类型
|
|
||||||
* @param int|string $id 好友或群ID
|
|
||||||
* @return string CQ码
|
|
||||||
*/
|
|
||||||
public static function contact($type, $id): string
|
|
||||||
{
|
|
||||||
return self::buildCQ('contact', ['type' => $type, 'id' => $id]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 发送位置
|
|
||||||
* @param float|string $lat 纬度
|
|
||||||
* @param float|string $lon 经度
|
|
||||||
* @param string $title 标题(可选)
|
|
||||||
* @param string $content 卡片内容(可选)
|
|
||||||
* @return string CQ码
|
|
||||||
*/
|
|
||||||
public static function location($lat, $lon, string $title = '', string $content = ''): string
|
|
||||||
{
|
|
||||||
$optional_values = [
|
|
||||||
'title' => $title ? 'title=' . self::encode($title, true) : '',
|
|
||||||
'content' => $content ? 'content=' . self::encode($content, true) : '',
|
|
||||||
];
|
|
||||||
return self::buildCQ('location', ['lat' => $lat, 'lon' => $lon], $optional_values);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 发送音乐分享(只能在单条回复中单独使用)
|
|
||||||
*
|
|
||||||
* qq、163、xiami为内置分享,需要先通过搜索功能获取id后使用
|
|
||||||
*
|
|
||||||
* @param string $type 分享类型(仅限 `qq`、`163`、`xiami` 或 `custom`)
|
|
||||||
* @param int|string $id_or_url 当分享类型不是 `custom` 时,表示的是分享音乐的ID(需要先通过搜索功能获取id后使用),反之表示的是音乐卡片点入的链接
|
|
||||||
* @param null|string $audio 当分享类型是 `custom` 时,表示为音乐(如mp3文件)的HTTP链接地址(不可为空)
|
|
||||||
* @param null|string $title 当分享类型是 `custom` 时,表示为音乐卡片的标题,建议12字以内(不可为空)
|
|
||||||
* @param null|string $content 当分享类型是 `custom` 时,表示为音乐卡片的简介(可忽略)
|
|
||||||
* @param null|string $image 当分享类型是 `custom` 时,表示为音乐卡片的图片链接地址(可忽略)
|
|
||||||
* @return string CQ码
|
|
||||||
*/
|
|
||||||
public static function music(string $type, $id_or_url, ?string $audio = null, ?string $title = null, ?string $content = null, ?string $image = null): string
|
|
||||||
{
|
|
||||||
switch ($type) {
|
|
||||||
case 'qq':
|
|
||||||
case '163':
|
|
||||||
case 'xiami':
|
|
||||||
return self::buildCQ('music', ['type' => $type, 'id' => $id_or_url]);
|
|
||||||
case 'custom':
|
|
||||||
if ($title === null || $audio === null) {
|
|
||||||
logger()->warning(zm_internal_errcode('E00035') . '传入CQ码实例的标题和音频链接不能为空!');
|
|
||||||
return ' ';
|
|
||||||
}
|
|
||||||
$optional_values = [
|
|
||||||
'content' => $content ? 'content=' . self::encode($content, true) : '',
|
|
||||||
'image' => $image ? 'image=' . self::encode($image, true) : '',
|
|
||||||
];
|
|
||||||
return self::buildCQ('music', ['type' => 'custom', 'url' => $id_or_url, 'audio' => $audio, 'title' => $title], $optional_values);
|
|
||||||
default:
|
|
||||||
logger()->warning(zm_internal_errcode('E00035') . "传入的music type({$type})错误!");
|
|
||||||
return ' ';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 合并转发消息
|
|
||||||
* @param int|string $id 合并转发ID, 需要通过 `/get_forward_msg` API获取转发的具体内容
|
|
||||||
* @return string CQ码
|
|
||||||
*/
|
|
||||||
public static function forward($id): string
|
|
||||||
{
|
|
||||||
return self::buildCQ('forward', ['id' => $id]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 合并转发消息节点
|
|
||||||
* 特殊说明: 需要使用单独的API /send_group_forward_msg 发送, 并且由于消息段较为复杂, 仅支持Array形式入参。
|
|
||||||
* 如果引用消息和自定义消息同时出现, 实际查看顺序将取消息段顺序。
|
|
||||||
* 另外按 CQHTTP 文档说明, data 应全为字符串, 但由于需要接收message 类型的消息, 所以 仅限此Type的content字段 支持Array套娃
|
|
||||||
* @param int|string $user_id 转发消息id
|
|
||||||
* @param string $nickname 发送者显示名字
|
|
||||||
* @param string $content 具体消息
|
|
||||||
* @return string CQ码
|
|
||||||
* @deprecated 这个不推荐使用,因为 go-cqhttp 官方没有对其提供CQ码模式相关支持,仅支持Array模式发送
|
|
||||||
*/
|
|
||||||
public static function node($user_id, string $nickname, string $content): string
|
|
||||||
{
|
|
||||||
return self::buildCQ('node', ['user_id' => $user_id, 'nickname' => $nickname, 'content' => $content]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* XML消息
|
|
||||||
* @param string $data xml内容, xml中的value部分
|
|
||||||
* @return string CQ码
|
|
||||||
*/
|
|
||||||
public static function xml(string $data): string
|
|
||||||
{
|
|
||||||
return self::buildCQ('xml', ['data' => $data]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* JSON消息
|
|
||||||
* @param string $data json内容
|
|
||||||
* @param int $resid 0为走小程序通道,其他值为富文本通道(默认为0)
|
|
||||||
* @return string CQ码
|
|
||||||
*/
|
|
||||||
public static function json(string $data, int $resid = 0): string
|
|
||||||
{
|
|
||||||
return self::buildCQ('json', ['data' => $data, 'resid' => $resid]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 返回一个自定义扩展的CQ码(支持自定义类型和参数)
|
|
||||||
* @param string $type_name CQ码类型名称
|
|
||||||
* @param array $params 参数
|
|
||||||
* @return string CQ码
|
|
||||||
*/
|
|
||||||
public static function _custom(string $type_name, array $params): string
|
|
||||||
{
|
|
||||||
return self::buildCQ($type_name, $params);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 反转义字符串中的CQ码敏感符号
|
|
||||||
* @param int|string|Stringable $msg 字符串
|
|
||||||
* @param bool $is_content 如果是解码CQ码本体内容,则为false(默认),如果是参数内的字符串,则为true
|
|
||||||
* @return string 转义后的CQ码
|
|
||||||
*/
|
|
||||||
public static function decode($msg, bool $is_content = false): string
|
|
||||||
{
|
|
||||||
$msg = str_replace(['&', '[', ']'], ['&', '[', ']'], (string) $msg);
|
|
||||||
if ($is_content) {
|
|
||||||
$msg = str_replace(',', ',', $msg);
|
|
||||||
}
|
|
||||||
return $msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 简单反转义替换CQ码的方括号
|
|
||||||
* @param int|string|Stringable $str 字符串
|
|
||||||
* @return string 字符串
|
|
||||||
*/
|
|
||||||
public static function replace($str): string
|
|
||||||
{
|
|
||||||
$str = str_replace('{{', '[', (string) $str);
|
|
||||||
return str_replace('}}', ']', $str);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 转义CQ码的特殊字符,同encode
|
|
||||||
* @param int|string|Stringable $msg 字符串
|
|
||||||
* @param bool $is_content 如果是转义CQ码本体内容,则为false(默认),如果是参数内的字符串,则为true
|
|
||||||
* @return string 转义后的CQ码
|
|
||||||
*/
|
|
||||||
public static function escape($msg, bool $is_content = false): string
|
|
||||||
{
|
|
||||||
$msg = str_replace(['&', '[', ']'], ['&', '[', ']'], (string) $msg);
|
|
||||||
if ($is_content) {
|
|
||||||
$msg = str_replace(',', ',', $msg);
|
|
||||||
}
|
|
||||||
return $msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 转义CQ码的特殊字符
|
|
||||||
* @param int|string|Stringable $msg 字符串
|
|
||||||
* @param bool $is_content 如果是转义CQ码本体内容,则为false(默认),如果是参数内的字符串,则为true
|
|
||||||
* @return string 转义后的CQ码
|
|
||||||
*/
|
|
||||||
public static function encode($msg, bool $is_content = false): string
|
|
||||||
{
|
|
||||||
$msg = str_replace(['&', '[', ']'], ['&', '[', ']'], (string) $msg);
|
|
||||||
if ($is_content) {
|
|
||||||
$msg = str_replace(',', ',', $msg);
|
|
||||||
}
|
|
||||||
return $msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 移除消息中所有的CQ码并返回移除CQ码后的消息
|
|
||||||
* @param string $msg 消息
|
|
||||||
* @return string 消息内容
|
|
||||||
*/
|
|
||||||
public static function removeCQ(string $msg): string
|
|
||||||
{
|
|
||||||
$final = '';
|
|
||||||
$last_end = 0;
|
|
||||||
foreach (self::getAllCQ($msg) as $v) {
|
|
||||||
$final .= mb_substr($msg, $last_end, $v['start'] - $last_end);
|
|
||||||
$last_end = $v['end'] + 1;
|
|
||||||
}
|
|
||||||
$final .= mb_substr($msg, $last_end);
|
|
||||||
return $final;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取消息中第一个CQ码
|
|
||||||
* @param string $msg 消息内容
|
|
||||||
* @param bool $is_object 是否以对象形式返回,如果为False的话,返回数组形式(默认为false)
|
|
||||||
* @return null|array|CQObject 返回的CQ码(数组或对象)
|
|
||||||
*/
|
|
||||||
public static function getCQ(string $msg, bool $is_object = false)
|
|
||||||
{
|
|
||||||
if (($head = mb_strpos($msg, '[CQ:')) !== false) {
|
|
||||||
$key_offset = mb_substr($msg, $head);
|
|
||||||
$close = mb_strpos($key_offset, ']');
|
|
||||||
if ($close === false) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
$content = mb_substr($msg, $head + 4, $close + $head - mb_strlen($msg));
|
|
||||||
$exp = explode(',', $content);
|
|
||||||
$cq['type'] = array_shift($exp);
|
|
||||||
foreach ($exp as $v) {
|
|
||||||
$ss = explode('=', $v);
|
|
||||||
$sk = array_shift($ss);
|
|
||||||
$cq['params'][$sk] = self::decode(implode('=', $ss), true);
|
|
||||||
}
|
|
||||||
$cq['start'] = $head;
|
|
||||||
$cq['end'] = $close + $head;
|
|
||||||
return !$is_object ? $cq : CQObject::fromArray($cq);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取消息中所有的CQ码
|
|
||||||
* @param string $msg 消息内容
|
|
||||||
* @param bool $is_object 是否以对象形式返回,如果为False的话,返回数组形式(默认为false)
|
|
||||||
* @return array|CQObject[] 返回的CQ码们(数组或对象)
|
|
||||||
*/
|
|
||||||
public static function getAllCQ(string $msg, bool $is_object = false): array
|
|
||||||
{
|
|
||||||
$cqs = [];
|
|
||||||
$offset = 0;
|
|
||||||
while (($head = mb_strpos(($submsg = mb_substr($msg, $offset)), '[CQ:')) !== false) {
|
|
||||||
$key_offset = mb_substr($submsg, $head);
|
|
||||||
$tmpmsg = mb_strpos($key_offset, ']');
|
|
||||||
if ($tmpmsg === false) {
|
|
||||||
break;
|
|
||||||
} // 没闭合,不算CQ码
|
|
||||||
$content = mb_substr($submsg, $head + 4, $tmpmsg + $head - mb_strlen($submsg));
|
|
||||||
$exp = explode(',', $content);
|
|
||||||
$cq = [];
|
|
||||||
$cq['type'] = array_shift($exp);
|
|
||||||
foreach ($exp as $v) {
|
|
||||||
$ss = explode('=', $v);
|
|
||||||
$sk = array_shift($ss);
|
|
||||||
$cq['params'][$sk] = self::decode(implode('=', $ss), true);
|
|
||||||
}
|
|
||||||
$cq['start'] = $offset + $head;
|
|
||||||
$cq['end'] = $offset + $tmpmsg + $head;
|
|
||||||
$offset += $head + $tmpmsg + 1;
|
|
||||||
$cqs[] = (!$is_object ? $cq : CQObject::fromArray($cq));
|
|
||||||
}
|
|
||||||
return $cqs;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function buildCQ(string $cq, array $array, array $optional_values = []): string
|
|
||||||
{
|
|
||||||
$str = '[CQ:' . $cq;
|
|
||||||
foreach ($array as $k => $v) {
|
|
||||||
if ($v === null) {
|
|
||||||
logger()->warning('param ' . $k . ' cannot be set with null, empty CQ will returned!');
|
|
||||||
return ' ';
|
|
||||||
}
|
|
||||||
$str .= ',' . $k . '=' . self::encode($v);
|
|
||||||
}
|
|
||||||
foreach ($optional_values as $v) {
|
|
||||||
if ($v !== '') {
|
|
||||||
$str .= ',' . $v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $str . ']';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,91 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\API;
|
|
||||||
|
|
||||||
use Closure;
|
|
||||||
use ZM\Store\LightCacheInside;
|
|
||||||
use ZM\Store\Lock\SpinLock;
|
|
||||||
use ZM\Store\ZMAtomic;
|
|
||||||
use ZM\Utils\CoMessage;
|
|
||||||
|
|
||||||
trait CQAPI
|
|
||||||
{
|
|
||||||
/** @var null|Closure 过滤用的 */
|
|
||||||
private static $filter;
|
|
||||||
|
|
||||||
public function __call($name, $arguments)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function registerFilter(callable $callable)
|
|
||||||
{
|
|
||||||
self::$filter = $callable;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getActionName($suffix, string $method)
|
|
||||||
{
|
|
||||||
$postfix = ($suffix == OneBotV11::API_ASYNC ? '_async' : ($suffix == OneBotV11::API_RATE_LIMITED ? '_rate_limited' : ''));
|
|
||||||
$func_name = strtolower(preg_replace('/(?<=[a-z])([A-Z])/', '_$1', $method));
|
|
||||||
return $func_name . $postfix;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function processAPI($connection, $reply, $function = null)
|
|
||||||
{
|
|
||||||
if (is_callable(self::$filter)) {
|
|
||||||
$reply2 = call_user_func(self::$filter, $reply);
|
|
||||||
if (is_bool($reply2)) {
|
|
||||||
return $reply2;
|
|
||||||
}
|
|
||||||
$reply = $reply2;
|
|
||||||
}
|
|
||||||
if ($connection->getOption('type') === CONN_WEBSOCKET) {
|
|
||||||
return $this->processWebsocketAPI($connection, $reply, $function);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->processHttpAPI($connection, $reply, $function);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function processWebsocketAPI($connection, $reply, $function = false)
|
|
||||||
{
|
|
||||||
$api_id = ZMAtomic::get('wait_msg_id')->add();
|
|
||||||
$reply['echo'] = $api_id;
|
|
||||||
if (server()->push($connection->getFd(), json_encode($reply))) {
|
|
||||||
if ($function === true) {
|
|
||||||
$obj = [
|
|
||||||
'data' => $reply,
|
|
||||||
'time' => microtime(true),
|
|
||||||
'self_id' => $connection->getOption('connect_id'),
|
|
||||||
'echo' => $api_id,
|
|
||||||
];
|
|
||||||
return CoMessage::yieldByWS($obj, ['echo'], 30);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
logger()->warning(zm_internal_errcode('E00036') . 'CQAPI send failed, websocket push error.');
|
|
||||||
$response = [
|
|
||||||
'status' => 'failed',
|
|
||||||
'retcode' => -1000,
|
|
||||||
'data' => null,
|
|
||||||
'self_id' => $connection->getOption('connect_id'),
|
|
||||||
];
|
|
||||||
SpinLock::lock('wait_api');
|
|
||||||
$r = LightCacheInside::get('wait_api', 'wait_api');
|
|
||||||
unset($r[$reply['echo']]);
|
|
||||||
LightCacheInside::set('wait_api', 'wait_api', $r);
|
|
||||||
SpinLock::unlock('wait_api');
|
|
||||||
if ($function === true) {
|
|
||||||
return $response;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function processHttpAPI($connection, $reply, $function = null): bool
|
|
||||||
{
|
|
||||||
unset($connection, $reply, $function);
|
|
||||||
// TODO: HTTP方式处理API的代码还没写
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,120 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\API;
|
|
||||||
|
|
||||||
class GoCqhttpAPIV11
|
|
||||||
{
|
|
||||||
use CQAPI;
|
|
||||||
|
|
||||||
public const SUPPORT_VERSION = '1.0.0-beta8';
|
|
||||||
|
|
||||||
private $connection;
|
|
||||||
|
|
||||||
private $callback;
|
|
||||||
|
|
||||||
private $prefix;
|
|
||||||
|
|
||||||
public function __construct($connection, $callback, $prefix)
|
|
||||||
{
|
|
||||||
$this->connection = $connection;
|
|
||||||
$this->callback = $callback;
|
|
||||||
$this->prefix = $prefix;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取频道系统内BOT的资料
|
|
||||||
* 响应字段:nickname, tiny_id, avatar_url
|
|
||||||
* @see https://github.com/Mrs4s/go-cqhttp/blob/master/docs/guild.md#%E8%8E%B7%E5%8F%96%E9%A2%91%E9%81%93%E7%B3%BB%E7%BB%9F%E5%86%85bot%E7%9A%84%E8%B5%84%E6%96%99
|
|
||||||
* @return array|bool 返回API调用结果(数组)或异步API调用状态(bool)
|
|
||||||
*/
|
|
||||||
public function getGuildServiceProfile()
|
|
||||||
{
|
|
||||||
return $this->processAPI($this->connection, [
|
|
||||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
|
||||||
], $this->callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取频道列表
|
|
||||||
* @see https://github.com/Mrs4s/go-cqhttp/blob/master/docs/guild.md#%E8%8E%B7%E5%8F%96%E9%A2%91%E9%81%93%E5%88%97%E8%A1%A8
|
|
||||||
* @return array|bool 返回API调用结果(数组)或异步API调用状态(bool)
|
|
||||||
*/
|
|
||||||
public function getGuildList()
|
|
||||||
{
|
|
||||||
return $this->processAPI($this->connection, [
|
|
||||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
|
||||||
], $this->callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 通过访客获取频道元数据
|
|
||||||
* @see https://github.com/Mrs4s/go-cqhttp/blob/master/docs/guild.md#%E9%80%9A%E8%BF%87%E8%AE%BF%E5%AE%A2%E8%8E%B7%E5%8F%96%E9%A2%91%E9%81%93%E5%85%83%E6%95%B0%E6%8D%AE
|
|
||||||
* @param int|string $guild_id 频道ID
|
|
||||||
* @return array|bool 返回API调用结果(数组)或异步API调用状态(bool)
|
|
||||||
*/
|
|
||||||
public function getGuildMetaByGuest($guild_id)
|
|
||||||
{
|
|
||||||
return $this->processAPI($this->connection, [
|
|
||||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
|
||||||
'params' => [
|
|
||||||
'guild_id' => $guild_id,
|
|
||||||
],
|
|
||||||
], $this->callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取子频道列表
|
|
||||||
* @see https://github.com/Mrs4s/go-cqhttp/blob/master/docs/guild.md#%E8%8E%B7%E5%8F%96%E5%AD%90%E9%A2%91%E9%81%93%E5%88%97%E8%A1%A8
|
|
||||||
* @param int|string $guild_id 频道ID
|
|
||||||
* @param false $no_cache 禁用缓存(默认为false)
|
|
||||||
* @return array|bool 返回API调用结果(数组)或异步API调用状态(bool)
|
|
||||||
*/
|
|
||||||
public function getGuildChannelList($guild_id, bool $no_cache = false)
|
|
||||||
{
|
|
||||||
return $this->processAPI($this->connection, [
|
|
||||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
|
||||||
'params' => [
|
|
||||||
'guild_id' => $guild_id,
|
|
||||||
'no_cache' => $no_cache,
|
|
||||||
],
|
|
||||||
], $this->callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取频道成员列表
|
|
||||||
* @see https://github.com/Mrs4s/go-cqhttp/blob/master/docs/guild.md#%E8%8E%B7%E5%8F%96%E9%A2%91%E9%81%93%E6%88%90%E5%91%98%E5%88%97%E8%A1%A8
|
|
||||||
* @param int|string $guild_id 频道ID
|
|
||||||
* @return array|bool 返回API调用结果(数组)或异步API调用状态(bool)
|
|
||||||
*/
|
|
||||||
public function getGuildMembers($guild_id)
|
|
||||||
{
|
|
||||||
return $this->processAPI($this->connection, [
|
|
||||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
|
||||||
'params' => [
|
|
||||||
'guild_id' => $guild_id,
|
|
||||||
],
|
|
||||||
], $this->callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 发送信息到子频道
|
|
||||||
* @see https://github.com/Mrs4s/go-cqhttp/blob/master/docs/guild.md#%E5%8F%91%E9%80%81%E4%BF%A1%E6%81%AF%E5%88%B0%E5%AD%90%E9%A2%91%E9%81%93
|
|
||||||
* @param int|string $guild_id 频道ID
|
|
||||||
* @param int|string $channel_id 子频道ID
|
|
||||||
* @param string $message 信息内容
|
|
||||||
* @return array|bool 返回API调用结果(数组)或异步API调用状态(bool)
|
|
||||||
*/
|
|
||||||
public function sendGuildChannelMsg($guild_id, $channel_id, string $message)
|
|
||||||
{
|
|
||||||
return $this->processAPI($this->connection, [
|
|
||||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
|
||||||
'params' => [
|
|
||||||
'guild_id' => $guild_id,
|
|
||||||
'channel_id' => $channel_id,
|
|
||||||
'message' => $message,
|
|
||||||
],
|
|
||||||
], $this->callback);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,765 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/** @noinspection PhpUnused */
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\API;
|
|
||||||
|
|
||||||
use Closure;
|
|
||||||
use ZM\ConnectionManager\ConnectionObject;
|
|
||||||
use ZM\ConnectionManager\ManagerGM;
|
|
||||||
use ZM\Exception\RobotNotFoundException;
|
|
||||||
use ZM\Exception\ZMKnownException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* OneBot V11 的 API 实现类
|
|
||||||
* Class OneBotV11
|
|
||||||
* @since 2.5.0
|
|
||||||
*/
|
|
||||||
class OneBotV11
|
|
||||||
{
|
|
||||||
use CQAPI;
|
|
||||||
|
|
||||||
public const API_ASYNC = 1;
|
|
||||||
|
|
||||||
public const API_NORMAL = 0;
|
|
||||||
|
|
||||||
public const API_RATE_LIMITED = 2;
|
|
||||||
|
|
||||||
/** @var null|ConnectionObject */
|
|
||||||
protected $connection;
|
|
||||||
|
|
||||||
protected $callback = true;
|
|
||||||
|
|
||||||
protected $prefix = 0;
|
|
||||||
|
|
||||||
public function __construct(ConnectionObject $connection)
|
|
||||||
{
|
|
||||||
$this->connection = $connection;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取机器人Action/API实例
|
|
||||||
* @param int|string $robot_id 机器人ID
|
|
||||||
* @throws RobotNotFoundException
|
|
||||||
* @return ZMRobot 机器人实例
|
|
||||||
*/
|
|
||||||
public static function get($robot_id): ZMRobot
|
|
||||||
{
|
|
||||||
$r = ManagerGM::getAllByName('qq');
|
|
||||||
foreach ($r as $v) {
|
|
||||||
if ($v->getOption('connect_id') == $robot_id) {
|
|
||||||
return new ZMRobot($v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new RobotNotFoundException('机器人 ' . $robot_id . ' 未连接到框架!');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 随机获取一个连接到框架的机器人实例
|
|
||||||
* @throws RobotNotFoundException
|
|
||||||
* @return ZMRobot 机器人实例
|
|
||||||
*/
|
|
||||||
public static function getRandom(): ZMRobot
|
|
||||||
{
|
|
||||||
$r = ManagerGM::getAllByName('qq');
|
|
||||||
if ($r == []) {
|
|
||||||
throw new RobotNotFoundException('没有任何机器人连接到框架!');
|
|
||||||
}
|
|
||||||
return new ZMRobot($r[array_rand($r)]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取所有机器人实例
|
|
||||||
* @return ZMRobot[] 机器人实例们
|
|
||||||
*/
|
|
||||||
public static function getAllRobot(): array
|
|
||||||
{
|
|
||||||
$r = ManagerGM::getAllByName('qq');
|
|
||||||
$obj = [];
|
|
||||||
foreach ($r as $v) {
|
|
||||||
$obj[] = new ZMRobot($v);
|
|
||||||
}
|
|
||||||
return $obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置回调或启用协程等待API回包
|
|
||||||
* @param bool|Closure $callback 是否开启协程或设置异步回调函数,如果为true,则协程等待结果,如果为false,则异步执行并不等待结果,如果为回调函数,则异步执行且调用回调
|
|
||||||
* @return OneBotV11 返回本身
|
|
||||||
*/
|
|
||||||
public function setCallback($callback = true): OneBotV11
|
|
||||||
{
|
|
||||||
$this->callback = $callback;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置API调用类型后缀
|
|
||||||
* @param int $prefix 设置后缀类型,API_NORMAL为不加后缀,API_ASYNC为异步调用,API_RATE_LIMITED为加后缀并且限制调用频率
|
|
||||||
* @return OneBotV11 返回本身
|
|
||||||
*/
|
|
||||||
public function setPrefix(int $prefix = self::API_NORMAL): OneBotV11
|
|
||||||
{
|
|
||||||
$this->prefix = $prefix;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getSelfId()
|
|
||||||
{
|
|
||||||
return $this->connection->getOption('connect_id');
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 下面是 OneBot 标准的 V11 公开 API */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 发送私聊消息
|
|
||||||
* @see https://github.com/botuniverse/onebot-11/blob/master/api/public.md#send_private_msg-%E5%8F%91%E9%80%81%E7%A7%81%E8%81%8A%E6%B6%88%E6%81%AF
|
|
||||||
* @param int|string $user_id 用户ID
|
|
||||||
* @param string $message 消息内容
|
|
||||||
* @param bool $auto_escape 是否自动转义(默认为false)
|
|
||||||
* @return array|bool 返回API调用结果(数组)或异步API调用状态(bool)
|
|
||||||
*/
|
|
||||||
public function sendPrivateMsg($user_id, string $message, bool $auto_escape = false)
|
|
||||||
{
|
|
||||||
return $this->processAPI($this->connection, [
|
|
||||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
|
||||||
'params' => [
|
|
||||||
'user_id' => $user_id,
|
|
||||||
'message' => $message,
|
|
||||||
'auto_escape' => $auto_escape,
|
|
||||||
],
|
|
||||||
], $this->callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 发送群消息
|
|
||||||
* @see https://github.com/botuniverse/onebot-11/blob/master/api/public.md#send_group_msg-%E5%8F%91%E9%80%81%E7%BE%A4%E6%B6%88%E6%81%AF
|
|
||||||
* @param int|string $group_id 群组ID
|
|
||||||
* @param string $message 消息内容
|
|
||||||
* @param bool $auto_escape 是否自动转义(默认为false)
|
|
||||||
* @return array|bool 返回API调用结果(数组)或异步API调用状态(bool)
|
|
||||||
*/
|
|
||||||
public function sendGroupMsg($group_id, string $message, bool $auto_escape = false)
|
|
||||||
{
|
|
||||||
return $this->processAPI($this->connection, [
|
|
||||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
|
||||||
'params' => [
|
|
||||||
'group_id' => $group_id,
|
|
||||||
'message' => $message,
|
|
||||||
'auto_escape' => $auto_escape,
|
|
||||||
],
|
|
||||||
], $this->callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 发送消息
|
|
||||||
* @see https://github.com/botuniverse/onebot-11/blob/master/api/public.md#send_msg-%E5%8F%91%E9%80%81%E6%B6%88%E6%81%AF
|
|
||||||
* @param string $message_type 消息类型
|
|
||||||
* @param int|string $target_id 目标ID
|
|
||||||
* @param string $message 消息内容
|
|
||||||
* @param bool $auto_escape 是否自动转义(默认为false)
|
|
||||||
* @return array|bool 返回API调用结果(数组)或异步API调用状态(bool)
|
|
||||||
*/
|
|
||||||
public function sendMsg(string $message_type, $target_id, string $message, bool $auto_escape = false)
|
|
||||||
{
|
|
||||||
return $this->processAPI($this->connection, [
|
|
||||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
|
||||||
'params' => [
|
|
||||||
'message_type' => $message_type,
|
|
||||||
($message_type == 'private' ? 'user' : $message_type) . '_id' => $target_id,
|
|
||||||
'message' => $message,
|
|
||||||
'auto_escape' => $auto_escape,
|
|
||||||
],
|
|
||||||
], $this->callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 撤回消息
|
|
||||||
* @see https://github.com/botuniverse/onebot-11/blob/master/api/public.md#delete_msg-%E6%92%A4%E5%9B%9E%E6%B6%88%E6%81%AF
|
|
||||||
* @param int|string $message_id 消息ID
|
|
||||||
* @return array|bool 返回API调用结果(数组)或异步API调用状态(bool)
|
|
||||||
*/
|
|
||||||
public function deleteMsg($message_id)
|
|
||||||
{
|
|
||||||
return $this->processAPI($this->connection, [
|
|
||||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
|
||||||
'params' => [
|
|
||||||
'message_id' => $message_id,
|
|
||||||
],
|
|
||||||
], $this->callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取消息
|
|
||||||
* @see https://github.com/botuniverse/onebot-11/blob/master/api/public.md#get_msg-%E8%8E%B7%E5%8F%96%E6%B6%88%E6%81%AF
|
|
||||||
* @param int|string $message_id 消息ID
|
|
||||||
* @return array|bool 返回API调用结果(数组)或异步API调用状态(bool)
|
|
||||||
*/
|
|
||||||
public function getMsg($message_id)
|
|
||||||
{
|
|
||||||
return $this->processAPI($this->connection, [
|
|
||||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
|
||||||
'params' => [
|
|
||||||
'message_id' => $message_id,
|
|
||||||
],
|
|
||||||
], $this->callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取合并转发消息
|
|
||||||
* @see https://github.com/botuniverse/onebot-11/blob/master/api/public.md#get_forward_msg-%E8%8E%B7%E5%8F%96%E5%90%88%E5%B9%B6%E8%BD%AC%E5%8F%91%E6%B6%88%E6%81%AF
|
|
||||||
* @param int|string $id ID
|
|
||||||
* @return array|bool 返回API调用结果(数组)或异步API调用状态(bool)
|
|
||||||
*/
|
|
||||||
public function getForwardMsg($id)
|
|
||||||
{
|
|
||||||
return $this->processAPI($this->connection, [
|
|
||||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
|
||||||
'params' => [
|
|
||||||
'id' => $id,
|
|
||||||
],
|
|
||||||
], $this->callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 发送好友赞
|
|
||||||
* @see https://github.com/botuniverse/onebot-11/blob/master/api/public.md#send_like-%E5%8F%91%E9%80%81%E5%A5%BD%E5%8F%8B%E8%B5%9E
|
|
||||||
* @param int|string $user_id 用户ID
|
|
||||||
* @param int $times 时间
|
|
||||||
* @return array|bool 返回API调用结果(数组)或异步API调用状态(bool)
|
|
||||||
*/
|
|
||||||
public function sendLike($user_id, int $times = 1)
|
|
||||||
{
|
|
||||||
return $this->processAPI($this->connection, [
|
|
||||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
|
||||||
'params' => [
|
|
||||||
'user_id' => $user_id,
|
|
||||||
'times' => $times,
|
|
||||||
],
|
|
||||||
], $this->callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 群组踢人
|
|
||||||
* @see https://github.com/botuniverse/onebot-11/blob/master/api/public.md#set_group_kick-%E7%BE%A4%E7%BB%84%E8%B8%A2%E4%BA%BA
|
|
||||||
* @param int|string $group_id 群ID
|
|
||||||
* @param int|string $user_id 用户ID
|
|
||||||
* @return array|bool 返回API调用结果(数组)或异步API调用状态(bool)
|
|
||||||
*/
|
|
||||||
public function setGroupKick($group_id, $user_id, bool $reject_add_request = false)
|
|
||||||
{
|
|
||||||
return $this->processAPI($this->connection, [
|
|
||||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
|
||||||
'params' => [
|
|
||||||
'group_id' => $group_id,
|
|
||||||
'user_id' => $user_id,
|
|
||||||
'reject_add_request' => $reject_add_request,
|
|
||||||
],
|
|
||||||
], $this->callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 群组单人禁言
|
|
||||||
* @see https://github.com/botuniverse/onebot-11/blob/master/api/public.md#set_group_ban-%E7%BE%A4%E7%BB%84%E5%8D%95%E4%BA%BA%E7%A6%81%E8%A8%80
|
|
||||||
* @param int|string $group_id 群ID
|
|
||||||
* @param int|string $user_id 用户ID
|
|
||||||
* @param int $duration 禁言时长
|
|
||||||
* @return array|bool 返回API调用结果(数组)或异步API调用状态(bool)
|
|
||||||
*/
|
|
||||||
public function setGroupBan($group_id, $user_id, int $duration = 1800)
|
|
||||||
{
|
|
||||||
return $this->processAPI($this->connection, [
|
|
||||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
|
||||||
'params' => [
|
|
||||||
'group_id' => $group_id,
|
|
||||||
'user_id' => $user_id,
|
|
||||||
'duration' => $duration,
|
|
||||||
],
|
|
||||||
], $this->callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 群组匿名用户禁言
|
|
||||||
* @see https://github.com/botuniverse/onebot-11/blob/master/api/public.md#set_group_anonymous_ban-%E7%BE%A4%E7%BB%84%E5%8C%BF%E5%90%8D%E7%94%A8%E6%88%B7%E7%A6%81%E8%A8%80
|
|
||||||
* @param int|string $group_id 群ID
|
|
||||||
* @param array|int|string $anonymous_or_flag 匿名禁言Flag或匿名用户对象
|
|
||||||
* @return array|bool 返回API调用结果(数组)或异步API调用状态(bool)
|
|
||||||
*/
|
|
||||||
public function setGroupAnonymousBan($group_id, $anonymous_or_flag, int $duration = 1800)
|
|
||||||
{
|
|
||||||
return $this->processAPI($this->connection, [
|
|
||||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
|
||||||
'params' => [
|
|
||||||
'group_id' => $group_id,
|
|
||||||
(is_string($anonymous_or_flag) ? 'flag' : 'anonymous') => $anonymous_or_flag,
|
|
||||||
'duration' => $duration,
|
|
||||||
],
|
|
||||||
], $this->callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 群组全员禁言
|
|
||||||
* @see https://github.com/botuniverse/onebot-11/blob/master/api/public.md#set_group_whole_ban-%E7%BE%A4%E7%BB%84%E5%85%A8%E5%91%98%E7%A6%81%E8%A8%80
|
|
||||||
* @param int|string $group_id 群ID
|
|
||||||
* @return array|bool 返回API调用结果(数组)或异步API调用状态(bool)
|
|
||||||
*/
|
|
||||||
public function setGroupWholeBan($group_id, bool $enable = true)
|
|
||||||
{
|
|
||||||
return $this->processAPI($this->connection, [
|
|
||||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
|
||||||
'params' => [
|
|
||||||
'group_id' => $group_id,
|
|
||||||
'enable' => $enable,
|
|
||||||
],
|
|
||||||
], $this->callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 群组设置管理员
|
|
||||||
* @see https://github.com/botuniverse/onebot-11/blob/master/api/public.md#set_group_admin-%E7%BE%A4%E7%BB%84%E8%AE%BE%E7%BD%AE%E7%AE%A1%E7%90%86%E5%91%98
|
|
||||||
* @param int|string $group_id 群ID
|
|
||||||
* @param int|string $user_id 用户ID
|
|
||||||
* @return array|bool 返回API调用结果(数组)或异步API调用状态(bool)
|
|
||||||
*/
|
|
||||||
public function setGroupAdmin($group_id, $user_id, bool $enable = true)
|
|
||||||
{
|
|
||||||
return $this->processAPI($this->connection, [
|
|
||||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
|
||||||
'params' => [
|
|
||||||
'group_id' => $group_id,
|
|
||||||
'user_id' => $user_id,
|
|
||||||
'enable' => $enable,
|
|
||||||
],
|
|
||||||
], $this->callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 群组匿名
|
|
||||||
* @see https://github.com/botuniverse/onebot-11/blob/master/api/public.md#set_group_anonymous-%E7%BE%A4%E7%BB%84%E5%8C%BF%E5%90%8D
|
|
||||||
* @param int|string $group_id 群ID
|
|
||||||
* @param bool $enable 是否启用(默认为true)
|
|
||||||
* @return array|bool 返回API调用结果(数组)或异步API调用状态(bool)
|
|
||||||
*/
|
|
||||||
public function setGroupAnonymous($group_id, bool $enable = true)
|
|
||||||
{
|
|
||||||
return $this->processAPI($this->connection, [
|
|
||||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
|
||||||
'params' => [
|
|
||||||
'group_id' => $group_id,
|
|
||||||
'enable' => $enable,
|
|
||||||
],
|
|
||||||
], $this->callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置群名片(群备注)
|
|
||||||
* @see https://github.com/botuniverse/onebot-11/blob/master/api/public.md#set_group_card-%E8%AE%BE%E7%BD%AE%E7%BE%A4%E5%90%8D%E7%89%87%E7%BE%A4%E5%A4%87%E6%B3%A8
|
|
||||||
* @param int|string $group_id 群ID
|
|
||||||
* @param int|string $user_id 用户ID
|
|
||||||
* @param string $card 名片内容(默认为空)
|
|
||||||
* @return array|bool 返回API调用结果(数组)或异步API调用状态(bool)
|
|
||||||
*/
|
|
||||||
public function setGroupCard($group_id, $user_id, string $card = '')
|
|
||||||
{
|
|
||||||
return $this->processAPI($this->connection, [
|
|
||||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
|
||||||
'params' => [
|
|
||||||
'group_id' => $group_id,
|
|
||||||
'user_id' => $user_id,
|
|
||||||
'card' => $card,
|
|
||||||
],
|
|
||||||
], $this->callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置群名
|
|
||||||
* @see https://github.com/botuniverse/onebot-11/blob/master/api/public.md#set_group_name-%E8%AE%BE%E7%BD%AE%E7%BE%A4%E5%90%8D
|
|
||||||
* @param int|string $group_id 群ID
|
|
||||||
* @param string $group_name 群名
|
|
||||||
* @return array|bool 返回API调用结果(数组)或异步API调用状态(bool)
|
|
||||||
*/
|
|
||||||
public function setGroupName($group_id, string $group_name)
|
|
||||||
{
|
|
||||||
return $this->processAPI($this->connection, [
|
|
||||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
|
||||||
'params' => [
|
|
||||||
'group_id' => $group_id,
|
|
||||||
'group_name' => $group_name,
|
|
||||||
],
|
|
||||||
], $this->callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 退出群组
|
|
||||||
* @see https://github.com/botuniverse/onebot-11/blob/master/api/public.md#set_group_leave-%E9%80%80%E5%87%BA%E7%BE%A4%E7%BB%84
|
|
||||||
* @param int|string $group_id 群ID
|
|
||||||
* @param bool $is_dismiss 是否解散(默认为false)
|
|
||||||
* @return array|bool 返回API调用结果(数组)或异步API调用状态(bool)
|
|
||||||
*/
|
|
||||||
public function setGroupLeave($group_id, bool $is_dismiss = false)
|
|
||||||
{
|
|
||||||
return $this->processAPI($this->connection, [
|
|
||||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
|
||||||
'params' => [
|
|
||||||
'group_id' => $group_id,
|
|
||||||
'is_dismiss' => $is_dismiss,
|
|
||||||
],
|
|
||||||
], $this->callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置群组专属头衔
|
|
||||||
* @see https://github.com/botuniverse/onebot-11/blob/master/api/public.md#set_group_special_title-%E8%AE%BE%E7%BD%AE%E7%BE%A4%E7%BB%84%E4%B8%93%E5%B1%9E%E5%A4%B4%E8%A1%94
|
|
||||||
* @param int|string $group_id 群ID
|
|
||||||
* @param int|string $user_id 用户ID
|
|
||||||
* @param string $special_title 专属头衔内容
|
|
||||||
* @param int $duration 持续时间(默认为-1,永久)
|
|
||||||
* @return array|bool 返回API调用结果(数组)或异步API调用状态(bool)
|
|
||||||
*/
|
|
||||||
public function setGroupSpecialTitle($group_id, $user_id, string $special_title = '', int $duration = -1)
|
|
||||||
{
|
|
||||||
return $this->processAPI($this->connection, [
|
|
||||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
|
||||||
'params' => [
|
|
||||||
'group_id' => $group_id,
|
|
||||||
'user_id' => $user_id,
|
|
||||||
'special_title' => $special_title,
|
|
||||||
'duration' => $duration,
|
|
||||||
],
|
|
||||||
], $this->callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 处理加好友请求
|
|
||||||
* @see https://github.com/botuniverse/onebot-11/blob/master/api/public.md#set_friend_add_request-%E5%A4%84%E7%90%86%E5%8A%A0%E5%A5%BD%E5%8F%8B%E8%AF%B7%E6%B1%82
|
|
||||||
* @param array|int|string $flag 处理加好友请求的flag
|
|
||||||
* @param bool $approve 是否同意(默认为true)
|
|
||||||
* @param string $remark 设置昵称(默认不设置)
|
|
||||||
* @return array|bool 返回API调用结果(数组)或异步API调用状态(bool)
|
|
||||||
*/
|
|
||||||
public function setFriendAddRequest($flag, bool $approve = true, string $remark = '')
|
|
||||||
{
|
|
||||||
return $this->processAPI($this->connection, [
|
|
||||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
|
||||||
'params' => [
|
|
||||||
'flag' => $flag,
|
|
||||||
'approve' => $approve,
|
|
||||||
'remark' => $remark,
|
|
||||||
],
|
|
||||||
], $this->callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 处理加群请求/邀请
|
|
||||||
* @see https://github.com/botuniverse/onebot-11/blob/master/api/public.md#set_group_add_request-%E5%A4%84%E7%90%86%E5%8A%A0%E7%BE%A4%E8%AF%B7%E6%B1%82%E9%82%80%E8%AF%B7
|
|
||||||
* @param array|int|string $flag 处理加群请求的flag
|
|
||||||
* @param string $sub_type 处理请求类型(包含add和invite)
|
|
||||||
* @param bool $approve 是否同意(默认为true)
|
|
||||||
* @param string $reason 拒绝理由(仅在拒绝时有效,默认为空)
|
|
||||||
* @return array|bool 返回API调用结果(数组)或异步API调用状态(bool)
|
|
||||||
*/
|
|
||||||
public function setGroupAddRequest($flag, string $sub_type, bool $approve = true, string $reason = '')
|
|
||||||
{
|
|
||||||
return $this->processAPI($this->connection, [
|
|
||||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
|
||||||
'params' => [
|
|
||||||
'flag' => $flag,
|
|
||||||
'sub_type' => $sub_type,
|
|
||||||
'approve' => $approve,
|
|
||||||
'reason' => $reason,
|
|
||||||
],
|
|
||||||
], $this->callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取登录号信息
|
|
||||||
* @see https://github.com/botuniverse/onebot-11/blob/master/api/public.md#get_login_info-%E8%8E%B7%E5%8F%96%E7%99%BB%E5%BD%95%E5%8F%B7%E4%BF%A1%E6%81%AF
|
|
||||||
* @return array|bool 返回API调用结果(数组)或异步API调用状态(bool)
|
|
||||||
*/
|
|
||||||
public function getLoginInfo()
|
|
||||||
{
|
|
||||||
return $this->processAPI($this->connection, ['action' => $this->getActionName($this->prefix, __FUNCTION__)], $this->callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取陌生人信息
|
|
||||||
* @see https://github.com/botuniverse/onebot-11/blob/master/api/public.md#get_stranger_info-%E8%8E%B7%E5%8F%96%E9%99%8C%E7%94%9F%E4%BA%BA%E4%BF%A1%E6%81%AF
|
|
||||||
* @param int|string $user_id 用户ID
|
|
||||||
* @param bool $no_cache 是否不使用缓存(默认为false)
|
|
||||||
* @return array|bool 返回API调用结果(数组)或异步API调用状态(bool)
|
|
||||||
*/
|
|
||||||
public function getStrangerInfo($user_id, bool $no_cache = false)
|
|
||||||
{
|
|
||||||
return $this->processAPI($this->connection, [
|
|
||||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
|
||||||
'params' => [
|
|
||||||
'user_id' => $user_id,
|
|
||||||
'no_cache' => $no_cache,
|
|
||||||
],
|
|
||||||
], $this->callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取好友列表
|
|
||||||
* @see https://github.com/botuniverse/onebot-11/blob/master/api/public.md#get_friend_list-%E8%8E%B7%E5%8F%96%E5%A5%BD%E5%8F%8B%E5%88%97%E8%A1%A8
|
|
||||||
* @return array|bool 返回API调用结果(数组)或异步API调用状态(bool)
|
|
||||||
*/
|
|
||||||
public function getFriendList()
|
|
||||||
{
|
|
||||||
return $this->processAPI($this->connection, [
|
|
||||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
|
||||||
], $this->callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取群信息
|
|
||||||
* @see https://github.com/botuniverse/onebot-11/blob/master/api/public.md#get_group_info-%E8%8E%B7%E5%8F%96%E7%BE%A4%E4%BF%A1%E6%81%AF
|
|
||||||
* @param int|string $group_id 群ID
|
|
||||||
* @return array|bool 返回API调用结果(数组)或异步API调用状态(bool)
|
|
||||||
*/
|
|
||||||
public function getGroupInfo($group_id, bool $no_cache = false)
|
|
||||||
{
|
|
||||||
return $this->processAPI($this->connection, [
|
|
||||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
|
||||||
'params' => [
|
|
||||||
'group_id' => $group_id,
|
|
||||||
'no_cache' => $no_cache,
|
|
||||||
],
|
|
||||||
], $this->callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取群列表
|
|
||||||
* @see https://github.com/botuniverse/onebot-11/blob/master/api/public.md#get_group_list-%E8%8E%B7%E5%8F%96%E7%BE%A4%E5%88%97%E8%A1%A8
|
|
||||||
* @return array|bool 返回API调用结果(数组)或异步API调用状态(bool)
|
|
||||||
*/
|
|
||||||
public function getGroupList()
|
|
||||||
{
|
|
||||||
return $this->processAPI($this->connection, [
|
|
||||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
|
||||||
], $this->callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取群成员信息
|
|
||||||
* @see https://github.com/botuniverse/onebot-11/blob/master/api/public.md#get_group_member_info-%E8%8E%B7%E5%8F%96%E7%BE%A4%E6%88%90%E5%91%98%E4%BF%A1%E6%81%AF
|
|
||||||
* @param int|string $group_id 群ID
|
|
||||||
* @param int|string $user_id 用户ID
|
|
||||||
* @return array|bool 返回API调用结果(数组)或异步API调用状态(bool)
|
|
||||||
*/
|
|
||||||
public function getGroupMemberInfo($group_id, $user_id, bool $no_cache = false)
|
|
||||||
{
|
|
||||||
return $this->processAPI($this->connection, [
|
|
||||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
|
||||||
'params' => [
|
|
||||||
'group_id' => $group_id,
|
|
||||||
'user_id' => $user_id,
|
|
||||||
'no_cache' => $no_cache,
|
|
||||||
],
|
|
||||||
], $this->callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取群成员列表
|
|
||||||
* @see https://github.com/botuniverse/onebot-11/blob/master/api/public.md#get_group_member_list-%E8%8E%B7%E5%8F%96%E7%BE%A4%E6%88%90%E5%91%98%E5%88%97%E8%A1%A8
|
|
||||||
* @param int|string $group_id 群ID
|
|
||||||
* @return array|bool 返回API调用结果(数组)或异步API调用状态(bool)
|
|
||||||
*/
|
|
||||||
public function getGroupMemberList($group_id)
|
|
||||||
{
|
|
||||||
return $this->processAPI($this->connection, [
|
|
||||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
|
||||||
'params' => [
|
|
||||||
'group_id' => $group_id,
|
|
||||||
],
|
|
||||||
], $this->callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取群荣誉信息
|
|
||||||
* @see https://github.com/botuniverse/onebot-11/blob/master/api/public.md#get_group_honor_info-%E8%8E%B7%E5%8F%96%E7%BE%A4%E8%8D%A3%E8%AA%89%E4%BF%A1%E6%81%AF
|
|
||||||
* @param int|string $group_id 群ID
|
|
||||||
* @param string $type 荣誉类型
|
|
||||||
* @return array|bool 返回API调用结果(数组)或异步API调用状态(bool)
|
|
||||||
*/
|
|
||||||
public function getGroupHonorInfo($group_id, string $type)
|
|
||||||
{
|
|
||||||
return $this->processAPI($this->connection, [
|
|
||||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
|
||||||
'params' => [
|
|
||||||
'group_id' => $group_id,
|
|
||||||
'type' => $type,
|
|
||||||
],
|
|
||||||
], $this->callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取 CSRF Token
|
|
||||||
* @see https://github.com/botuniverse/onebot-11/blob/master/api/public.md#get_csrf_token-%E8%8E%B7%E5%8F%96-csrf-token
|
|
||||||
* @return array|bool 返回API调用结果(数组)或异步API调用状态(bool)
|
|
||||||
*/
|
|
||||||
public function getCsrfToken()
|
|
||||||
{
|
|
||||||
return $this->processAPI($this->connection, [
|
|
||||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
|
||||||
], $this->callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取 QQ 相关接口凭证
|
|
||||||
* @see https://github.com/botuniverse/onebot-11/blob/master/api/public.md#get_credentials-%E8%8E%B7%E5%8F%96-qq-%E7%9B%B8%E5%85%B3%E6%8E%A5%E5%8F%A3%E5%87%AD%E8%AF%81
|
|
||||||
* @return array|bool 返回API调用结果(数组)或异步API调用状态(bool)
|
|
||||||
*/
|
|
||||||
public function getCredentials(string $domain = '')
|
|
||||||
{
|
|
||||||
return $this->processAPI($this->connection, [
|
|
||||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
|
||||||
'params' => [
|
|
||||||
'domain' => $domain,
|
|
||||||
],
|
|
||||||
], $this->callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取语音
|
|
||||||
* @see https://github.com/botuniverse/onebot-11/blob/master/api/public.md#get_record-%E8%8E%B7%E5%8F%96%E8%AF%AD%E9%9F%B3
|
|
||||||
* @param string $file 文件
|
|
||||||
* @param string $out_format 输出格式
|
|
||||||
* @return array|bool 返回API调用结果(数组)或异步API调用状态(bool)
|
|
||||||
*/
|
|
||||||
public function getRecord(string $file, string $out_format)
|
|
||||||
{
|
|
||||||
return $this->processAPI($this->connection, [
|
|
||||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
|
||||||
'params' => [
|
|
||||||
'file' => $file,
|
|
||||||
'out_format' => $out_format,
|
|
||||||
],
|
|
||||||
], $this->callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取图片
|
|
||||||
* @see https://github.com/botuniverse/onebot-11/blob/master/api/public.md#get_image-%E8%8E%B7%E5%8F%96%E5%9B%BE%E7%89%87
|
|
||||||
* @param string $file 文件
|
|
||||||
* @return array|bool 返回API调用结果(数组)或异步API调用状态(bool)
|
|
||||||
*/
|
|
||||||
public function getImage(string $file)
|
|
||||||
{
|
|
||||||
return $this->processAPI($this->connection, [
|
|
||||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
|
||||||
'params' => [
|
|
||||||
'file' => $file,
|
|
||||||
],
|
|
||||||
], $this->callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 检查是否可以发送图片
|
|
||||||
* @see https://github.com/botuniverse/onebot-11/blob/master/api/public.md#can_send_image-%E6%A3%80%E6%9F%A5%E6%98%AF%E5%90%A6%E5%8F%AF%E4%BB%A5%E5%8F%91%E9%80%81%E5%9B%BE%E7%89%87
|
|
||||||
* @return array|bool 返回API调用结果(数组)或异步API调用状态(bool)
|
|
||||||
*/
|
|
||||||
public function canSendImage()
|
|
||||||
{
|
|
||||||
return $this->processAPI($this->connection, [
|
|
||||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
|
||||||
], $this->callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 检查是否可以发送语音
|
|
||||||
* @see https://github.com/botuniverse/onebot-11/blob/master/api/public.md#can_send_record-%E6%A3%80%E6%9F%A5%E6%98%AF%E5%90%A6%E5%8F%AF%E4%BB%A5%E5%8F%91%E9%80%81%E8%AF%AD%E9%9F%B3
|
|
||||||
* @return array|bool 返回API调用结果(数组)或异步API调用状态(bool)
|
|
||||||
*/
|
|
||||||
public function canSendRecord()
|
|
||||||
{
|
|
||||||
return $this->processAPI($this->connection, [
|
|
||||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
|
||||||
], $this->callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取运行状态
|
|
||||||
* @see https://github.com/botuniverse/onebot-11/blob/master/api/public.md#get_status-%E8%8E%B7%E5%8F%96%E8%BF%90%E8%A1%8C%E7%8A%B6%E6%80%81
|
|
||||||
* @return array|bool 返回API调用结果(数组)或异步API调用状态(bool)
|
|
||||||
*/
|
|
||||||
public function getStatus()
|
|
||||||
{
|
|
||||||
return $this->processAPI($this->connection, [
|
|
||||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
|
||||||
], $this->callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取版本信息
|
|
||||||
* @see https://github.com/botuniverse/onebot-11/blob/master/api/public.md#get_version_info-%E8%8E%B7%E5%8F%96%E7%89%88%E6%9C%AC%E4%BF%A1%E6%81%AF
|
|
||||||
* @return array|bool 返回API调用结果(数组)或异步API调用状态(bool)
|
|
||||||
*/
|
|
||||||
public function getVersionInfo()
|
|
||||||
{
|
|
||||||
return $this->processAPI($this->connection, [
|
|
||||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
|
||||||
], $this->callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 重启 OneBot 实现
|
|
||||||
* @see https://github.com/botuniverse/onebot-11/blob/master/api/public.md#set_restart-%E9%87%8D%E5%90%AF-onebot-%E5%AE%9E%E7%8E%B0
|
|
||||||
* @param int $delay 延迟时间(毫秒,默认为0)
|
|
||||||
* @return array|bool 返回API调用结果(数组)或异步API调用状态(bool)
|
|
||||||
*/
|
|
||||||
public function setRestart(int $delay = 0)
|
|
||||||
{
|
|
||||||
return $this->processAPI($this->connection, [
|
|
||||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
|
||||||
'params' => [
|
|
||||||
'delay' => $delay,
|
|
||||||
],
|
|
||||||
], $this->callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 清理缓存
|
|
||||||
* @see https://github.com/botuniverse/onebot-11/blob/master/api/public.md#clean_cache-%E6%B8%85%E7%90%86%E7%BC%93%E5%AD%98
|
|
||||||
* @return array|bool 返回API调用结果(数组)或异步API调用状态(bool)
|
|
||||||
*/
|
|
||||||
public function cleanCache()
|
|
||||||
{
|
|
||||||
return $this->processAPI($this->connection, [
|
|
||||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
|
||||||
], $this->callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取内置支持的扩展API对象
|
|
||||||
* 现支持 go-cqhttp 的扩展API
|
|
||||||
* @params string $package_name 包名
|
|
||||||
* @throws ZMKnownException
|
|
||||||
* @return mixed 返回包的操作对象
|
|
||||||
*/
|
|
||||||
public function getExtendedAPI(string $package_name = 'go-cqhttp')
|
|
||||||
{
|
|
||||||
$table = [
|
|
||||||
'go-cqhttp' => GoCqhttpAPIV11::class,
|
|
||||||
];
|
|
||||||
if (isset($table[$package_name])) {
|
|
||||||
return new $table[$package_name]($this->connection, $this->callback, $this->prefix);
|
|
||||||
}
|
|
||||||
throw new ZMKnownException(zm_internal_errcode('E00071'), '无法找到对应的调用扩展类');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $action 动作(API)名称
|
|
||||||
* @param array $params 参数
|
|
||||||
* @return array|bool 返回API调用结果(数组)或异步API调用状态(bool)
|
|
||||||
*/
|
|
||||||
public function callExtendedAPI(string $action, array $params = [])
|
|
||||||
{
|
|
||||||
return $this->processAPI($this->connection, [
|
|
||||||
'action' => $action,
|
|
||||||
'params' => $params,
|
|
||||||
], $this->callback);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,74 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\API\Proxies\Bot;
|
|
||||||
|
|
||||||
use ZM\API\ZMRobot;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @mixin ZMRobot
|
|
||||||
*/
|
|
||||||
abstract class AbstractBotProxy
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* 传入的机器人实例
|
|
||||||
*
|
|
||||||
* @var ZMRobot
|
|
||||||
*/
|
|
||||||
protected $bot;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 构造函数
|
|
||||||
*
|
|
||||||
* @param AbstractBotProxy|ZMRobot $bot 调用此代理的机器人实例
|
|
||||||
*/
|
|
||||||
public function __construct($bot)
|
|
||||||
{
|
|
||||||
$this->bot = $bot;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 在传入的机器人实例上调用方法
|
|
||||||
*
|
|
||||||
* @param string $name 方法名
|
|
||||||
* @param array $arguments 参数
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function __call(string $name, array $arguments)
|
|
||||||
{
|
|
||||||
return $this->bot->{$name}(...$arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取传入的机器人实例的属性
|
|
||||||
*
|
|
||||||
* @param string $name 属性名
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function __get(string $name)
|
|
||||||
{
|
|
||||||
return $this->bot->{$name};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置传入的机器人实例的属性
|
|
||||||
*
|
|
||||||
* @param string $name 属性名
|
|
||||||
* @param mixed $value 属性值
|
|
||||||
*/
|
|
||||||
public function __set(string $name, $value)
|
|
||||||
{
|
|
||||||
$this->bot->{$name} = $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 判断传入的机器人实例的属性是否存在
|
|
||||||
*
|
|
||||||
* @param string $name 属性名
|
|
||||||
*/
|
|
||||||
public function __isset(string $name): bool
|
|
||||||
{
|
|
||||||
return isset($this->bot->{$name});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,42 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\API\Proxies\Bot;
|
|
||||||
|
|
||||||
use ReflectionException;
|
|
||||||
use ReflectionMethod;
|
|
||||||
use ReflectionNamedType;
|
|
||||||
use ZM\API\ZMRobot;
|
|
||||||
|
|
||||||
class AllBotsProxy extends AbstractBotProxy
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* 在所有机器人实例上调用方法
|
|
||||||
*
|
|
||||||
* @param string $name 方法名
|
|
||||||
* @param array $arguments 参数
|
|
||||||
* @throws ReflectionException
|
|
||||||
* @return array<mixed> 返回一个包含所有执行结果的数组,键名为机器人ID
|
|
||||||
*/
|
|
||||||
public function __call(string $name, array $arguments)
|
|
||||||
{
|
|
||||||
// 如果调用的方法为代理方法,则传入当前代理作为参数
|
|
||||||
// 一般此情况代表用户进行嵌套代理,即 `->all()->allGroups()` 等情况
|
|
||||||
$reflection = new ReflectionMethod(ZMRobot::class, $name);
|
|
||||||
if (($return = $reflection->getReturnType()) && $return instanceof ReflectionNamedType && str_contains($return->getName(), 'Proxy')) {
|
|
||||||
logger()->debug("Trying to construct proxy {$name} inside proxy, returning nested proxy.");
|
|
||||||
// 插入当前代理作为第一个参数
|
|
||||||
array_unshift($arguments, $this);
|
|
||||||
return $this->bot->{$name}(...$arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
$result = [];
|
|
||||||
// 遍历所有机器人实例
|
|
||||||
foreach ($this->bot::getAllRobot() as $bot) {
|
|
||||||
logger()->debug("Calling {$name} on bot {$bot->getSelfId()}.");
|
|
||||||
$result[$bot->getSelfId()] = $bot->{$name}(...$arguments);
|
|
||||||
}
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,42 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\API\Proxies\Bot;
|
|
||||||
|
|
||||||
use ReflectionException;
|
|
||||||
use ZM\API\ZMRobot;
|
|
||||||
|
|
||||||
class AllGroupsProxy extends AbstractBotProxy
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* 在传入的机器人实例上调用方法
|
|
||||||
*
|
|
||||||
* @param string $name 方法名
|
|
||||||
* @param array $arguments 参数
|
|
||||||
* @throws ReflectionException
|
|
||||||
* @return array<mixed> 返回一个包含所有执行结果的数组,键名为群号
|
|
||||||
*/
|
|
||||||
public function __call(string $name, array $arguments)
|
|
||||||
{
|
|
||||||
// 如果调用的方法并非群组方法,则直接返回输出
|
|
||||||
// 因为目前所有群组方法都是以 `group_id` 作为第一个参数,故以此来判断
|
|
||||||
$reflection = new \ReflectionMethod(ZMRobot::class, $name);
|
|
||||||
if (!$reflection->getNumberOfParameters() || $reflection->getParameters()[0]->getName() !== 'group_id') {
|
|
||||||
logger()->warning("Trying to call non-group method {$name} on AllGroupsProxy, skipped.");
|
|
||||||
return $this->bot->{$name}(...$arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
$result = [];
|
|
||||||
// 获取并遍历所有群组
|
|
||||||
$groups = $this->bot->getGroupList()['data'];
|
|
||||||
foreach ($groups as $group) {
|
|
||||||
$arguments[0] = $group['group_id'];
|
|
||||||
$bot_id = implode_when_necessary($this->bot->getSelfId());
|
|
||||||
logger()->debug("Calling {$name} on group {$group['group_id']} on bot {$bot_id}.");
|
|
||||||
// 在群组上调用方法
|
|
||||||
$result[$group['group_id']] = $this->bot->{$name}(...$arguments);
|
|
||||||
}
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,132 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\API;
|
|
||||||
|
|
||||||
use Swoole\Coroutine\Http\Client;
|
|
||||||
use ZM\Console\Console;
|
|
||||||
|
|
||||||
class TuringAPI
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* 请求图灵API,返回图灵的消息
|
|
||||||
* @param string $msg 消息
|
|
||||||
* @param int|string $user_id 用户ID
|
|
||||||
* @param string $api API Key
|
|
||||||
* @return string 图灵的回复
|
|
||||||
*/
|
|
||||||
public static function getTuringMsg(string $msg, $user_id, string $api): string
|
|
||||||
{
|
|
||||||
$origin = $msg;
|
|
||||||
if (($cq = CQ::getCQ($msg)) !== null) {// 如有CQ码则去除
|
|
||||||
if ($cq['type'] == 'image') {
|
|
||||||
$url = $cq['params']['url'];
|
|
||||||
$msg = str_replace(mb_substr($msg, $cq['start'], $cq['end'] - $cq['start'] + 1), '', $msg);
|
|
||||||
}
|
|
||||||
$msg = trim($msg);
|
|
||||||
}
|
|
||||||
// 构建将要发送的json包给图灵
|
|
||||||
$content = [
|
|
||||||
'reqType' => 0,
|
|
||||||
'userInfo' => [
|
|
||||||
'apiKey' => $api,
|
|
||||||
'userId' => $user_id,
|
|
||||||
],
|
|
||||||
];
|
|
||||||
if ($msg != '') {
|
|
||||||
$content['perception']['inputText']['text'] = $msg;
|
|
||||||
}
|
|
||||||
$msg = trim($msg);
|
|
||||||
if (mb_strlen($msg) < 1 && !isset($url)) {
|
|
||||||
return '请说出你想说的话';
|
|
||||||
}
|
|
||||||
if (isset($url)) {
|
|
||||||
$content['perception']['inputImage']['url'] = $url;
|
|
||||||
$content['reqType'] = 1;
|
|
||||||
}
|
|
||||||
if (!isset($content['perception'])) {
|
|
||||||
return '请说出你想说的话';
|
|
||||||
}
|
|
||||||
$client = new Client('openapi.tuling123.com', 80);
|
|
||||||
$client->setHeaders(['Content-type' => 'application/json']);
|
|
||||||
$client->post('/openapi/api/v2', json_encode($content, JSON_UNESCAPED_UNICODE));
|
|
||||||
$api_return = json_decode($client->body, true);
|
|
||||||
if (!isset($api_return['intent']['code'])) {
|
|
||||||
return 'XD 哎呀,我脑子突然短路了,请稍后再问我吧!';
|
|
||||||
}
|
|
||||||
$status = self::getResultStatus($api_return);
|
|
||||||
if ($status !== true) {
|
|
||||||
if ($status == 'err:输入文本内容超长(上限150)') {
|
|
||||||
return '你的话太多了!!!';
|
|
||||||
}
|
|
||||||
if ($api_return['intent']['code'] == 4003) {
|
|
||||||
return '哎呀,我刚才有点走神了,可能忘记你说什么了,可以重说一遍吗';
|
|
||||||
}
|
|
||||||
Console::error(zm_internal_errcode('E00038') . "图灵机器人发送错误!\n错误原始内容:" . $origin . "\n来自:" . $user_id . "\n错误信息:" . $status);
|
|
||||||
// echo json_encode($r, 128|256);
|
|
||||||
return '哎呀,我刚才有点走神了,要不一会儿换一种问题试试?';
|
|
||||||
}
|
|
||||||
$result = $api_return['results'];
|
|
||||||
// logger()->info(Console::setColor(json_encode($result, 128 | 256), "green"));
|
|
||||||
$final = '';
|
|
||||||
foreach ($result as $v) {
|
|
||||||
switch ($v['resultType']) {
|
|
||||||
case 'url':
|
|
||||||
$final .= "\n" . $v['values']['url'];
|
|
||||||
break;
|
|
||||||
case 'text':
|
|
||||||
$final .= "\n" . $v['values']['text'];
|
|
||||||
break;
|
|
||||||
case 'image':
|
|
||||||
$final .= "\n" . CQ::image($v['values']['image']);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return trim($final);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array $r 数据API回包
|
|
||||||
* @return bool|string 错误消息或成功鸥鸟
|
|
||||||
*/
|
|
||||||
public static function getResultStatus(array $r)
|
|
||||||
{
|
|
||||||
switch ($r['intent']['code']) {
|
|
||||||
case 5000:
|
|
||||||
return 'err:无解析结果';
|
|
||||||
case 4000:
|
|
||||||
case 6000:
|
|
||||||
return 'err:暂不支持该功能';
|
|
||||||
case 4001:
|
|
||||||
return 'err:加密方式错误';
|
|
||||||
case 4005:
|
|
||||||
case 4002:
|
|
||||||
return 'err:无功能权限';
|
|
||||||
case 4003:
|
|
||||||
return 'err:该apikey没有可用请求次数';
|
|
||||||
case 4007:
|
|
||||||
return 'err:apikey不合法';
|
|
||||||
case 4100:
|
|
||||||
return 'err:userid获取失败';
|
|
||||||
case 4200:
|
|
||||||
return 'err:上传格式错误';
|
|
||||||
case 4300:
|
|
||||||
return 'err:批量操作超过限制';
|
|
||||||
case 4400:
|
|
||||||
return 'err:没有上传合法userid';
|
|
||||||
case 4500:
|
|
||||||
return 'err:userid申请个数超过限制';
|
|
||||||
case 4600:
|
|
||||||
return 'err:输入内容为空';
|
|
||||||
case 4602:
|
|
||||||
return 'err:输入文本内容超长(上限150)';
|
|
||||||
case 7002:
|
|
||||||
return 'err:上传信息失败';
|
|
||||||
case 8008:
|
|
||||||
return 'err:服务器错误';
|
|
||||||
default:
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,37 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\API;
|
|
||||||
|
|
||||||
use ZM\API\Proxies\Bot as Proxies;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class ZMRobot
|
|
||||||
* @since 1.2
|
|
||||||
* @version V11
|
|
||||||
*/
|
|
||||||
class ZMRobot extends OneBotV11
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* 获取一个会在所有机器人实例上执行的代理
|
|
||||||
*/
|
|
||||||
public function all(Proxies\AbstractBotProxy $proxy = null): Proxies\AllBotsProxy
|
|
||||||
{
|
|
||||||
$bot = $proxy ?: $this;
|
|
||||||
$bot_id = implode_when_necessary($bot->getSelfId());
|
|
||||||
logger()->debug("Constructing AllBotsProxy for ZMRobot({$bot_id})");
|
|
||||||
return new Proxies\AllBotsProxy($bot);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取一个会在所有群上执行的代理
|
|
||||||
*/
|
|
||||||
public function allGroups(Proxies\AbstractBotProxy $proxy = null): Proxies\AllGroupsProxy
|
|
||||||
{
|
|
||||||
$bot = $proxy ?: $this;
|
|
||||||
$bot_id = implode_when_necessary($bot->getSelfId());
|
|
||||||
logger()->debug("Constructing AllGroupsProxy for ZMRobot({$bot_id})");
|
|
||||||
return new Proxies\AllGroupsProxy($bot);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,27 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\Adapters;
|
|
||||||
|
|
||||||
use ZM\Context\ContextInterface;
|
|
||||||
|
|
||||||
interface AdapterInterface
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* 获取适配器名称
|
|
||||||
*/
|
|
||||||
public function getName(): string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取适配器版本
|
|
||||||
*/
|
|
||||||
public function getVersion(): string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 处理传入请求
|
|
||||||
*
|
|
||||||
* @param ContextInterface $context 上下文
|
|
||||||
*/
|
|
||||||
public function handleIncomingRequest(ContextInterface $context): void;
|
|
||||||
}
|
|
||||||
@ -1,334 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\Adapters;
|
|
||||||
|
|
||||||
use ZM\Annotation\CQ\CQAfter;
|
|
||||||
use ZM\Annotation\CQ\CQAPIResponse;
|
|
||||||
use ZM\Annotation\CQ\CQBefore;
|
|
||||||
use ZM\Annotation\CQ\CQCommand;
|
|
||||||
use ZM\Annotation\CQ\CQMessage;
|
|
||||||
use ZM\Annotation\CQ\CQMetaEvent;
|
|
||||||
use ZM\Annotation\CQ\CQNotice;
|
|
||||||
use ZM\Annotation\CQ\CQRequest;
|
|
||||||
use ZM\Config\ZMConfig;
|
|
||||||
use ZM\Context\ContextInterface;
|
|
||||||
use ZM\Event\EventDispatcher;
|
|
||||||
use ZM\Exception\WaitTimeoutException;
|
|
||||||
use ZM\Utils\CoMessage;
|
|
||||||
use ZM\Utils\MessageUtil;
|
|
||||||
|
|
||||||
class OneBot11Adapter implements AdapterInterface
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public function getName(): string
|
|
||||||
{
|
|
||||||
return 'onebot';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public function getVersion(): string
|
|
||||||
{
|
|
||||||
return '11';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public function handleIncomingRequest(ContextInterface $context): void
|
|
||||||
{
|
|
||||||
$data = json_decode($context->getFrame()->data, true);
|
|
||||||
|
|
||||||
// 将数据存入协程参数中
|
|
||||||
set_coroutine_params(compact('data'));
|
|
||||||
|
|
||||||
try {
|
|
||||||
logger()->debug('start handle incoming request');
|
|
||||||
|
|
||||||
// 非元事件调用 pre-before 事件
|
|
||||||
if (!$this->isMetaEvent($data)) {
|
|
||||||
logger()->debug('pre-before event');
|
|
||||||
$pre_before_result = $this->handleBeforeEvent($data, 'pre');
|
|
||||||
if ($pre_before_result->store === 'block') {
|
|
||||||
EventDispatcher::interrupt();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 回调或事件处理 resume
|
|
||||||
if (CoMessage::resumeByWS()) {
|
|
||||||
EventDispatcher::interrupt();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 非元事件调用 after-before 事件
|
|
||||||
if (!$this->isMetaEvent($data)) {
|
|
||||||
logger()->debug('post-before event');
|
|
||||||
$post_before_result = $this->handleBeforeEvent($data, 'post');
|
|
||||||
if ($post_before_result->store === 'block') {
|
|
||||||
EventDispatcher::interrupt();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 进入回调、事件分发流程
|
|
||||||
if ($this->isEvent($data)) {
|
|
||||||
// 事件分发
|
|
||||||
switch ($data['post_type']) {
|
|
||||||
case 'message':
|
|
||||||
logger()->debug('message event {data}', compact('data'));
|
|
||||||
$this->handleMessageEvent($data, $context);
|
|
||||||
break;
|
|
||||||
case 'meta_event':
|
|
||||||
logger()->debug('meta event {data}', compact('data'));
|
|
||||||
$this->handleMetaEvent($data, $context);
|
|
||||||
break;
|
|
||||||
case 'notice':
|
|
||||||
logger()->debug('notice event {data}', compact('data'));
|
|
||||||
$this->handleNoticeEvent($data, $context);
|
|
||||||
break;
|
|
||||||
case 'request':
|
|
||||||
logger()->debug('request event {data}', compact('data'));
|
|
||||||
$this->handleRequestEvent($data, $context);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} elseif ($this->isAPIResponse($data)) {
|
|
||||||
logger()->debug('api response {data}', compact('data'));
|
|
||||||
$this->handleAPIResponse($data, $context);
|
|
||||||
}
|
|
||||||
logger()->debug('event end {data}', compact('data'));
|
|
||||||
// 回调、事件处理完成
|
|
||||||
} catch (WaitTimeoutException $e) {
|
|
||||||
$e->module->finalReply($e->getMessage());
|
|
||||||
} finally {
|
|
||||||
// 非元事件调用 after 事件
|
|
||||||
if (!$this->isMetaEvent($data)) {
|
|
||||||
logger()->debug('after event');
|
|
||||||
$after_result = $this->handleAfterEvent($data);
|
|
||||||
if ($after_result->store === 'block') {
|
|
||||||
EventDispatcher::interrupt();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 处理 API 响应
|
|
||||||
*
|
|
||||||
* @param array $data 数据
|
|
||||||
* @param ContextInterface $context 上下文
|
|
||||||
*/
|
|
||||||
private function handleAPIResponse(array $data, ContextInterface $context): void
|
|
||||||
{
|
|
||||||
set_coroutine_params(['cq_response' => $data]);
|
|
||||||
$dispatcher = new EventDispatcher(CQAPIResponse::class);
|
|
||||||
$dispatcher->setRuleFunction(function (CQAPIResponse $event) use ($context) {
|
|
||||||
return $event->retcode === $context->getCQResponse()['retcode'];
|
|
||||||
});
|
|
||||||
$dispatcher->dispatchEvents($data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 处理消息事件
|
|
||||||
*
|
|
||||||
* @param array $data 消息数据
|
|
||||||
* @param ContextInterface $context 上下文
|
|
||||||
*/
|
|
||||||
private function handleMessageEvent(array $data, ContextInterface $context): void
|
|
||||||
{
|
|
||||||
// 分发 CQCommand 事件
|
|
||||||
$dispatcher = new EventDispatcher(CQCommand::class);
|
|
||||||
// 设定返回值处理函数
|
|
||||||
$dispatcher->setReturnFunction(function ($result) use ($context) {
|
|
||||||
if (is_string($result)) {
|
|
||||||
$context->reply($result);
|
|
||||||
}
|
|
||||||
if ($context->getCache('has_reply') === true) {
|
|
||||||
EventDispatcher::interrupt();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$message = $data['message'];
|
|
||||||
|
|
||||||
// 将消息段数组转换为消息字符串
|
|
||||||
if (is_array($message)) {
|
|
||||||
$message = MessageUtil::arrayToStr($message);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 匹配命令
|
|
||||||
$match_result = MessageUtil::matchCommand($message, $context->getData());
|
|
||||||
|
|
||||||
if ($match_result->status) {
|
|
||||||
$matches = $match_result->match;
|
|
||||||
|
|
||||||
$input_arguments = MessageUtil::checkArguments($match_result->object->class, $match_result->object->method, $matches);
|
|
||||||
if (!empty($matches)) {
|
|
||||||
$context->setCache('match', $matches);
|
|
||||||
}
|
|
||||||
|
|
||||||
$dispatcher->dispatchEvent($match_result->object, null, ...$input_arguments);
|
|
||||||
|
|
||||||
// 处理命令返回结果
|
|
||||||
if (is_string($dispatcher->store)) {
|
|
||||||
$context->reply($dispatcher->store);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($context->getCache('has_reply') === true) {
|
|
||||||
$policy = ZMConfig::get('global', 'onebot')['message_command_policy'] ?? 'interrupt';
|
|
||||||
switch ($policy) {
|
|
||||||
case 'interrupt':
|
|
||||||
EventDispatcher::interrupt();
|
|
||||||
// no break
|
|
||||||
case 'continue':
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new \Exception('未知的消息命令策略:' . $policy);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 分发 CQMessage 事件
|
|
||||||
$dispatcher = new EventDispatcher(CQMessage::class);
|
|
||||||
// 设定匹配规则函数
|
|
||||||
$dispatcher->setRuleFunction(function (CQMessage $event) use ($context) {
|
|
||||||
return ($event->message === '' || ($event->message === $context->getMessage()))
|
|
||||||
&& ($event->user_id === 0 || ($event->user_id === $context->getUserId()))
|
|
||||||
&& ($event->group_id === 0 || ($event->group_id === ($context->getGroupId() ?? 0)))
|
|
||||||
&& ($event->message_type === '' || ($event->message_type === $context->getMessageType()))
|
|
||||||
&& ($event->raw_message === '' || ($event->raw_message === $context->getData()['raw_message']));
|
|
||||||
});
|
|
||||||
// 设定返回值处理函数
|
|
||||||
$dispatcher->setReturnFunction(function ($result) use ($context) {
|
|
||||||
if (is_string($result)) {
|
|
||||||
$context->reply($result);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$dispatcher->dispatchEvents($context->getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 处理元事件
|
|
||||||
*
|
|
||||||
* @param array $data 消息数据
|
|
||||||
* @param ContextInterface $context 上下文
|
|
||||||
*/
|
|
||||||
private function handleMetaEvent(array $data, ContextInterface $context): void
|
|
||||||
{
|
|
||||||
$dispatcher = new EventDispatcher(CQMetaEvent::class);
|
|
||||||
// 设定匹配规则函数
|
|
||||||
$dispatcher->setRuleFunction(function (CQMetaEvent $event) use ($context) {
|
|
||||||
return compare_object_and_array_by_keys($event, $context->getData(), ['meta_event_type']);
|
|
||||||
});
|
|
||||||
|
|
||||||
$dispatcher->dispatchEvents($context->getData());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 处理通知事件
|
|
||||||
*
|
|
||||||
* @param array $data 消息数据
|
|
||||||
* @param ContextInterface $context 上下文
|
|
||||||
*/
|
|
||||||
private function handleNoticeEvent(array $data, ContextInterface $context): void
|
|
||||||
{
|
|
||||||
$dispatcher = new EventDispatcher(CQNotice::class);
|
|
||||||
// 设定匹配规则函数
|
|
||||||
$dispatcher->setRuleFunction(function (CQNotice $event) use ($context) {
|
|
||||||
return compare_object_and_array_by_keys($event, $context->getData(), ['notice_type', 'sub_type', 'group_id', 'operator_id']);
|
|
||||||
});
|
|
||||||
|
|
||||||
$dispatcher->dispatchEvents($context->getData());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 处理请求事件
|
|
||||||
*
|
|
||||||
* @param array $data 消息数据
|
|
||||||
* @param ContextInterface $context 上下文
|
|
||||||
*/
|
|
||||||
private function handleRequestEvent(array $data, ContextInterface $context): void
|
|
||||||
{
|
|
||||||
$dispatcher = new EventDispatcher(CQRequest::class);
|
|
||||||
// 设定匹配规则函数
|
|
||||||
$dispatcher->setRuleFunction(function (CQRequest $event) use ($context) {
|
|
||||||
return compare_object_and_array_by_keys($event, $context->getData(), ['request_type', 'sub_type', 'user_id', 'comment']);
|
|
||||||
});
|
|
||||||
|
|
||||||
$dispatcher->dispatchEvents($context->getData());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 处理前置事件
|
|
||||||
*
|
|
||||||
* @param array $data 消息数据
|
|
||||||
* @param string $time 执行时机
|
|
||||||
*/
|
|
||||||
private function handleBeforeEvent(array $data, string $time): EventDispatcher
|
|
||||||
{
|
|
||||||
$dispatcher = new EventDispatcher(CQBefore::class);
|
|
||||||
// 设定匹配规则函数
|
|
||||||
$dispatcher->setRuleFunction(function (CQBefore $event) use ($data, $time) {
|
|
||||||
if ($time === 'pre') {
|
|
||||||
$level = $event->level >= 200;
|
|
||||||
} else {
|
|
||||||
$level = $event->level < 200;
|
|
||||||
}
|
|
||||||
return $level && ($event->cq_event === ($data['post_type'] ?? ''));
|
|
||||||
});
|
|
||||||
// 设定返回值处理函数
|
|
||||||
$dispatcher->setReturnFunction(function ($result) {
|
|
||||||
if (!$result) {
|
|
||||||
EventDispatcher::interrupt('block');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$dispatcher->dispatchEvents($data);
|
|
||||||
return $dispatcher;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 处理后置事件
|
|
||||||
*
|
|
||||||
* @param array $data 消息数据
|
|
||||||
*/
|
|
||||||
private function handleAfterEvent(array $data): EventDispatcher
|
|
||||||
{
|
|
||||||
$dispatcher = new EventDispatcher(CQAfter::class);
|
|
||||||
// 设定匹配规则函数
|
|
||||||
$dispatcher->setRuleFunction(function (CQAfter $event) use ($data) {
|
|
||||||
return $event->cq_event === $data['post_type'];
|
|
||||||
});
|
|
||||||
|
|
||||||
$dispatcher->dispatchEvents($data);
|
|
||||||
return $dispatcher;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 判断是否为 API 回调
|
|
||||||
*/
|
|
||||||
private function isAPIResponse(array $data): bool
|
|
||||||
{
|
|
||||||
// API 响应应带有 echo 字段
|
|
||||||
return !isset($data['post_type']) && isset($data['echo']);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 判断是否为事件
|
|
||||||
*/
|
|
||||||
private function isEvent(array $data): bool
|
|
||||||
{
|
|
||||||
// 所有事件都应带有 post_type 字段
|
|
||||||
return isset($data['post_type']);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 判断是否为元事件
|
|
||||||
*/
|
|
||||||
private function isMetaEvent(array $data): bool
|
|
||||||
{
|
|
||||||
return $this->isEvent($data) && $data['post_type'] === 'meta_event';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
183
src/ZM/Annotation/AnnotationHandler.php
Normal file
183
src/ZM/Annotation/AnnotationHandler.php
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace ZM\Annotation;
|
||||||
|
|
||||||
|
use Generator;
|
||||||
|
use Throwable;
|
||||||
|
use ZM\Annotation\Middleware\Middleware;
|
||||||
|
use ZM\Exception\InterruptException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注解调用器,原 EventDispatcher
|
||||||
|
*/
|
||||||
|
class AnnotationHandler
|
||||||
|
{
|
||||||
|
public const STATUS_NORMAL = 0; // 正常结束
|
||||||
|
|
||||||
|
public const STATUS_INTERRUPTED = 1; // 被interrupt了,不管在什么地方
|
||||||
|
|
||||||
|
public const STATUS_EXCEPTION = 2; // 执行过程中抛出了异常
|
||||||
|
|
||||||
|
public const STATUS_BEFORE_FAILED = 3; // 中间件HandleBefore返回了false,所以不执行此方法
|
||||||
|
|
||||||
|
public const STATUS_RULE_FAILED = 4; // 判断事件执行的规则函数判定为false,所以不执行此方法
|
||||||
|
|
||||||
|
/** @var AnnotationBase|string */
|
||||||
|
private $annotation_class;
|
||||||
|
|
||||||
|
/** @var callable */
|
||||||
|
private $rule_callback;
|
||||||
|
|
||||||
|
/** @var callable */
|
||||||
|
private $return_callback;
|
||||||
|
|
||||||
|
/** @var int */
|
||||||
|
private $status = self::STATUS_NORMAL;
|
||||||
|
|
||||||
|
/** @var mixed */
|
||||||
|
private $return_val;
|
||||||
|
|
||||||
|
public function __construct(string $annotation_class)
|
||||||
|
{
|
||||||
|
$this->annotation_class = $annotation_class;
|
||||||
|
logger()->debug('开始分发注解 {annotation}', ['annotation' => $annotation_class]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function interrupt($return_var = null)
|
||||||
|
{
|
||||||
|
throw new InterruptException($return_var);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setRuleCallback(callable $rule): AnnotationHandler
|
||||||
|
{
|
||||||
|
logger()->debug('注解调用器设置事件ruleFunc: {annotation}', ['annotation' => $this->annotation_class]);
|
||||||
|
$this->rule_callback = $rule;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setReturnCallback(callable $return): AnnotationHandler
|
||||||
|
{
|
||||||
|
logger()->debug('注解调用器设置事件returnFunc: {annotation}', ['annotation' => $this->annotation_class]);
|
||||||
|
$this->return_callback = $return;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed ...$params
|
||||||
|
* @throws Throwable
|
||||||
|
*/
|
||||||
|
public function handleAll(...$params)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
foreach ((AnnotationMap::$_list[$this->annotation_class] ?? []) as $v) {
|
||||||
|
$this->handle($v, $this->rule_callback, ...$params);
|
||||||
|
if ($this->status == self::STATUS_BEFORE_FAILED || $this->status == self::STATUS_RULE_FAILED) {
|
||||||
|
$this->status = self::STATUS_NORMAL;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (is_callable($this->return_callback) && $this->status === self::STATUS_NORMAL) {
|
||||||
|
($this->return_callback)($this->return_val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (InterruptException $e) {
|
||||||
|
$this->return_val = $e->return_var;
|
||||||
|
$this->status = self::STATUS_INTERRUPTED;
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
$this->status = self::STATUS_EXCEPTION;
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handle(AnnotationBase $v, ?callable $rule_callback = null, ...$params): bool
|
||||||
|
{
|
||||||
|
$target_class = resolve($v->class);
|
||||||
|
$target_method = $v->method;
|
||||||
|
// 先执行规则
|
||||||
|
if ($rule_callback !== null && !$rule_callback($this, $params)) {
|
||||||
|
$this->status = self::STATUS_RULE_FAILED;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查中间件
|
||||||
|
$mid_obj = [];
|
||||||
|
$before_result = true;
|
||||||
|
foreach ($this->getRegisteredMiddlewares($target_class, $target_method) as $v) {
|
||||||
|
$mid_obj[] = $v[0]; // 投喂中间件
|
||||||
|
if ($v[1] !== '') { // 顺带执行before
|
||||||
|
if (function_exists('container')) {
|
||||||
|
$before_result = container()->call([$v[0], $v[1]], $params);
|
||||||
|
} else {
|
||||||
|
$before_result = call_user_func([$v[0], $v[1]], $params);
|
||||||
|
}
|
||||||
|
if ($before_result === false) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$mid_obj_cnt1 = count($mid_obj) - 1;
|
||||||
|
if ($before_result) { // before全部通过了
|
||||||
|
try {
|
||||||
|
// 执行注解绑定的方法
|
||||||
|
// TODO: 记得完善好容器后把这里的这个if else去掉
|
||||||
|
if (function_exists('container')) {
|
||||||
|
$this->return_val = container()->call([$target_class, $target_method], $params);
|
||||||
|
} else {
|
||||||
|
$this->return_val = call_user_func([$target_class, $target_method], $params);
|
||||||
|
}
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
if ($e instanceof InterruptException) {
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
for ($i = $mid_obj_cnt1; $i >= 0; --$i) {
|
||||||
|
$obj = $mid_obj[$i];
|
||||||
|
foreach ($obj[3] as $name => $method) {
|
||||||
|
if ($e instanceof $name) {
|
||||||
|
$obj[0]->{$method}($e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->status = self::STATUS_BEFORE_FAILED;
|
||||||
|
}
|
||||||
|
for ($i = $mid_obj_cnt1; $i >= 0; --$i) {
|
||||||
|
if ($mid_obj[$i][2] !== '') {
|
||||||
|
$mid_obj[$i][0]->{$mid_obj[$i][2]}($this->return_val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取注册过的中间件
|
||||||
|
*
|
||||||
|
* @param object|string $class 类对象
|
||||||
|
* @param string $method 方法名称
|
||||||
|
*/
|
||||||
|
private function getRegisteredMiddlewares($class, string $method): Generator
|
||||||
|
{
|
||||||
|
foreach (AnnotationMap::$_map[get_class($class)][$method] ?? [] as $annotation) {
|
||||||
|
if ($annotation instanceof Middleware) {
|
||||||
|
$name = $annotation->name;
|
||||||
|
$reg_mid = AnnotationMap::$_middleware_map[$name]['class'] ?? null;
|
||||||
|
if ($reg_mid === null) {
|
||||||
|
logger()->error('Not a valid middleware name: {name}', ['name' => $name]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$obj = new $reg_mid($annotation->params);
|
||||||
|
yield [
|
||||||
|
$obj,
|
||||||
|
AnnotationMap::$_middleware_map[$name]['before'] ?? '',
|
||||||
|
AnnotationMap::$_middleware_map[$name]['after'] ?? '',
|
||||||
|
AnnotationMap::$_middleware_map[$name]['exceptions'] ?? [],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
31
src/ZM/Annotation/AnnotationMap.php
Normal file
31
src/ZM/Annotation/AnnotationMap.php
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace ZM\Annotation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注解全局存取位置
|
||||||
|
*/
|
||||||
|
class AnnotationMap
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 存取注解对象的列表
|
||||||
|
*
|
||||||
|
* @var array<string, array<AnnotationBase>>
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
public static $_list = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array<string, array<string, array<AnnotationBase>>>
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
public static $_map = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
public static $_middleware_map = [];
|
||||||
|
}
|
||||||
@ -10,21 +10,20 @@ use Koriym\Attributes\DualReader;
|
|||||||
use ReflectionClass;
|
use ReflectionClass;
|
||||||
use ReflectionException;
|
use ReflectionException;
|
||||||
use ReflectionMethod;
|
use ReflectionMethod;
|
||||||
use ZM\Annotation\Http\HandleAfter;
|
use Symfony\Component\Routing\RouteCollection;
|
||||||
use ZM\Annotation\Http\HandleBefore;
|
use ZM\Annotation\Http\Controller;
|
||||||
use ZM\Annotation\Http\HandleException;
|
use ZM\Annotation\Http\Route;
|
||||||
use ZM\Annotation\Http\Middleware;
|
|
||||||
use ZM\Annotation\Http\MiddlewareClass;
|
|
||||||
use ZM\Annotation\Http\RequestMapping;
|
|
||||||
use ZM\Annotation\Interfaces\ErgodicAnnotation;
|
use ZM\Annotation\Interfaces\ErgodicAnnotation;
|
||||||
use ZM\Annotation\Interfaces\Level;
|
use ZM\Annotation\Interfaces\Level;
|
||||||
use ZM\Annotation\Module\Closed;
|
use ZM\Annotation\Middleware\HandleAfter;
|
||||||
|
use ZM\Annotation\Middleware\HandleBefore;
|
||||||
|
use ZM\Annotation\Middleware\HandleException;
|
||||||
|
use ZM\Annotation\Middleware\Middleware;
|
||||||
|
use ZM\Annotation\Middleware\MiddlewareClass;
|
||||||
use ZM\Config\ZMConfig;
|
use ZM\Config\ZMConfig;
|
||||||
use ZM\Event\EventManager;
|
use ZM\Exception\ConfigException;
|
||||||
use ZM\Exception\AnnotationException;
|
use ZM\Store\FileSystem;
|
||||||
use ZM\Utils\Manager\RouteManager;
|
use ZM\Store\InternalGlobals;
|
||||||
use ZM\Utils\ZMUtil;
|
|
||||||
use function server;
|
|
||||||
|
|
||||||
class AnnotationParser
|
class AnnotationParser
|
||||||
{
|
{
|
||||||
@ -60,14 +59,15 @@ class AnnotationParser
|
|||||||
/**
|
/**
|
||||||
* 注册各个模块类的注解和模块level的排序
|
* 注册各个模块类的注解和模块level的排序
|
||||||
* @throws ReflectionException
|
* @throws ReflectionException
|
||||||
|
* @throws ConfigException
|
||||||
*/
|
*/
|
||||||
public function registerMods()
|
public function parseAll()
|
||||||
{
|
{
|
||||||
foreach ($this->path_list as $path) {
|
foreach ($this->path_list as $path) {
|
||||||
logger()->debug('parsing annotation in ' . $path[0] . ':' . $path[1]);
|
logger()->debug('parsing annotation in ' . $path[0] . ':' . $path[1]);
|
||||||
$all_class = ZMUtil::getClassesPsr4($path[0], $path[1]);
|
$all_class = FileSystem::getClassesPsr4($path[0], $path[1]);
|
||||||
|
|
||||||
$conf = ZMConfig::get('global', 'runtime')['annotation_reader_ignore'] ?? [];
|
$conf = ZMConfig::get('global.runtime.annotation_reader_ignore');
|
||||||
if (isset($conf['name']) && is_array($conf['name'])) {
|
if (isset($conf['name']) && is_array($conf['name'])) {
|
||||||
foreach ($conf['name'] as $v) {
|
foreach ($conf['name'] as $v) {
|
||||||
AnnotationReader::addGlobalIgnoredName($v);
|
AnnotationReader::addGlobalIgnoredName($v);
|
||||||
@ -145,7 +145,7 @@ class AnnotationParser
|
|||||||
/* @var AnnotationBase $method_anno */
|
/* @var AnnotationBase $method_anno */
|
||||||
$method_anno->class = $v;
|
$method_anno->class = $v;
|
||||||
$method_anno->method = $method_name;
|
$method_anno->method = $method_name;
|
||||||
if (!($method_anno instanceof Middleware) && ($middlewares = ZMConfig::get('global', 'runtime')['global_middleware_binding'][get_class($method_anno)] ?? []) !== []) {
|
if (!($method_anno instanceof Middleware) && ($middlewares = ZMConfig::get('global.global_middleware_binding')[get_class($method_anno)] ?? []) !== []) {
|
||||||
if (!isset($inserted[$v][$method_name])) {
|
if (!isset($inserted[$v][$method_name])) {
|
||||||
// 在这里在其他中间件前插入插入全局的中间件
|
// 在这里在其他中间件前插入插入全局的中间件
|
||||||
foreach ($middlewares as $middleware) {
|
foreach ($middlewares as $middleware) {
|
||||||
@ -156,12 +156,12 @@ class AnnotationParser
|
|||||||
}
|
}
|
||||||
$inserted[$v][$method_name] = true;
|
$inserted[$v][$method_name] = true;
|
||||||
}
|
}
|
||||||
} elseif ($method_anno instanceof RequestMapping) {
|
} elseif ($method_anno instanceof Route) {
|
||||||
RouteManager::importRouteByAnnotation($method_anno, $method_name, $v, $methods_annotations);
|
$this->addRouteAnnotation($method_anno, $method_name, $v, $methods_annotations);
|
||||||
} elseif ($method_anno instanceof Middleware) {
|
} elseif ($method_anno instanceof Middleware) {
|
||||||
$this->middleware_map[$method_anno->class][$method_anno->method][] = $method_anno;
|
$this->middleware_map[$method_anno->class][$method_anno->method][] = $method_anno;
|
||||||
} else {
|
} else {
|
||||||
EventManager::$event_map[$method_anno->class][$method_anno->method][] = $method_anno;
|
AnnotationMap::$_map[$method_anno->class][$method_anno->method][] = $method_anno;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -214,9 +214,7 @@ class AnnotationParser
|
|||||||
*/
|
*/
|
||||||
public function addRegisterPath(string $path, string $indoor_name)
|
public function addRegisterPath(string $path, string $indoor_name)
|
||||||
{
|
{
|
||||||
if (server()->worker_id === 0) {
|
|
||||||
logger()->debug('Add register path: ' . $path . ' => ' . $indoor_name);
|
logger()->debug('Add register path: ' . $path . ' => ' . $indoor_name);
|
||||||
}
|
|
||||||
$this->path_list[] = [$path, $indoor_name];
|
$this->path_list[] = [$path, $indoor_name];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -238,26 +236,7 @@ class AnnotationParser
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function getUsedTime()
|
||||||
* @throws AnnotationException
|
|
||||||
*/
|
|
||||||
public function verifyMiddlewares()
|
|
||||||
{
|
|
||||||
if ((ZMConfig::get('global', 'runtime')['middleware_error_policy'] ?? 1) === 2) {
|
|
||||||
// 我承认套三层foreach很不优雅,但是这个会很快的。
|
|
||||||
foreach ($this->middleware_map as $v) {
|
|
||||||
foreach ($v as $vs) {
|
|
||||||
foreach ($vs as $mid) {
|
|
||||||
if (!isset($this->middlewares[$mid->middleware])) {
|
|
||||||
throw new AnnotationException("Annotation parse error: Unknown MiddlewareClass named \"{$mid->middleware}\"!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getRunTime()
|
|
||||||
{
|
{
|
||||||
return microtime(true) - $this->start_time;
|
return microtime(true) - $this->start_time;
|
||||||
}
|
}
|
||||||
@ -287,4 +266,27 @@ class AnnotationParser
|
|||||||
}
|
}
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function addRouteAnnotation(Route $vss, $method, $class, $methods_annotations)
|
||||||
|
{
|
||||||
|
if (InternalGlobals::$routes === null) {
|
||||||
|
InternalGlobals::$routes = new RouteCollection();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 拿到所属方法的类上面有没有控制器的注解
|
||||||
|
$prefix = '';
|
||||||
|
foreach ($methods_annotations as $annotation) {
|
||||||
|
if ($annotation instanceof Controller) {
|
||||||
|
$prefix = $annotation->prefix;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$tail = trim($vss->route, '/');
|
||||||
|
$route_name = $prefix . ($tail === '' ? '' : '/') . $tail;
|
||||||
|
logger()->debug('添加路由:' . $route_name);
|
||||||
|
$route = new \Symfony\Component\Routing\Route($route_name, ['_class' => $class, '_method' => $method]);
|
||||||
|
$route->setMethods($vss->request_method);
|
||||||
|
|
||||||
|
InternalGlobals::$routes->add(md5($route_name), $route);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,32 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\Annotation\CQ;
|
|
||||||
|
|
||||||
use Attribute;
|
|
||||||
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
|
|
||||||
use Doctrine\Common\Annotations\Annotation\Required;
|
|
||||||
use Doctrine\Common\Annotations\Annotation\Target;
|
|
||||||
use ZM\Annotation\AnnotationBase;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class CQAPIResponse
|
|
||||||
* @Annotation
|
|
||||||
* @NamedArgumentConstructor()
|
|
||||||
* @Target("METHOD")
|
|
||||||
*/
|
|
||||||
#[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_METHOD)]
|
|
||||||
class CQAPIResponse extends AnnotationBase
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var int
|
|
||||||
* @Required()
|
|
||||||
*/
|
|
||||||
public $retcode;
|
|
||||||
|
|
||||||
public function __construct($retcode)
|
|
||||||
{
|
|
||||||
$this->retcode = $retcode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,51 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\Annotation\CQ;
|
|
||||||
|
|
||||||
use Attribute;
|
|
||||||
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
|
|
||||||
use Doctrine\Common\Annotations\Annotation\Target;
|
|
||||||
use ZM\Annotation\AnnotationBase;
|
|
||||||
use ZM\Annotation\Interfaces\Level;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class CQAfter
|
|
||||||
* @Annotation
|
|
||||||
* @NamedArgumentConstructor()
|
|
||||||
* @Target("METHOD")
|
|
||||||
*/
|
|
||||||
#[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_METHOD)]
|
|
||||||
class CQAfter extends AnnotationBase implements Level
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
* @Required()
|
|
||||||
*/
|
|
||||||
public $cq_event;
|
|
||||||
|
|
||||||
public $level = 20;
|
|
||||||
|
|
||||||
public function __construct($cq_event, $level = 20)
|
|
||||||
{
|
|
||||||
$this->cq_event = $cq_event;
|
|
||||||
$this->level = $level;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function getLevel()
|
|
||||||
{
|
|
||||||
return $this->level;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param mixed $level
|
|
||||||
*/
|
|
||||||
public function setLevel($level)
|
|
||||||
{
|
|
||||||
$this->level = $level;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,52 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\Annotation\CQ;
|
|
||||||
|
|
||||||
use Attribute;
|
|
||||||
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
|
|
||||||
use Doctrine\Common\Annotations\Annotation\Required;
|
|
||||||
use Doctrine\Common\Annotations\Annotation\Target;
|
|
||||||
use ZM\Annotation\AnnotationBase;
|
|
||||||
use ZM\Annotation\Interfaces\Level;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class CQBefore
|
|
||||||
* @Annotation
|
|
||||||
* @NamedArgumentConstructor()
|
|
||||||
* @Target("METHOD")
|
|
||||||
*/
|
|
||||||
#[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_METHOD)]
|
|
||||||
class CQBefore extends AnnotationBase implements Level
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
* @Required()
|
|
||||||
*/
|
|
||||||
public $cq_event;
|
|
||||||
|
|
||||||
public $level = 20;
|
|
||||||
|
|
||||||
public function __construct($cq_event, $level = 20)
|
|
||||||
{
|
|
||||||
$this->cq_event = $cq_event;
|
|
||||||
$this->level = $level;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return int 返回等级
|
|
||||||
*/
|
|
||||||
public function getLevel(): int
|
|
||||||
{
|
|
||||||
return $this->level;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param mixed $level
|
|
||||||
*/
|
|
||||||
public function setLevel($level)
|
|
||||||
{
|
|
||||||
$this->level = $level;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,86 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\Annotation\CQ;
|
|
||||||
|
|
||||||
use Attribute;
|
|
||||||
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
|
|
||||||
use Doctrine\Common\Annotations\Annotation\Target;
|
|
||||||
use ZM\Annotation\AnnotationBase;
|
|
||||||
use ZM\Annotation\Interfaces\Level;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class CQCommand
|
|
||||||
* @Annotation
|
|
||||||
* @NamedArgumentConstructor
|
|
||||||
* @Target("ALL")
|
|
||||||
*/
|
|
||||||
#[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_ALL)]
|
|
||||||
class CQCommand extends AnnotationBase implements Level
|
|
||||||
{
|
|
||||||
/** @var string */
|
|
||||||
public $match = '';
|
|
||||||
|
|
||||||
/** @var string */
|
|
||||||
public $pattern = '';
|
|
||||||
|
|
||||||
/** @var string */
|
|
||||||
public $regex = '';
|
|
||||||
|
|
||||||
/** @var string */
|
|
||||||
public $start_with = '';
|
|
||||||
|
|
||||||
/** @var string */
|
|
||||||
public $end_with = '';
|
|
||||||
|
|
||||||
/** @var string */
|
|
||||||
public $keyword = '';
|
|
||||||
|
|
||||||
/** @var string[] */
|
|
||||||
public $alias = [];
|
|
||||||
|
|
||||||
/** @var string */
|
|
||||||
public $message_type = '';
|
|
||||||
|
|
||||||
/** @var int */
|
|
||||||
public $user_id = 0;
|
|
||||||
|
|
||||||
/** @var int */
|
|
||||||
public $group_id = 0;
|
|
||||||
|
|
||||||
/** @var int */
|
|
||||||
public $discuss_id = 0;
|
|
||||||
|
|
||||||
/** @var int */
|
|
||||||
public $level = 20;
|
|
||||||
|
|
||||||
public function __construct($match = '', $pattern = '', $regex = '', $start_with = '', $end_with = '', $keyword = '', $alias = [], $message_type = '', $user_id = 0, $group_id = 0, $discuss_id = 0, $level = 20)
|
|
||||||
{
|
|
||||||
$this->match = $match;
|
|
||||||
$this->pattern = $pattern;
|
|
||||||
$this->regex = $regex;
|
|
||||||
$this->start_with = $start_with;
|
|
||||||
$this->end_with = $end_with;
|
|
||||||
$this->keyword = $keyword;
|
|
||||||
$this->alias = $alias;
|
|
||||||
$this->message_type = $message_type;
|
|
||||||
$this->user_id = $user_id;
|
|
||||||
$this->group_id = $group_id;
|
|
||||||
$this->discuss_id = $discuss_id;
|
|
||||||
$this->level = $level;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getLevel(): int
|
|
||||||
{
|
|
||||||
return $this->level;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param int $level
|
|
||||||
*/
|
|
||||||
public function setLevel($level)
|
|
||||||
{
|
|
||||||
$this->level = $level;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,65 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\Annotation\CQ;
|
|
||||||
|
|
||||||
use Attribute;
|
|
||||||
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
|
|
||||||
use Doctrine\Common\Annotations\Annotation\Target;
|
|
||||||
use ZM\Annotation\AnnotationBase;
|
|
||||||
use ZM\Annotation\Interfaces\Level;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class CQMessage
|
|
||||||
* @Annotation
|
|
||||||
* @NamedArgumentConstructor()
|
|
||||||
* @Target("ALL")
|
|
||||||
*/
|
|
||||||
#[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_ALL)]
|
|
||||||
class CQMessage extends AnnotationBase implements Level
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public $message_type = '';
|
|
||||||
|
|
||||||
/** @var int */
|
|
||||||
public $user_id = 0;
|
|
||||||
|
|
||||||
/** @var int */
|
|
||||||
public $group_id = 0;
|
|
||||||
|
|
||||||
/** @var int */
|
|
||||||
public $discuss_id = 0;
|
|
||||||
|
|
||||||
/** @var string */
|
|
||||||
public $message = '';
|
|
||||||
|
|
||||||
/** @var string */
|
|
||||||
public $raw_message = '';
|
|
||||||
|
|
||||||
/** @var int */
|
|
||||||
public $level = 20;
|
|
||||||
|
|
||||||
public function __construct($message_type = '', $user_id = 0, $group_id = 0, $discuss_id = 0, $message = '', $raw_message = '', $level = 20)
|
|
||||||
{
|
|
||||||
$this->message_type = $message_type;
|
|
||||||
$this->user_id = $user_id;
|
|
||||||
$this->group_id = $group_id;
|
|
||||||
$this->discuss_id = $discuss_id;
|
|
||||||
$this->message = $message;
|
|
||||||
$this->raw_message = $raw_message;
|
|
||||||
$this->level = $level;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getLevel(): int
|
|
||||||
{
|
|
||||||
return $this->level;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setLevel($level)
|
|
||||||
{
|
|
||||||
$this->level = $level;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,53 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\Annotation\CQ;
|
|
||||||
|
|
||||||
use Attribute;
|
|
||||||
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
|
|
||||||
use Doctrine\Common\Annotations\Annotation\Required;
|
|
||||||
use Doctrine\Common\Annotations\Annotation\Target;
|
|
||||||
use ZM\Annotation\AnnotationBase;
|
|
||||||
use ZM\Annotation\Interfaces\Level;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class CQMetaEvent
|
|
||||||
* @Annotation
|
|
||||||
* @NamedArgumentConstructor()
|
|
||||||
* @Target("ALL")
|
|
||||||
*/
|
|
||||||
#[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_ALL)]
|
|
||||||
class CQMetaEvent extends AnnotationBase implements Level
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
* @Required()
|
|
||||||
*/
|
|
||||||
public $meta_event_type;
|
|
||||||
|
|
||||||
/** @var int */
|
|
||||||
public $level = 20;
|
|
||||||
|
|
||||||
public function __construct($meta_event_type, $level = 20)
|
|
||||||
{
|
|
||||||
$this->meta_event_type = $meta_event_type;
|
|
||||||
$this->level = $level;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return int 返回等级
|
|
||||||
*/
|
|
||||||
public function getLevel(): int
|
|
||||||
{
|
|
||||||
return $this->level;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param int $level
|
|
||||||
*/
|
|
||||||
public function setLevel($level)
|
|
||||||
{
|
|
||||||
$this->level = $level;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,58 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\Annotation\CQ;
|
|
||||||
|
|
||||||
use Attribute;
|
|
||||||
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
|
|
||||||
use Doctrine\Common\Annotations\Annotation\Target;
|
|
||||||
use ZM\Annotation\AnnotationBase;
|
|
||||||
use ZM\Annotation\Interfaces\Level;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class CQNotice
|
|
||||||
* @Annotation
|
|
||||||
* @NamedArgumentConstructor()
|
|
||||||
* @Target("ALL")
|
|
||||||
*/
|
|
||||||
#[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_ALL)]
|
|
||||||
class CQNotice extends AnnotationBase implements Level
|
|
||||||
{
|
|
||||||
/** @var string */
|
|
||||||
public $notice_type = '';
|
|
||||||
|
|
||||||
/** @var string */
|
|
||||||
public $sub_type = '';
|
|
||||||
|
|
||||||
/** @var int */
|
|
||||||
public $group_id = 0;
|
|
||||||
|
|
||||||
/** @var int */
|
|
||||||
public $operator_id = 0;
|
|
||||||
|
|
||||||
/** @var int */
|
|
||||||
public $level = 20;
|
|
||||||
|
|
||||||
public function __construct($notice_type = '', $sub_type = '', $group_id = 0, $operator_id = 0, $level = 20)
|
|
||||||
{
|
|
||||||
$this->notice_type = $notice_type;
|
|
||||||
$this->sub_type = $sub_type;
|
|
||||||
$this->group_id = $group_id;
|
|
||||||
$this->operator_id = $operator_id;
|
|
||||||
$this->level = $level;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getLevel(): int
|
|
||||||
{
|
|
||||||
return $this->level;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param int $level
|
|
||||||
*/
|
|
||||||
public function setLevel($level)
|
|
||||||
{
|
|
||||||
$this->level = $level;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,58 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\Annotation\CQ;
|
|
||||||
|
|
||||||
use Attribute;
|
|
||||||
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
|
|
||||||
use Doctrine\Common\Annotations\Annotation\Target;
|
|
||||||
use ZM\Annotation\AnnotationBase;
|
|
||||||
use ZM\Annotation\Interfaces\Level;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class CQRequest
|
|
||||||
* @Annotation
|
|
||||||
* @NamedArgumentConstructor()
|
|
||||||
* @Target("ALL")
|
|
||||||
*/
|
|
||||||
#[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_ALL)]
|
|
||||||
class CQRequest extends AnnotationBase implements Level
|
|
||||||
{
|
|
||||||
/** @var string */
|
|
||||||
public $request_type = '';
|
|
||||||
|
|
||||||
/** @var string */
|
|
||||||
public $sub_type = '';
|
|
||||||
|
|
||||||
/** @var int */
|
|
||||||
public $user_id = 0;
|
|
||||||
|
|
||||||
/** @var string */
|
|
||||||
public $comment = '';
|
|
||||||
|
|
||||||
/** @var int */
|
|
||||||
public $level = 20;
|
|
||||||
|
|
||||||
public function __construct($request_type = '', $sub_type = '', $user_id = 0, $comment = '', $level = 20)
|
|
||||||
{
|
|
||||||
$this->request_type = $request_type;
|
|
||||||
$this->sub_type = $sub_type;
|
|
||||||
$this->user_id = $user_id;
|
|
||||||
$this->comment = $comment;
|
|
||||||
$this->level = $level;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getLevel(): int
|
|
||||||
{
|
|
||||||
return $this->level;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param int $level
|
|
||||||
*/
|
|
||||||
public function setLevel($level)
|
|
||||||
{
|
|
||||||
$this->level = $level;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,146 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\Annotation\CQ;
|
|
||||||
|
|
||||||
use Attribute;
|
|
||||||
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
|
|
||||||
use Doctrine\Common\Annotations\Annotation\Required;
|
|
||||||
use ZM\Annotation\AnnotationBase;
|
|
||||||
use ZM\Annotation\Interfaces\ErgodicAnnotation;
|
|
||||||
use ZM\Exception\InvalidArgumentException;
|
|
||||||
use ZM\Exception\ZMKnownException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class CommandArgument
|
|
||||||
* @Annotation
|
|
||||||
* @NamedArgumentConstructor()
|
|
||||||
* @Target("ALL")
|
|
||||||
*/
|
|
||||||
#[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_ALL)]
|
|
||||||
class CommandArgument extends AnnotationBase implements ErgodicAnnotation
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
* @Required()
|
|
||||||
*/
|
|
||||||
public $name;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public $description = '';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public $type = 'string';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var bool
|
|
||||||
*/
|
|
||||||
public $required = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public $prompt = '';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public $default = '';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
public $timeout = 60;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
public $error_prompt_policy = 1;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $name 参数名称(可以是中文)
|
|
||||||
* @param string $description 参数描述(默认为空)
|
|
||||||
* @param bool $required 参数是否必需,如果是必需,为true(默认为false)
|
|
||||||
* @param string $prompt 当参数为必需时,返回给用户的提示输入的消息(默认为"请输入$name")
|
|
||||||
* @param string $default 当required为false时,未匹配到参数将自动使用default值(默认为空)
|
|
||||||
* @param int $timeout prompt超时时间(默认为60秒)
|
|
||||||
* @throws InvalidArgumentException|ZMKnownException
|
|
||||||
*/
|
|
||||||
public function __construct(
|
|
||||||
string $name,
|
|
||||||
string $description = '',
|
|
||||||
string $type = 'string',
|
|
||||||
bool $required = false,
|
|
||||||
string $prompt = '',
|
|
||||||
string $default = '',
|
|
||||||
int $timeout = 60,
|
|
||||||
int $error_prompt_policy = 1
|
|
||||||
) {
|
|
||||||
$this->name = $name;
|
|
||||||
$this->description = $description;
|
|
||||||
$this->type = $this->fixTypeName($type);
|
|
||||||
$this->required = $required;
|
|
||||||
$this->prompt = $prompt;
|
|
||||||
$this->default = $default;
|
|
||||||
$this->timeout = $timeout;
|
|
||||||
$this->error_prompt_policy = $error_prompt_policy;
|
|
||||||
if ($this->type === 'bool') {
|
|
||||||
if ($this->default === '') {
|
|
||||||
$this->default = 'yes';
|
|
||||||
}
|
|
||||||
if (!in_array($this->default, array_merge(TRUE_LIST, FALSE_LIST))) {
|
|
||||||
throw new InvalidArgumentException('CommandArgument参数 ' . $name . ' 类型传入类型应为布尔型,检测到非法的默认值 ' . $this->default);
|
|
||||||
}
|
|
||||||
} elseif ($this->type === 'number') {
|
|
||||||
if ($this->default === '') {
|
|
||||||
$this->default = '0';
|
|
||||||
}
|
|
||||||
if (!is_numeric($this->default)) {
|
|
||||||
throw new InvalidArgumentException('CommandArgument参数 ' . $name . ' 类型传入类型应为数字型,检测到非法的默认值 ' . $this->default);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getTypeErrorPrompt(): string
|
|
||||||
{
|
|
||||||
return '参数类型错误,请重新输入!';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getErrorQuitPrompt(): string
|
|
||||||
{
|
|
||||||
return '参数类型错误,停止输入!';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @throws ZMKnownException
|
|
||||||
*/
|
|
||||||
protected function fixTypeName(string $type): string
|
|
||||||
{
|
|
||||||
$table = [
|
|
||||||
'str' => 'string',
|
|
||||||
'string' => 'string',
|
|
||||||
'strings' => 'string',
|
|
||||||
'byte' => 'string',
|
|
||||||
'num' => 'number',
|
|
||||||
'number' => 'number',
|
|
||||||
'int' => 'number',
|
|
||||||
'float' => 'number',
|
|
||||||
'double' => 'number',
|
|
||||||
'boolean' => 'bool',
|
|
||||||
'bool' => 'bool',
|
|
||||||
'true' => 'bool',
|
|
||||||
'any' => 'any',
|
|
||||||
'all' => 'any',
|
|
||||||
'*' => 'any',
|
|
||||||
];
|
|
||||||
if (array_key_exists($type, $table)) {
|
|
||||||
return $table[$type];
|
|
||||||
}
|
|
||||||
throw new ZMKnownException(zm_internal_errcode('E00077') . 'Invalid argument type: ' . $type . ', only support any, string, number and bool !');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -2,12 +2,11 @@
|
|||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace ZM\Annotation\Module;
|
namespace ZM\Annotation;
|
||||||
|
|
||||||
use Attribute;
|
use Attribute;
|
||||||
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
|
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
|
||||||
use Doctrine\Common\Annotations\Annotation\Target;
|
use Doctrine\Common\Annotations\Annotation\Target;
|
||||||
use ZM\Annotation\AnnotationBase;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class Closed
|
* Class Closed
|
||||||
@ -1,41 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\Annotation\Command;
|
|
||||||
|
|
||||||
use Attribute;
|
|
||||||
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
|
|
||||||
use Doctrine\Common\Annotations\Annotation\Required;
|
|
||||||
use Doctrine\Common\Annotations\Annotation\Target;
|
|
||||||
use ZM\Annotation\AnnotationBase;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class TerminalCommand
|
|
||||||
* @Annotation
|
|
||||||
* @NamedArgumentConstructor
|
|
||||||
* @Target("METHOD")
|
|
||||||
*/
|
|
||||||
#[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_METHOD)]
|
|
||||||
class TerminalCommand extends AnnotationBase
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
* @Required()
|
|
||||||
*/
|
|
||||||
public $command;
|
|
||||||
|
|
||||||
public $alias = '';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public $description = '';
|
|
||||||
|
|
||||||
public function __construct($command, $alias = '', $description = '')
|
|
||||||
{
|
|
||||||
$this->command = $command;
|
|
||||||
$this->alias = $alias;
|
|
||||||
$this->description = $description;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,85 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\Annotation\Cron;
|
|
||||||
|
|
||||||
use Attribute;
|
|
||||||
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
|
|
||||||
use Doctrine\Common\Annotations\Annotation\Required;
|
|
||||||
use Doctrine\Common\Annotations\Annotation\Target;
|
|
||||||
use ZM\Annotation\AnnotationBase;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @Annotation
|
|
||||||
* @NamedArgumentConstructor()
|
|
||||||
* @Target("METHOD")
|
|
||||||
*/
|
|
||||||
#[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)]
|
|
||||||
class Cron extends AnnotationBase
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
* @Required()
|
|
||||||
*/
|
|
||||||
public $expression;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
public $worker_id = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
public $check_delay_time = 20000;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
public $max_iteration_count = 1000;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var int Cron执行状态
|
|
||||||
*/
|
|
||||||
private $status = 0;
|
|
||||||
|
|
||||||
private $record_next_time = 0;
|
|
||||||
|
|
||||||
public function __construct(string $expression, int $worker_id = 0, int $check_delay_time = 20000, int $max_iteration_count = 1000)
|
|
||||||
{
|
|
||||||
$this->expression = $expression;
|
|
||||||
$this->worker_id = $worker_id;
|
|
||||||
$this->check_delay_time = $check_delay_time;
|
|
||||||
$this->max_iteration_count = $max_iteration_count;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getStatus(): int
|
|
||||||
{
|
|
||||||
return $this->status;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
public function setStatus(int $status): void
|
|
||||||
{
|
|
||||||
$this->status = $status;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
public function getRecordNextTime(): int
|
|
||||||
{
|
|
||||||
return $this->record_next_time;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
public function setRecordNextTime(int $record_next_time): void
|
|
||||||
{
|
|
||||||
$this->record_next_time = $record_next_time;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace ZM\Annotation\Swoole;
|
namespace ZM\Annotation\Framework;
|
||||||
|
|
||||||
use Attribute;
|
use Attribute;
|
||||||
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
|
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
|
||||||
@ -1,46 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\Annotation\Http;
|
|
||||||
|
|
||||||
use Attribute;
|
|
||||||
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
|
|
||||||
use Doctrine\Common\Annotations\Annotation\Required;
|
|
||||||
use Doctrine\Common\Annotations\Annotation\Target;
|
|
||||||
use ZM\Annotation\AnnotationBase;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class RequestMethod
|
|
||||||
* @Annotation
|
|
||||||
* @NamedArgumentConstructor()
|
|
||||||
* @Target("METHOD")
|
|
||||||
*/
|
|
||||||
#[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_METHOD)]
|
|
||||||
class RequestMethod extends AnnotationBase
|
|
||||||
{
|
|
||||||
public const GET = 'GET';
|
|
||||||
|
|
||||||
public const POST = 'POST';
|
|
||||||
|
|
||||||
public const PUT = 'PUT';
|
|
||||||
|
|
||||||
public const PATCH = 'PATCH';
|
|
||||||
|
|
||||||
public const DELETE = 'DELETE';
|
|
||||||
|
|
||||||
public const OPTIONS = 'OPTIONS';
|
|
||||||
|
|
||||||
public const HEAD = 'HEAD';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
* @Required()
|
|
||||||
*/
|
|
||||||
public $method = self::GET;
|
|
||||||
|
|
||||||
public function __construct($method)
|
|
||||||
{
|
|
||||||
$this->method = $method;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -17,7 +17,7 @@ use ZM\Annotation\AnnotationBase;
|
|||||||
* @Target("METHOD")
|
* @Target("METHOD")
|
||||||
*/
|
*/
|
||||||
#[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_METHOD)]
|
#[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_METHOD)]
|
||||||
class RequestMapping extends AnnotationBase
|
class Route extends AnnotationBase
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var string
|
* @var string
|
||||||
@ -33,7 +33,7 @@ class RequestMapping extends AnnotationBase
|
|||||||
/**
|
/**
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
public $request_method = [RequestMethod::GET, RequestMethod::POST];
|
public $request_method = ['GET', 'POST'];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Routing path params binding. eg. {"id"="\d+"}
|
* Routing path params binding. eg. {"id"="\d+"}
|
||||||
@ -41,7 +41,7 @@ class RequestMapping extends AnnotationBase
|
|||||||
*/
|
*/
|
||||||
public $params = [];
|
public $params = [];
|
||||||
|
|
||||||
public function __construct($route, $name = '', $request_method = [RequestMethod::GET, RequestMethod::POST], $params = [])
|
public function __construct($route, $name = '', $request_method = ['GET', 'POST'], $params = [])
|
||||||
{
|
{
|
||||||
$this->route = $route;
|
$this->route = $route;
|
||||||
$this->name = $name;
|
$this->name = $name;
|
||||||
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace ZM\Annotation\Http;
|
namespace ZM\Annotation\Middleware;
|
||||||
|
|
||||||
use Attribute;
|
use Attribute;
|
||||||
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
|
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
|
||||||
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace ZM\Annotation\Http;
|
namespace ZM\Annotation\Middleware;
|
||||||
|
|
||||||
use Attribute;
|
use Attribute;
|
||||||
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
|
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
|
||||||
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace ZM\Annotation\Http;
|
namespace ZM\Annotation\Middleware;
|
||||||
|
|
||||||
use Attribute;
|
use Attribute;
|
||||||
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
|
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
|
||||||
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace ZM\Annotation\Http;
|
namespace ZM\Annotation\Middleware;
|
||||||
|
|
||||||
use Attribute;
|
use Attribute;
|
||||||
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
|
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
|
||||||
@ -24,16 +24,16 @@ class Middleware extends AnnotationBase implements ErgodicAnnotation
|
|||||||
* @var string
|
* @var string
|
||||||
* @Required()
|
* @Required()
|
||||||
*/
|
*/
|
||||||
public $middleware;
|
public $name;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string[]
|
* @var string[]
|
||||||
*/
|
*/
|
||||||
public $params = [];
|
public $params = [];
|
||||||
|
|
||||||
public function __construct($middleware, $params = [])
|
public function __construct($name, $params = [])
|
||||||
{
|
{
|
||||||
$this->middleware = $middleware;
|
$this->name = $name;
|
||||||
$this->params = $params;
|
$this->params = $params;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace ZM\Annotation\Http;
|
namespace ZM\Annotation\Middleware;
|
||||||
|
|
||||||
use Attribute;
|
use Attribute;
|
||||||
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
|
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
|
||||||
53
src/ZM/Annotation/OneBot/OnOneBotEvent.php
Normal file
53
src/ZM/Annotation/OneBot/OnOneBotEvent.php
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace ZM\Annotation\OneBot;
|
||||||
|
|
||||||
|
use Attribute;
|
||||||
|
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
|
||||||
|
use Doctrine\Common\Annotations\Annotation\Target;
|
||||||
|
use ZM\Annotation\AnnotationBase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Annotation
|
||||||
|
* @Target("METHOD")
|
||||||
|
* @NamedArgumentConstructor
|
||||||
|
*/
|
||||||
|
#[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)]
|
||||||
|
class OnOneBotEvent extends AnnotationBase
|
||||||
|
{
|
||||||
|
/** @var null|string */
|
||||||
|
public $type;
|
||||||
|
|
||||||
|
/** @var null|string */
|
||||||
|
public $detail_type;
|
||||||
|
|
||||||
|
/** @var null|string */
|
||||||
|
public $impl;
|
||||||
|
|
||||||
|
/** @var null|string */
|
||||||
|
public $platform;
|
||||||
|
|
||||||
|
/** @var null|string */
|
||||||
|
public $self_id;
|
||||||
|
|
||||||
|
/** @var null|string */
|
||||||
|
public $sub_type;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
?string $type = null,
|
||||||
|
?string $detail_type = null,
|
||||||
|
?string $impl = null,
|
||||||
|
?string $platform = null,
|
||||||
|
?string $self_id = null,
|
||||||
|
?string $sub_type = null
|
||||||
|
) {
|
||||||
|
$this->type = $type;
|
||||||
|
$this->detail_type = $detail_type;
|
||||||
|
$this->impl = $impl;
|
||||||
|
$this->platform = $platform;
|
||||||
|
$this->self_id = $self_id;
|
||||||
|
$this->sub_type = $sub_type;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,31 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\Annotation\Swoole;
|
|
||||||
|
|
||||||
use Attribute;
|
|
||||||
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
|
|
||||||
use Doctrine\Common\Annotations\Annotation\Target;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @Annotation
|
|
||||||
* @NamedArgumentConstructor()
|
|
||||||
* @Target("METHOD")
|
|
||||||
* Class OnCloseEvent
|
|
||||||
*/
|
|
||||||
#[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_METHOD)]
|
|
||||||
class OnCloseEvent extends OnSwooleEventBase
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public $connect_type = 'default';
|
|
||||||
|
|
||||||
public function __construct($connect_type = 'default', $rule = '', $level = 20)
|
|
||||||
{
|
|
||||||
$this->connect_type = $connect_type;
|
|
||||||
$this->rule = $rule;
|
|
||||||
$this->level = $level;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,25 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\Annotation\Swoole;
|
|
||||||
|
|
||||||
use Attribute;
|
|
||||||
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
|
|
||||||
use Doctrine\Common\Annotations\Annotation\Target;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @Annotation
|
|
||||||
* @NamedArgumentConstructor()
|
|
||||||
* @Target("METHOD")
|
|
||||||
* @since 2.7.0
|
|
||||||
*/
|
|
||||||
#[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_METHOD)]
|
|
||||||
class OnManagerStartEvent extends OnSwooleEventBase
|
|
||||||
{
|
|
||||||
public function __construct($rule = '', $level = 20)
|
|
||||||
{
|
|
||||||
$this->rule = $rule;
|
|
||||||
$this->level = $level;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,31 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\Annotation\Swoole;
|
|
||||||
|
|
||||||
use Attribute;
|
|
||||||
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
|
|
||||||
use Doctrine\Common\Annotations\Annotation\Target;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @Annotation
|
|
||||||
* @Target("METHOD")
|
|
||||||
* @NamedArgumentConstructor()
|
|
||||||
* Class OnMessageEvent
|
|
||||||
*/
|
|
||||||
#[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_METHOD)]
|
|
||||||
class OnMessageEvent extends OnSwooleEventBase
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public $connect_type = 'default';
|
|
||||||
|
|
||||||
public function __construct($connect_type = 'default', $rule = '', $level = 20)
|
|
||||||
{
|
|
||||||
$this->connect_type = $connect_type;
|
|
||||||
$this->rule = $rule;
|
|
||||||
$this->level = $level;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,31 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\Annotation\Swoole;
|
|
||||||
|
|
||||||
use Attribute;
|
|
||||||
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
|
|
||||||
use Doctrine\Common\Annotations\Annotation\Target;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @Annotation
|
|
||||||
* @NamedArgumentConstructor()
|
|
||||||
* @Target("METHOD")
|
|
||||||
* Class OnOpenEvent
|
|
||||||
*/
|
|
||||||
#[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_METHOD)]
|
|
||||||
class OnOpenEvent extends OnSwooleEventBase
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public $connect_type = 'default';
|
|
||||||
|
|
||||||
public function __construct($connect_type = 'default', $rule = '', $level = 20)
|
|
||||||
{
|
|
||||||
$this->connect_type = $connect_type;
|
|
||||||
$this->rule = $rule;
|
|
||||||
$this->level = $level;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,32 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\Annotation\Swoole;
|
|
||||||
|
|
||||||
use Attribute;
|
|
||||||
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
|
|
||||||
use Doctrine\Common\Annotations\Annotation\Required;
|
|
||||||
use Doctrine\Common\Annotations\Annotation\Target;
|
|
||||||
use ZM\Annotation\AnnotationBase;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class OnPipeMessageEvent
|
|
||||||
* @Annotation
|
|
||||||
* @NamedArgumentConstructor()
|
|
||||||
* @Target("METHOD")
|
|
||||||
*/
|
|
||||||
#[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_METHOD)]
|
|
||||||
class OnPipeMessageEvent extends AnnotationBase
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
* @Required()
|
|
||||||
*/
|
|
||||||
public $action;
|
|
||||||
|
|
||||||
public function __construct($action)
|
|
||||||
{
|
|
||||||
$this->action = $action;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,25 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\Annotation\Swoole;
|
|
||||||
|
|
||||||
use Attribute;
|
|
||||||
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
|
|
||||||
use Doctrine\Common\Annotations\Annotation\Target;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @Annotation
|
|
||||||
* @NamedArgumentConstructor()
|
|
||||||
* @Target("METHOD")
|
|
||||||
* Class OnRequestEvent
|
|
||||||
*/
|
|
||||||
#[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_METHOD)]
|
|
||||||
class OnRequestEvent extends OnSwooleEventBase
|
|
||||||
{
|
|
||||||
public function __construct($rule = '', $level = 20)
|
|
||||||
{
|
|
||||||
$this->rule = $rule;
|
|
||||||
$this->level = $level;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,21 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\Annotation\Swoole;
|
|
||||||
|
|
||||||
use Attribute;
|
|
||||||
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
|
|
||||||
use Doctrine\Common\Annotations\Annotation\Target;
|
|
||||||
use ZM\Annotation\AnnotationBase;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class OnSave
|
|
||||||
* @Annotation
|
|
||||||
* @NamedArgumentConstructor()
|
|
||||||
* @Target("METHOD")
|
|
||||||
*/
|
|
||||||
#[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_METHOD)]
|
|
||||||
class OnSave extends AnnotationBase
|
|
||||||
{
|
|
||||||
}
|
|
||||||
@ -1,30 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\Annotation\Swoole;
|
|
||||||
|
|
||||||
use Attribute;
|
|
||||||
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
|
|
||||||
use Doctrine\Common\Annotations\Annotation\Target;
|
|
||||||
use ZM\Annotation\AnnotationBase;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class OnWorkerStart
|
|
||||||
* @Annotation
|
|
||||||
* @NamedArgumentConstructor()
|
|
||||||
* @Target("METHOD")
|
|
||||||
*/
|
|
||||||
#[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_METHOD)]
|
|
||||||
class OnStart extends AnnotationBase
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
public $worker_id = 0;
|
|
||||||
|
|
||||||
public function __construct($worker_id = 0)
|
|
||||||
{
|
|
||||||
$this->worker_id = $worker_id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,43 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\Annotation\Swoole;
|
|
||||||
|
|
||||||
use Attribute;
|
|
||||||
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
|
|
||||||
use Doctrine\Common\Annotations\Annotation\Required;
|
|
||||||
use Doctrine\Common\Annotations\Annotation\Target;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class OnSwooleEvent
|
|
||||||
* @Annotation
|
|
||||||
* @NamedArgumentConstructor()
|
|
||||||
* @Target("METHOD")
|
|
||||||
*/
|
|
||||||
#[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_METHOD)]
|
|
||||||
class OnSwooleEvent extends OnSwooleEventBase
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
* @Required
|
|
||||||
*/
|
|
||||||
public $type;
|
|
||||||
|
|
||||||
public function __construct($type, $rule = '', $level = 20)
|
|
||||||
{
|
|
||||||
$this->type = $type;
|
|
||||||
$this->rule = $rule;
|
|
||||||
$this->level = $level;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getType(): string
|
|
||||||
{
|
|
||||||
return $this->type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setType(string $type)
|
|
||||||
{
|
|
||||||
$this->type = $type;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,45 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\Annotation\Swoole;
|
|
||||||
|
|
||||||
use ZM\Annotation\AnnotationBase;
|
|
||||||
use ZM\Annotation\Interfaces\Level;
|
|
||||||
use ZM\Annotation\Interfaces\Rule;
|
|
||||||
|
|
||||||
abstract class OnSwooleEventBase extends AnnotationBase implements Level, Rule
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public $rule = '';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
public $level = 20;
|
|
||||||
|
|
||||||
public function getRule()
|
|
||||||
{
|
|
||||||
return $this->rule !== '' ? $this->rule : true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setRule(string $rule)
|
|
||||||
{
|
|
||||||
$this->rule = $rule;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getLevel(): int
|
|
||||||
{
|
|
||||||
return $this->level;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param int $level
|
|
||||||
*/
|
|
||||||
public function setLevel($level)
|
|
||||||
{
|
|
||||||
$this->level = $level;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,47 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\Annotation\Swoole;
|
|
||||||
|
|
||||||
use Attribute;
|
|
||||||
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
|
|
||||||
use Doctrine\Common\Annotations\Annotation\Required;
|
|
||||||
use Doctrine\Common\Annotations\Annotation\Target;
|
|
||||||
use ZM\Annotation\AnnotationBase;
|
|
||||||
use ZM\Annotation\Interfaces\Rule;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class OnTask
|
|
||||||
* @Annotation
|
|
||||||
* @NamedArgumentConstructor()
|
|
||||||
* @Target("METHOD")
|
|
||||||
*/
|
|
||||||
#[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_METHOD)]
|
|
||||||
class OnTask extends AnnotationBase implements Rule
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
* @Required()
|
|
||||||
*/
|
|
||||||
public $task_name;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public $rule = '';
|
|
||||||
|
|
||||||
public function __construct($task_name, $rule = '')
|
|
||||||
{
|
|
||||||
$this->task_name = $task_name;
|
|
||||||
$this->rule = $rule;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string 返回规则语句
|
|
||||||
*/
|
|
||||||
public function getRule(): string
|
|
||||||
{
|
|
||||||
return $this->rule;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,25 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\Annotation\Swoole;
|
|
||||||
|
|
||||||
use Attribute;
|
|
||||||
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
|
|
||||||
use Doctrine\Common\Annotations\Annotation\Target;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class OnTaskEvent
|
|
||||||
* @Annotation
|
|
||||||
* @NamedArgumentConstructor()
|
|
||||||
* @Target("METHOD")
|
|
||||||
*/
|
|
||||||
#[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_METHOD)]
|
|
||||||
class OnTaskEvent extends OnSwooleEventBase
|
|
||||||
{
|
|
||||||
public function __construct($rule = '', $level = 20)
|
|
||||||
{
|
|
||||||
$this->rule = $rule;
|
|
||||||
$this->level = $level;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,39 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\Annotation\Swoole;
|
|
||||||
|
|
||||||
use Attribute;
|
|
||||||
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
|
|
||||||
use Doctrine\Common\Annotations\Annotation\Required;
|
|
||||||
use Doctrine\Common\Annotations\Annotation\Target;
|
|
||||||
use ZM\Annotation\AnnotationBase;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class OnTick
|
|
||||||
* @Annotation
|
|
||||||
* @NamedArgumentConstructor()
|
|
||||||
* @Target("METHOD")
|
|
||||||
* @since 1.2
|
|
||||||
*/
|
|
||||||
#[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)]
|
|
||||||
class OnTick extends AnnotationBase
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var int
|
|
||||||
* @Required()
|
|
||||||
*/
|
|
||||||
public $tick_ms;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
public $worker_id = 0;
|
|
||||||
|
|
||||||
public function __construct($tick_ms, $worker_id = 0)
|
|
||||||
{
|
|
||||||
$this->tick_ms = $tick_ms;
|
|
||||||
$this->worker_id = $worker_id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,36 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\Annotation\Swoole;
|
|
||||||
|
|
||||||
use Attribute;
|
|
||||||
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
|
|
||||||
use Doctrine\Common\Annotations\Annotation\Required;
|
|
||||||
use Doctrine\Common\Annotations\Annotation\Target;
|
|
||||||
use ZM\Annotation\AnnotationBase;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class SwooleHandler
|
|
||||||
* @Annotation
|
|
||||||
* @NamedArgumentConstructor()
|
|
||||||
* @Target("ALL")
|
|
||||||
*/
|
|
||||||
#[Attribute(Attribute::TARGET_ALL)]
|
|
||||||
class SwooleHandler extends AnnotationBase
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
* @Required()
|
|
||||||
*/
|
|
||||||
public $event;
|
|
||||||
|
|
||||||
/** @var string */
|
|
||||||
public $params = '';
|
|
||||||
|
|
||||||
public function __construct($event, $params = '')
|
|
||||||
{
|
|
||||||
$this->event = $event;
|
|
||||||
$this->params = $params;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -4,15 +4,10 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace ZM\Command;
|
namespace ZM\Command;
|
||||||
|
|
||||||
use ArrayIterator;
|
|
||||||
use League\CLImate\CLImate;
|
|
||||||
use Phar;
|
|
||||||
use Symfony\Component\Console\Command\Command;
|
use Symfony\Component\Console\Command\Command;
|
||||||
use Symfony\Component\Console\Input\InputInterface;
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
use Symfony\Component\Console\Input\InputOption;
|
use Symfony\Component\Console\Input\InputOption;
|
||||||
use Symfony\Component\Console\Output\OutputInterface;
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
use ZM\Console\TermColor;
|
|
||||||
use ZM\Utils\DataProvider;
|
|
||||||
|
|
||||||
class BuildCommand extends Command
|
class BuildCommand extends Command
|
||||||
{
|
{
|
||||||
@ -20,10 +15,8 @@ class BuildCommand extends Command
|
|||||||
protected static $defaultName = 'build';
|
protected static $defaultName = 'build';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var OutputInterface
|
* 配置
|
||||||
*/
|
*/
|
||||||
private $output;
|
|
||||||
|
|
||||||
protected function configure()
|
protected function configure()
|
||||||
{
|
{
|
||||||
$this->setDescription('Build an ".phar" file | 将项目构建一个phar包');
|
$this->setDescription('Build an ".phar" file | 将项目构建一个phar包');
|
||||||
@ -34,8 +27,9 @@ class BuildCommand extends Command
|
|||||||
|
|
||||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||||
{
|
{
|
||||||
|
/* TODO
|
||||||
$this->output = $output;
|
$this->output = $output;
|
||||||
$target_dir = $input->getOption('target') ?? (WORKING_DIR);
|
$target_dir = $input->getOption('target') ?? WORKING_DIR;
|
||||||
if (mb_strpos($target_dir, '../')) {
|
if (mb_strpos($target_dir, '../')) {
|
||||||
$target_dir = realpath($target_dir);
|
$target_dir = realpath($target_dir);
|
||||||
}
|
}
|
||||||
@ -58,10 +52,11 @@ class BuildCommand extends Command
|
|||||||
}
|
}
|
||||||
$filename = 'server.phar';
|
$filename = 'server.phar';
|
||||||
$this->build($target_dir, $filename);
|
$this->build($target_dir, $filename);
|
||||||
|
*/
|
||||||
return 0;
|
$output->writeln('<error>Not implemented.</error>');
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
private function build($target_dir, $filename)
|
private function build($target_dir, $filename)
|
||||||
{
|
{
|
||||||
@unlink($target_dir . $filename);
|
@unlink($target_dir . $filename);
|
||||||
@ -101,4 +96,5 @@ class BuildCommand extends Command
|
|||||||
$this->output->writeln('Successfully built. Location: ' . $target_dir . "{$filename}");
|
$this->output->writeln('Successfully built. Location: ' . $target_dir . "{$filename}");
|
||||||
$this->output->writeln('<info>You may use `chmod +x server.phar` to let phar executable with `./` command</info>');
|
$this->output->writeln('<info>You may use `chmod +x server.phar` to let phar executable with `./` command</info>');
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,7 +27,7 @@ class CheckConfigCommand extends Command
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
$current_cfg = getcwd() . '/config/';
|
$current_cfg = getcwd() . '/config/';
|
||||||
$remote_cfg = include_once FRAMEWORK_ROOT_DIR . '/config/global.php';
|
$remote_cfg = include_once FRAMEWORK_ROOT_DIR . '/config/global_old.php';
|
||||||
if (file_exists($current_cfg . 'global.php')) {
|
if (file_exists($current_cfg . 'global.php')) {
|
||||||
$this->check($remote_cfg, 'global.php', $output);
|
$this->check($remote_cfg, 'global.php', $output);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,7 +8,6 @@ use Symfony\Component\Console\Command\Command;
|
|||||||
use Symfony\Component\Console\Input\InputInterface;
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
use Symfony\Component\Console\Output\OutputInterface;
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
use ZM\Config\ZMConfig;
|
use ZM\Config\ZMConfig;
|
||||||
use ZM\Utils\DataProvider;
|
|
||||||
|
|
||||||
class SystemdGenerateCommand extends Command
|
class SystemdGenerateCommand extends Command
|
||||||
{
|
{
|
||||||
@ -22,7 +21,7 @@ class SystemdGenerateCommand extends Command
|
|||||||
|
|
||||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||||
{
|
{
|
||||||
ZMConfig::setDirectory(DataProvider::getSourceRootDir() . '/config');
|
ZMConfig::setDirectory(SOURCE_ROOT_DIR . '/config');
|
||||||
$path = $this->generate();
|
$path = $this->generate();
|
||||||
$output->writeln('<info>成功生成 systemd 文件,位置:' . $path . '</info>');
|
$output->writeln('<info>成功生成 systemd 文件,位置:' . $path . '</info>');
|
||||||
$output->writeln('<info>有关如何使用 systemd 配置文件,请访问 `https://github.com/zhamao-robot/zhamao-framework/issues/36`</info>');
|
$output->writeln('<info>有关如何使用 systemd 配置文件,请访问 `https://github.com/zhamao-robot/zhamao-framework/issues/36`</info>');
|
||||||
@ -38,8 +37,7 @@ class SystemdGenerateCommand extends Command
|
|||||||
global $argv;
|
global $argv;
|
||||||
$s .= "\nExecStart=" . PHP_BINARY . " {$argv[0]} server";
|
$s .= "\nExecStart=" . PHP_BINARY . " {$argv[0]} server";
|
||||||
$s .= "\nRestart=always\n\n[Install]\nWantedBy=multi-user.target\n";
|
$s .= "\nRestart=always\n\n[Install]\nWantedBy=multi-user.target\n";
|
||||||
@mkdir(getcwd() . '/resources/');
|
file_put_contents(WORKING_DIR . '/zhamao.service', $s);
|
||||||
file_put_contents(ZMConfig::get('global', 'zm_data') . 'zhamao.service', $s);
|
return WORKING_DIR . '/zhamao.service';
|
||||||
return ZMConfig::get('global', 'zm_data') . 'zhamao.service';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -48,7 +48,7 @@ class InitCommand extends Command
|
|||||||
$info = pathinfo($file);
|
$info = pathinfo($file);
|
||||||
@mkdir($base_path . $info['dirname'], 0777, true);
|
@mkdir($base_path . $info['dirname'], 0777, true);
|
||||||
echo 'Copying ' . $file . PHP_EOL;
|
echo 'Copying ' . $file . PHP_EOL;
|
||||||
$package_name = (json_decode(file_get_contents(__DIR__ . '/../../../composer.json'), true)['name']);
|
$package_name = json_decode(file_get_contents(__DIR__ . '/../../../composer.json'), true)['name'];
|
||||||
copy($base_path . '/vendor/' . $package_name . $file, $base_path . $file);
|
copy($base_path . '/vendor/' . $package_name . $file, $base_path . $file);
|
||||||
} else {
|
} else {
|
||||||
echo 'Skipping ' . $file . ' , file exists.' . PHP_EOL;
|
echo 'Skipping ' . $file . ' , file exists.' . PHP_EOL;
|
||||||
|
|||||||
@ -1,46 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\Command\Module;
|
|
||||||
|
|
||||||
use Symfony\Component\Console\Command\Command;
|
|
||||||
use Symfony\Component\Console\Input\InputInterface;
|
|
||||||
use Symfony\Component\Console\Input\InputOption;
|
|
||||||
use Symfony\Component\Console\Output\OutputInterface;
|
|
||||||
use ZM\Config\ZMConfig;
|
|
||||||
use ZM\Console\Console;
|
|
||||||
use ZM\Utils\DataProvider;
|
|
||||||
|
|
||||||
abstract class ModuleCommand extends Command
|
|
||||||
{
|
|
||||||
protected function configure()
|
|
||||||
{
|
|
||||||
$this->addOption('env', null, InputOption::VALUE_REQUIRED, '设置环境类型 (production, development, staging)', '');
|
|
||||||
$this->addOption('log-theme', null, InputOption::VALUE_REQUIRED, '改变终端的主题配色', 'default');
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
|
||||||
{
|
|
||||||
ZMConfig::setDirectory(DataProvider::getSourceRootDir() . '/config');
|
|
||||||
ZMConfig::setEnv($input->getOption('env'));
|
|
||||||
if (ZMConfig::get('global') === false) {
|
|
||||||
exit(zm_internal_errcode('E00007') . 'Global config load failed: ' . ZMConfig::$last_error . "\nPlease init first!\nSee: https://github.com/zhamao-robot/zhamao-framework/issues/37\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 定义常量
|
|
||||||
/** @noinspection PhpIncludeInspection */
|
|
||||||
include_once DataProvider::getFrameworkRootDir() . '/src/ZM/global_defines.php';
|
|
||||||
|
|
||||||
Console::init(
|
|
||||||
ZMConfig::get('global', 'info_level') ?? 2,
|
|
||||||
null,
|
|
||||||
$input->getOption('log-theme'),
|
|
||||||
($o = ZMConfig::get('console_color')) === false ? [] : $o
|
|
||||||
);
|
|
||||||
|
|
||||||
$timezone = ZMConfig::get('global', 'timezone') ?? 'Asia/Shanghai';
|
|
||||||
date_default_timezone_set($timezone);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,94 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\Command\Module;
|
|
||||||
|
|
||||||
use Symfony\Component\Console\Input\InputInterface;
|
|
||||||
use Symfony\Component\Console\Output\OutputInterface;
|
|
||||||
use ZM\Console\Console;
|
|
||||||
use ZM\Exception\ZMException;
|
|
||||||
use ZM\Utils\DataProvider;
|
|
||||||
use ZM\Utils\Manager\ModuleManager;
|
|
||||||
|
|
||||||
class ModuleListCommand extends ModuleCommand
|
|
||||||
{
|
|
||||||
// the name of the command (the part after "bin/console")
|
|
||||||
protected static $defaultName = 'module:list';
|
|
||||||
|
|
||||||
protected function configure()
|
|
||||||
{
|
|
||||||
parent::configure();
|
|
||||||
$this->setDescription('查看所有模块信息');
|
|
||||||
$this->setHelp('此功能将会把炸毛框架的模块列举出来。');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @throws ZMException
|
|
||||||
*/
|
|
||||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
|
||||||
{
|
|
||||||
parent::execute($input, $output);
|
|
||||||
|
|
||||||
$list = ModuleManager::getConfiguredModules();
|
|
||||||
|
|
||||||
foreach ($list as $v) {
|
|
||||||
echo '[' . Console::setColor($v['name'], 'green') . ']' . PHP_EOL;
|
|
||||||
$out_list = ['类型' => '源码(source)'];
|
|
||||||
if (isset($v['version'])) {
|
|
||||||
$out_list['版本'] = $v['version'];
|
|
||||||
}
|
|
||||||
if (isset($v['description'])) {
|
|
||||||
$out_list['描述'] = $v['description'];
|
|
||||||
}
|
|
||||||
$out_list['目录'] = str_replace(DataProvider::getSourceRootDir() . '/', '', $v['module-path']);
|
|
||||||
$this->printList($out_list);
|
|
||||||
}
|
|
||||||
if ($list === []) {
|
|
||||||
echo Console::setColor('没有发现已编写打包配置文件(zm.json)的模块!', 'yellow') . PHP_EOL;
|
|
||||||
}
|
|
||||||
$list = ModuleManager::getPackedModules();
|
|
||||||
foreach ($list as $v) {
|
|
||||||
echo '[' . Console::setColor($v['name'], 'gold') . ']' . PHP_EOL;
|
|
||||||
$out_list = ['类型' => '模块包(phar)'];
|
|
||||||
if (isset($v['module-config']['version'])) {
|
|
||||||
$out_list['版本'] = $v['module-config']['version'];
|
|
||||||
}
|
|
||||||
if (isset($v['module-config']['description'])) {
|
|
||||||
$out_list['描述'] = $v['module-config']['description'];
|
|
||||||
}
|
|
||||||
$out_list['位置'] = str_replace(DataProvider::getSourceRootDir() . '/', '', $v['phar-path']);
|
|
||||||
$this->printList($out_list);
|
|
||||||
}
|
|
||||||
if ($list === []) {
|
|
||||||
echo Console::setColor('没有发现已打包且装载的模块!', 'yellow') . PHP_EOL;
|
|
||||||
}
|
|
||||||
|
|
||||||
$list = ModuleManager::getComposerModules();
|
|
||||||
foreach ($list as $v) {
|
|
||||||
echo '[' . Console::setColor($v['name'], 'blue') . ']' . PHP_EOL;
|
|
||||||
$out_list = ['类型' => 'Composer库(composer)'];
|
|
||||||
$out_list['包名'] = $v['composer-name'];
|
|
||||||
$out_list['目录'] = str_replace(DataProvider::getSourceRootDir() . '/', '', $v['module-path']);
|
|
||||||
if (isset($v['version'])) {
|
|
||||||
$out_list['版本'] = $v['version'];
|
|
||||||
}
|
|
||||||
if (isset($v['description'])) {
|
|
||||||
$out_list['描述'] = $v['description'];
|
|
||||||
}
|
|
||||||
$out_list['命名空间'] = $v['namespace'];
|
|
||||||
$this->printList($out_list);
|
|
||||||
}
|
|
||||||
if ($list === []) {
|
|
||||||
echo Console::setColor('没有发现Composer模块!', 'yellow') . PHP_EOL;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function printList($list)
|
|
||||||
{
|
|
||||||
foreach ($list as $k => $v) {
|
|
||||||
echo "\t" . $k . ': ' . Console::setColor($v, 'yellow') . PHP_EOL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,49 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\Command\Module;
|
|
||||||
|
|
||||||
use Symfony\Component\Console\Input\InputArgument;
|
|
||||||
use Symfony\Component\Console\Input\InputInterface;
|
|
||||||
use Symfony\Component\Console\Input\InputOption;
|
|
||||||
use Symfony\Component\Console\Output\OutputInterface;
|
|
||||||
use ZM\Console\Console;
|
|
||||||
use ZM\Exception\ZMException;
|
|
||||||
use ZM\Utils\DataProvider;
|
|
||||||
use ZM\Utils\Manager\ModuleManager;
|
|
||||||
|
|
||||||
class ModulePackCommand extends ModuleCommand
|
|
||||||
{
|
|
||||||
// the name of the command (the part after "bin/console")
|
|
||||||
protected static $defaultName = 'module:pack';
|
|
||||||
|
|
||||||
protected function configure()
|
|
||||||
{
|
|
||||||
parent::configure();
|
|
||||||
$this->addArgument('module-name', InputArgument::REQUIRED);
|
|
||||||
$this->setDescription('将配置好的模块构建一个phar包');
|
|
||||||
$this->setHelp('此功能将会把炸毛框架的模块打包为".phar",供发布和执行。');
|
|
||||||
$this->addOption('target', 'D', InputOption::VALUE_REQUIRED, 'Output Directory | 指定输出目录');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @throws ZMException
|
|
||||||
*/
|
|
||||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
|
||||||
{
|
|
||||||
parent::execute($input, $output);
|
|
||||||
$list = ModuleManager::getConfiguredModules();
|
|
||||||
if (!isset($list[$input->getArgument('module-name')])) {
|
|
||||||
$output->writeln('<error>不存在模块 ' . $input->getArgument('module-name') . ' !</error>');
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
$result = ModuleManager::packModule($list[$input->getArgument('module-name')], $input->getOption('target') ?? (DataProvider::getDataFolder() . '/output'));
|
|
||||||
if ($result) {
|
|
||||||
Console::success('打包完成!');
|
|
||||||
} else {
|
|
||||||
Console::error('打包失败!');
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,47 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\Command\Module;
|
|
||||||
|
|
||||||
use Symfony\Component\Console\Input\InputArgument;
|
|
||||||
use Symfony\Component\Console\Input\InputInterface;
|
|
||||||
use Symfony\Component\Console\Output\OutputInterface;
|
|
||||||
use ZM\Console\Console;
|
|
||||||
use ZM\Utils\Manager\ModuleManager;
|
|
||||||
|
|
||||||
class ModuleUnpackCommand extends ModuleCommand
|
|
||||||
{
|
|
||||||
// the name of the command (the part after "bin/console")
|
|
||||||
protected static $defaultName = 'module:unpack';
|
|
||||||
|
|
||||||
protected function configure()
|
|
||||||
{
|
|
||||||
parent::configure();
|
|
||||||
$this->addOption('overwrite-light-cache', null, null, '覆盖现有的LightCache项目');
|
|
||||||
$this->addOption('overwrite-zm-data', null, null, '覆盖现有的zm_data文件');
|
|
||||||
$this->addOption('overwrite-source', null, null, '覆盖现有的源码文件');
|
|
||||||
$this->addOption('ignore-depends', null, null, '解包时忽略检查依赖');
|
|
||||||
$this->addArgument('module-name', InputArgument::REQUIRED, '模块名称');
|
|
||||||
$this->setDescription('解包一个phar模块到src目录');
|
|
||||||
$this->setHelp('此功能将phar格式的模块包解包到src目录下。');
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
|
||||||
{
|
|
||||||
parent::execute($input, $output);
|
|
||||||
|
|
||||||
$list = ModuleManager::getPackedModules();
|
|
||||||
if (!isset($list[$input->getArgument('module-name')])) {
|
|
||||||
$output->writeln('<error>不存在打包的模块 ' . $input->getArgument('module-name') . ' !</error>');
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
$result = ModuleManager::unpackModule($list[$input->getArgument('module-name')], $input->getOptions());
|
|
||||||
if ($result) {
|
|
||||||
Console::success('解压完成!');
|
|
||||||
} else {
|
|
||||||
Console::error('解压失败!');
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,102 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\Command;
|
|
||||||
|
|
||||||
use Swoole\Atomic;
|
|
||||||
use Swoole\Http\Request;
|
|
||||||
use Swoole\Http\Response;
|
|
||||||
use Swoole\Http\Server;
|
|
||||||
use Swoole\Process;
|
|
||||||
use Symfony\Component\Console\Command\Command;
|
|
||||||
use Symfony\Component\Console\Input\InputArgument;
|
|
||||||
use Symfony\Component\Console\Input\InputInterface;
|
|
||||||
use Symfony\Component\Console\Input\InputOption;
|
|
||||||
use Symfony\Component\Console\Output\OutputInterface;
|
|
||||||
use ZM\Config\ZMConfig;
|
|
||||||
use ZM\Console\Console;
|
|
||||||
use ZM\Logger\TablePrinter;
|
|
||||||
use ZM\Store\ZMAtomic;
|
|
||||||
use ZM\Utils\DataProvider;
|
|
||||||
use ZM\Utils\HttpUtil;
|
|
||||||
|
|
||||||
class PureHttpCommand extends Command
|
|
||||||
{
|
|
||||||
// the name of the command (the part after "bin/console")
|
|
||||||
protected static $defaultName = 'simple-http-server';
|
|
||||||
|
|
||||||
protected function configure()
|
|
||||||
{
|
|
||||||
$this->setDescription('Run a simple http server | 启动一个简单的文件 HTTP 服务器');
|
|
||||||
$this->setHelp('直接运行可以启动');
|
|
||||||
$this->addArgument('dir', InputArgument::REQUIRED, 'Your directory');
|
|
||||||
$this->addOption('host', 'H', InputOption::VALUE_REQUIRED, '启动监听地址');
|
|
||||||
$this->addOption('port', 'P', InputOption::VALUE_REQUIRED, '启动监听地址的端口');
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
|
||||||
{
|
|
||||||
$tty_width = explode(' ', trim(exec('stty size')))[1];
|
|
||||||
if (realpath($input->getArgument('dir') ?? '.') === false) {
|
|
||||||
$output->writeln('<error>Directory error(' . ($input->getArgument('dir') ?? '.') . '): no such file or directory.</error>');
|
|
||||||
return self::FAILURE;
|
|
||||||
}
|
|
||||||
ZMConfig::setDirectory(DataProvider::getSourceRootDir() . '/config');
|
|
||||||
$global = ZMConfig::get('global');
|
|
||||||
$host = $input->getOption('host') ?? $global['host'];
|
|
||||||
$port = $input->getOption('port') ?? $global['port'];
|
|
||||||
|
|
||||||
$index = ['index.html', 'index.htm'];
|
|
||||||
$out = [
|
|
||||||
'listen' => $host . ':' . $port,
|
|
||||||
'version' => ZM_VERSION,
|
|
||||||
'web_root' => realpath($input->getArgument('dir') ?? '.'),
|
|
||||||
'index' => implode(',', $index),
|
|
||||||
];
|
|
||||||
$printer = new TablePrinter($out);
|
|
||||||
$printer->printAll();
|
|
||||||
$server = new Server($host, $port);
|
|
||||||
$server->set(ZMConfig::get('global', 'swoole'));
|
|
||||||
Console::init(2, $server);
|
|
||||||
ZMAtomic::$atomics['request'] = [];
|
|
||||||
for ($i = 0; $i < 32; ++$i) {
|
|
||||||
ZMAtomic::$atomics['request'][$i] = new Atomic(0);
|
|
||||||
}
|
|
||||||
$server->on('request', function (Request $request, Response $response) use ($input, $index, $server) {
|
|
||||||
ZMAtomic::$atomics['request'][$server->worker_id]->add(1);
|
|
||||||
HttpUtil::handleStaticPage(
|
|
||||||
$request->server['request_uri'],
|
|
||||||
$response,
|
|
||||||
[
|
|
||||||
'document_root' => realpath($input->getArgument('dir') ?? '.'),
|
|
||||||
'document_index' => $index,
|
|
||||||
]
|
|
||||||
);
|
|
||||||
// echo "\r" . Coroutine::stats()["coroutine_peak_num"];
|
|
||||||
});
|
|
||||||
$server->on('start', function ($server) {
|
|
||||||
Process::signal(SIGINT, function () use ($server) {
|
|
||||||
echo "\r";
|
|
||||||
logger()->notice('服务器收到中断信号 SIGINT,正在停止');
|
|
||||||
for ($i = 0; $i < 32; ++$i) {
|
|
||||||
$num = ZMAtomic::$atomics['request'][$i]->get();
|
|
||||||
if ($num != 0) {
|
|
||||||
echo "[{$i}]: " . $num . "\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$server->shutdown();
|
|
||||||
$server->stop();
|
|
||||||
});
|
|
||||||
logger()->notice('服务器已启动,请使用 Ctrl+C 以停止。');
|
|
||||||
});
|
|
||||||
$server->start();
|
|
||||||
// return this if there was no problem running the command
|
|
||||||
// (it's equivalent to returning int(0))
|
|
||||||
return 0;
|
|
||||||
// or return this if some error happened during the execution
|
|
||||||
// (it's equivalent to returning int(1))
|
|
||||||
// return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -8,7 +8,7 @@ use Symfony\Component\Console\Command\Command;
|
|||||||
use Symfony\Component\Console\Input\InputInterface;
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
use Symfony\Component\Console\Output\OutputInterface;
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
use ZM\Exception\ZMKnownException;
|
use ZM\Exception\ZMKnownException;
|
||||||
use ZM\Utils\Manager\ProcessManager;
|
use ZM\Process\ProcessStateManager;
|
||||||
|
|
||||||
abstract class ServerCommand extends Command
|
abstract class ServerCommand extends Command
|
||||||
{
|
{
|
||||||
@ -19,11 +19,12 @@ abstract class ServerCommand extends Command
|
|||||||
*/
|
*/
|
||||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||||
{
|
{
|
||||||
$file = ProcessManager::getProcessState(ZM_PROCESS_MASTER);
|
$file = ProcessStateManager::getProcessState(ZM_PROCESS_MASTER);
|
||||||
|
/* @noinspection PhpComposerExtensionStubsInspection */
|
||||||
if ($file === false || posix_getsid(intval($file['pid'])) === false) {
|
if ($file === false || posix_getsid(intval($file['pid'])) === false) {
|
||||||
$output->writeln('<comment>未检测到正在运行的守护进程或框架进程!</comment>');
|
$output->writeln('<comment>未检测到正在运行的守护进程或框架进程!</comment>');
|
||||||
if (ProcessManager::isStateEmpty()) {
|
if (ProcessStateManager::isStateEmpty()) {
|
||||||
ProcessManager::removeProcessState(ZM_PROCESS_MASTER);
|
ProcessStateManager::removeProcessState(ZM_PROCESS_MASTER);
|
||||||
} else {
|
} else {
|
||||||
$output->writeln('<comment>检测到可能残留的守护进程或框架进程,请使用命令关闭:server:stop --force</comment>');
|
$output->writeln('<comment>检测到可能残留的守护进程或框架进程,请使用命令关闭:server:stop --force</comment>');
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,24 +4,24 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace ZM\Command\Server;
|
namespace ZM\Command\Server;
|
||||||
|
|
||||||
use Symfony\Component\Console\Input\InputDefinition;
|
|
||||||
use Symfony\Component\Console\Input\InputInterface;
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
use Symfony\Component\Console\Input\InputOption;
|
use Symfony\Component\Console\Input\InputOption;
|
||||||
use Symfony\Component\Console\Output\OutputInterface;
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
use ZM\Exception\ConfigException;
|
use ZM\Exception\ConfigException;
|
||||||
|
use ZM\Exception\InitException;
|
||||||
use ZM\Exception\ZMKnownException;
|
use ZM\Exception\ZMKnownException;
|
||||||
use ZM\Framework;
|
use ZM\Framework;
|
||||||
use ZM\Utils\Manager\ProcessManager;
|
use ZM\Process\ProcessStateManager;
|
||||||
|
|
||||||
class ServerStartCommand extends ServerCommand
|
class ServerStartCommand extends ServerCommand
|
||||||
{
|
{
|
||||||
protected static $defaultName = 'server';
|
protected static $defaultName = 'server';
|
||||||
|
|
||||||
public static function exportDefinition(): InputDefinition
|
public static function exportOptionArray(): array
|
||||||
{
|
{
|
||||||
$cmd = new self();
|
$cmd = new self();
|
||||||
$cmd->configure();
|
$cmd->configure();
|
||||||
return $cmd->getDefinition();
|
return array_map(function ($x) { return $x->getDefault(); }, $cmd->getDefinition()->getOptions());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function configure()
|
protected function configure()
|
||||||
@ -29,7 +29,10 @@ class ServerStartCommand extends ServerCommand
|
|||||||
$this->setAliases(['server:start']);
|
$this->setAliases(['server:start']);
|
||||||
$this->setDefinition([
|
$this->setDefinition([
|
||||||
new InputOption('debug-mode', 'D', null, '开启调试模式 (这将关闭协程化)'),
|
new InputOption('debug-mode', 'D', null, '开启调试模式 (这将关闭协程化)'),
|
||||||
|
new InputOption('config-dir', null, InputOption::VALUE_REQUIRED, '指定其他配置文件目录'),
|
||||||
|
new InputOption('driver', null, InputOption::VALUE_REQUIRED, '指定驱动类型'),
|
||||||
new InputOption('log-debug', null, null, '调整消息等级到debug (log-level=4)'),
|
new InputOption('log-debug', null, null, '调整消息等级到debug (log-level=4)'),
|
||||||
|
new InputOption('log-level', null, InputOption::VALUE_REQUIRED, '调整消息等级到debug (log-level=4)'),
|
||||||
new InputOption('log-verbose', null, null, '调整消息等级到verbose (log-level=3)'),
|
new InputOption('log-verbose', null, null, '调整消息等级到verbose (log-level=3)'),
|
||||||
new InputOption('log-info', null, null, '调整消息等级到info (log-level=2)'),
|
new InputOption('log-info', null, null, '调整消息等级到info (log-level=2)'),
|
||||||
new InputOption('log-warning', null, null, '调整消息等级到warning (log-level=1)'),
|
new InputOption('log-warning', null, null, '调整消息等级到warning (log-level=1)'),
|
||||||
@ -59,17 +62,20 @@ class ServerStartCommand extends ServerCommand
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws ZMKnownException
|
* @throws ZMKnownException
|
||||||
* @throws ConfigException
|
* @throws ConfigException|InitException
|
||||||
|
* @noinspection PhpComposerExtensionStubsInspection
|
||||||
*/
|
*/
|
||||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||||
{
|
{
|
||||||
if (($opt = $input->getOption('env')) !== null) {
|
// 这段用于config的环境解析,但显然不是很好的方式,应该改成一个独立的方法,不应该在这里检查,但暂时搁置,TODO
|
||||||
|
/* if (($opt = $input->getOption('env')) !== null) {
|
||||||
if (!in_array($opt, ['production', 'staging', 'development', ''])) {
|
if (!in_array($opt, ['production', 'staging', 'development', ''])) {
|
||||||
$output->writeln('<error> "--env" option only accept production, development, staging and [empty] ! </error>');
|
$output->writeln('<error> "--env" option only accept production, development, staging and [empty] ! </error>');
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
$state = ProcessManager::getProcessState(ZM_PROCESS_MASTER);
|
if (\OneBot\Driver\Process\ProcessManager::isSupportedMultiProcess()) {
|
||||||
|
$state = ProcessStateManager::getProcessState(ZM_PROCESS_MASTER);
|
||||||
if (!$input->getOption('no-state-check')) {
|
if (!$input->getOption('no-state-check')) {
|
||||||
if (is_array($state) && posix_getsid($state['pid'] ?? -1) !== false) {
|
if (is_array($state) && posix_getsid($state['pid'] ?? -1) !== false) {
|
||||||
$output->writeln("<error>检测到已经在 pid: {$state['pid']} 进程启动了框架!</error>");
|
$output->writeln("<error>检测到已经在 pid: {$state['pid']} 进程启动了框架!</error>");
|
||||||
@ -77,6 +83,7 @@ class ServerStartCommand extends ServerCommand
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
(new Framework($input->getOptions()))->start();
|
(new Framework($input->getOptions()))->start();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,8 +8,8 @@ use Swoole\Process;
|
|||||||
use Symfony\Component\Console\Input\InputInterface;
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
use Symfony\Component\Console\Input\InputOption;
|
use Symfony\Component\Console\Input\InputOption;
|
||||||
use Symfony\Component\Console\Output\OutputInterface;
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
use ZM\Utils\DataProvider;
|
use ZM\Process\ProcessStateManager;
|
||||||
use ZM\Utils\Manager\ProcessManager;
|
use ZM\Store\FileSystem;
|
||||||
|
|
||||||
class ServerStopCommand extends ServerCommand
|
class ServerStopCommand extends ServerCommand
|
||||||
{
|
{
|
||||||
@ -26,8 +26,8 @@ class ServerStopCommand extends ServerCommand
|
|||||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||||
{
|
{
|
||||||
if ($input->getOption('force') !== false) {
|
if ($input->getOption('force') !== false) {
|
||||||
$file_path = _zm_pid_dir();
|
$file_path = ZM_PID_DIR;
|
||||||
$list = DataProvider::scanDirFiles($file_path, false, true);
|
$list = FileSystem::scanDirFiles($file_path, false, true);
|
||||||
foreach ($list as $file) {
|
foreach ($list as $file) {
|
||||||
$name = explode('.', $file);
|
$name = explode('.', $file);
|
||||||
if (end($name) == 'pid') {
|
if (end($name) == 'pid') {
|
||||||
@ -46,7 +46,7 @@ class ServerStopCommand extends ServerCommand
|
|||||||
Process::kill(intval($this->daemon_file['pid']), SIGTERM);
|
Process::kill(intval($this->daemon_file['pid']), SIGTERM);
|
||||||
}
|
}
|
||||||
$i = 10;
|
$i = 10;
|
||||||
while (ProcessManager::getProcessState(ZM_PROCESS_MASTER) !== false && $i > 0) {
|
while (ProcessStateManager::getProcessState(ZM_PROCESS_MASTER) !== false && $i > 0) {
|
||||||
sleep(1);
|
sleep(1);
|
||||||
--$i;
|
--$i;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,7 +5,7 @@ declare(strict_types=1);
|
|||||||
namespace ZM\Config;
|
namespace ZM\Config;
|
||||||
|
|
||||||
use ZM\Exception\ConfigException;
|
use ZM\Exception\ConfigException;
|
||||||
use ZM\Utils\DataProvider;
|
use ZM\Store\FileSystem;
|
||||||
|
|
||||||
class ZMConfig
|
class ZMConfig
|
||||||
{
|
{
|
||||||
@ -22,7 +22,7 @@ class ZMConfig
|
|||||||
public static $config = [];
|
public static $config = [];
|
||||||
|
|
||||||
/** @var string 配置文件 */
|
/** @var string 配置文件 */
|
||||||
private static $path = '.';
|
private static $path = 'config';
|
||||||
|
|
||||||
/** @var string 上次的路径 */
|
/** @var string 上次的路径 */
|
||||||
private static $last_path = '.';
|
private static $last_path = '.';
|
||||||
@ -187,7 +187,7 @@ class ZMConfig
|
|||||||
private static function parseList(string $name): void
|
private static function parseList(string $name): void
|
||||||
{
|
{
|
||||||
$list = [];
|
$list = [];
|
||||||
$files = DataProvider::scanDirFiles(self::$path, true, true);
|
$files = FileSystem::scanDirFiles(self::$path, true, true);
|
||||||
foreach ($files as $file) {
|
foreach ($files as $file) {
|
||||||
logger()->debug('正在从目录' . self::$path . '读取配置文件 ' . $file);
|
logger()->debug('正在从目录' . self::$path . '读取配置文件 ' . $file);
|
||||||
$info = pathinfo($file);
|
$info = pathinfo($file);
|
||||||
@ -227,15 +227,15 @@ class ZMConfig
|
|||||||
logger()->warning('文件名 ' . $info['filename'] . ' 不合法(含有"."),请检查文件名是否合法。');
|
logger()->warning('文件名 ' . $info['filename'] . ' 不合法(含有"."),请检查文件名是否合法。');
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$obj->path = realpath(self::$path . '/' . $info['dirname'] . '/' . $info['basename']);
|
$obj->path = zm_dir(self::$path . '/' . $info['dirname'] . '/' . $info['basename']);
|
||||||
$obj->extension = $ext;
|
$obj->extension = $ext;
|
||||||
$obj->data = self::readConfigFromFile(realpath(self::$path . '/' . $info['dirname'] . '/' . $info['basename']), $info['extension']);
|
$obj->data = self::readConfigFromFile(zm_dir(self::$path . '/' . $info['dirname'] . '/' . $info['basename']), $info['extension']);
|
||||||
$list[] = $obj;
|
$list[] = $obj;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 如果是源码模式,config目录和default目录相同,所以不需要继续采摘default目录下的文件
|
// 如果是源码模式,config目录和default目录相同,所以不需要继续采摘default目录下的文件
|
||||||
if (realpath(self::$path) !== realpath(self::DEFAULT_PATH)) {
|
if (realpath(self::$path) !== realpath(self::DEFAULT_PATH)) {
|
||||||
$files = DataProvider::scanDirFiles(self::DEFAULT_PATH, true, true);
|
$files = FileSystem::scanDirFiles(self::DEFAULT_PATH, true, true);
|
||||||
foreach ($files as $file) {
|
foreach ($files as $file) {
|
||||||
$info = pathinfo($file);
|
$info = pathinfo($file);
|
||||||
$info['extension'] = $info['extension'] ?? '';
|
$info['extension'] = $info['extension'] ?? '';
|
||||||
@ -249,7 +249,7 @@ class ZMConfig
|
|||||||
$obj->is_env = false;
|
$obj->is_env = false;
|
||||||
$obj->path = realpath(self::DEFAULT_PATH . '/' . $info['dirname'] . '/' . $info['basename']);
|
$obj->path = realpath(self::DEFAULT_PATH . '/' . $info['dirname'] . '/' . $info['basename']);
|
||||||
$obj->extension = $info['extension'];
|
$obj->extension = $info['extension'];
|
||||||
$obj->data = self::readConfigFromFile(realpath(self::DEFAULT_PATH . '/' . $info['dirname'] . '/' . $info['basename']), $info['extension']);
|
$obj->data = self::readConfigFromFile(zm_dir(self::DEFAULT_PATH . '/' . $info['dirname'] . '/' . $info['basename']), $info['extension']);
|
||||||
$list[] = $obj;
|
$list[] = $obj;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,97 +13,61 @@ use ZM\Command\BuildCommand;
|
|||||||
use ZM\Command\CheckConfigCommand;
|
use ZM\Command\CheckConfigCommand;
|
||||||
use ZM\Command\Generate\SystemdGenerateCommand;
|
use ZM\Command\Generate\SystemdGenerateCommand;
|
||||||
use ZM\Command\InitCommand;
|
use ZM\Command\InitCommand;
|
||||||
use ZM\Command\Module\ModuleListCommand;
|
|
||||||
use ZM\Command\Module\ModulePackCommand;
|
|
||||||
use ZM\Command\Module\ModuleUnpackCommand;
|
|
||||||
use ZM\Command\PureHttpCommand;
|
|
||||||
use ZM\Command\Server\ServerReloadCommand;
|
use ZM\Command\Server\ServerReloadCommand;
|
||||||
use ZM\Command\Server\ServerStartCommand;
|
use ZM\Command\Server\ServerStartCommand;
|
||||||
use ZM\Command\Server\ServerStatusCommand;
|
use ZM\Command\Server\ServerStatusCommand;
|
||||||
use ZM\Command\Server\ServerStopCommand;
|
use ZM\Command\Server\ServerStopCommand;
|
||||||
use ZM\Exception\InitException;
|
use ZM\Exception\InitException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 命令行启动的入口文件,用于初始化环境变量,并启动命令行应用
|
||||||
|
*
|
||||||
|
* 这里启动的不是框架,而是框架相关的命令行环境
|
||||||
|
*/
|
||||||
class ConsoleApplication extends Application
|
class ConsoleApplication extends Application
|
||||||
{
|
{
|
||||||
public const VERSION_ID = 473;
|
|
||||||
|
|
||||||
public const VERSION = '2.8.0';
|
|
||||||
|
|
||||||
private static $obj;
|
private static $obj;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws InitException
|
* @throws InitException
|
||||||
*/
|
*/
|
||||||
public function __construct(string $name = 'UNKNOWN')
|
public function __construct(string $name = 'zhamao-framework')
|
||||||
{
|
{
|
||||||
if (self::$obj !== null) {
|
if (self::$obj !== null) {
|
||||||
throw new InitException(zm_internal_errcode('E00069') . 'Initializing another Application is not allowed!');
|
throw new InitException(zm_internal_errcode('E00069') . 'Initializing another Application is not allowed!');
|
||||||
}
|
}
|
||||||
define('ZM_VERSION_ID', self::VERSION_ID);
|
// 如果已经有定义了全局的 WORKING_DIR,那么就报错
|
||||||
define('ZM_VERSION', self::VERSION);
|
// if (defined('WORKING_DIR')) {
|
||||||
self::$obj = $this;
|
// throw new InitException();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// 启动前检查炸毛运行情况
|
||||||
|
// _zm_env_check();
|
||||||
|
|
||||||
|
// 初始化命令
|
||||||
|
$this->add(new ServerStatusCommand()); // server运行状态
|
||||||
|
$this->add(new ServerReloadCommand()); // server重载
|
||||||
|
$this->add(new ServerStopCommand()); // server停止
|
||||||
|
$this->add(new ServerStartCommand()); // 运行主服务的指令控制器
|
||||||
|
$this->add(new SystemdGenerateCommand()); // 生成systemd文件
|
||||||
|
if (LOAD_MODE === 1) { // 如果是 Composer 模式加载的,那么可以输入 check:config 命令,检查配置文件是否需要更新
|
||||||
|
$this->add(new CheckConfigCommand());
|
||||||
|
}
|
||||||
|
if (Phar::running() === '') { // 不是 Phar 模式的话,可以执行打包解包初始化命令
|
||||||
|
$this->add(new BuildCommand()); // 用于将整个应用打包为一个可执行的 phar
|
||||||
|
$this->add(new InitCommand()); // 用于在 Composer 模式启动下,初始化脚手架文件
|
||||||
|
// $this->add(new PluginPackCommand()); // 用于打包一个子模块为 phar 并进行分发
|
||||||
|
// $this->add(new PluginListCommand()); // 用于列出已配置的子模块列表(存在 zm.json 文件的目录)
|
||||||
|
// $this->add(new PluginUnpackCommand()); // 用于将打包好的 phar 模块解包到 src 目录中
|
||||||
|
}
|
||||||
|
|
||||||
|
self::$obj = $this; // 用于标记已经初始化完成
|
||||||
parent::__construct($name, ZM_VERSION);
|
parent::__construct($name, ZM_VERSION);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws InitException
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function initEnv(string $with_default_cmd = ''): ConsoleApplication
|
|
||||||
{
|
|
||||||
if (defined('WORKING_DIR')) {
|
|
||||||
throw new InitException();
|
|
||||||
}
|
|
||||||
|
|
||||||
_zm_env_check();
|
|
||||||
|
|
||||||
// 定义多进程的全局变量
|
|
||||||
define('ZM_PROCESS_MASTER', 1);
|
|
||||||
define('ZM_PROCESS_MANAGER', 2);
|
|
||||||
define('ZM_PROCESS_WORKER', 4);
|
|
||||||
define('ZM_PROCESS_USER', 8);
|
|
||||||
define('ZM_PROCESS_TASKWORKER', 16);
|
|
||||||
|
|
||||||
define('WORKING_DIR', getcwd());
|
|
||||||
|
|
||||||
if (!is_dir(_zm_pid_dir())) {
|
|
||||||
@mkdir(_zm_pid_dir());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Phar::running() !== '') {
|
|
||||||
echo "* Running in phar mode.\n";
|
|
||||||
define('SOURCE_ROOT_DIR', Phar::running());
|
|
||||||
define('LOAD_MODE', is_dir(SOURCE_ROOT_DIR . '/src/ZM') ? 0 : 1);
|
|
||||||
define('FRAMEWORK_ROOT_DIR', LOAD_MODE == 1 ? (SOURCE_ROOT_DIR . '/vendor/zhamao/framework') : SOURCE_ROOT_DIR);
|
|
||||||
} else {
|
|
||||||
define('SOURCE_ROOT_DIR', WORKING_DIR);
|
|
||||||
define('LOAD_MODE', is_dir(SOURCE_ROOT_DIR . '/src/ZM') ? 0 : 1);
|
|
||||||
define('FRAMEWORK_ROOT_DIR', realpath(__DIR__ . '/../../'));
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->addCommands([
|
|
||||||
new ServerStatusCommand(),
|
|
||||||
new ServerReloadCommand(),
|
|
||||||
new ServerStopCommand(),
|
|
||||||
new ServerStartCommand(), // 运行主服务的指令控制器
|
|
||||||
new PureHttpCommand(), // 纯HTTP服务器指令
|
|
||||||
new SystemdGenerateCommand(),
|
|
||||||
]);
|
|
||||||
if (LOAD_MODE === 1) {
|
|
||||||
$this->add(new CheckConfigCommand());
|
|
||||||
}
|
|
||||||
if (Phar::running() === '') {
|
|
||||||
$this->add(new BuildCommand());
|
|
||||||
$this->add(new InitCommand());
|
|
||||||
$this->add(new ModulePackCommand());
|
|
||||||
$this->add(new ModuleListCommand());
|
|
||||||
$this->add(new ModuleUnpackCommand());
|
|
||||||
}
|
|
||||||
if (!empty($with_default_cmd)) {
|
|
||||||
$this->setDefaultCommand($with_default_cmd);
|
|
||||||
}
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function run(InputInterface $input = null, OutputInterface $output = null): int
|
public function run(InputInterface $input = null, OutputInterface $output = null): int
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -1,104 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\Container;
|
|
||||||
|
|
||||||
use InvalidArgumentException;
|
|
||||||
use ReflectionException;
|
|
||||||
use ReflectionParameter;
|
|
||||||
use ZM\Utils\ReflectionUtil;
|
|
||||||
|
|
||||||
class BoundMethod
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* 调用指定闭包、类方法并注入依赖
|
|
||||||
*
|
|
||||||
* @param Container $container
|
|
||||||
* @param callable|string $callback
|
|
||||||
* @throws InvalidArgumentException
|
|
||||||
* @throws ReflectionException
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public static function call(ContainerInterface $container, $callback, array $parameters = [], string $default_method = null)
|
|
||||||
{
|
|
||||||
if (is_string($callback) && !$default_method && method_exists($callback, '__invoke')) {
|
|
||||||
$default_method = '__invoke';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_string($callback) && $default_method) {
|
|
||||||
$callback = [$callback, $default_method];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ReflectionUtil::isNonStaticMethod($callback)) {
|
|
||||||
$callback[0] = $container->make($callback[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!is_callable($callback)) {
|
|
||||||
throw new InvalidArgumentException('Callback is not callable.');
|
|
||||||
}
|
|
||||||
|
|
||||||
return call_user_func_array($callback, self::getMethodDependencies($container, $callback, $parameters));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get all dependencies for a given method.
|
|
||||||
*
|
|
||||||
* @param callable|string $callback
|
|
||||||
* @throws ReflectionException
|
|
||||||
*/
|
|
||||||
protected static function getMethodDependencies(ContainerInterface $container, $callback, array $parameters = []): array
|
|
||||||
{
|
|
||||||
$dependencies = [];
|
|
||||||
|
|
||||||
foreach (ReflectionUtil::getCallReflector($callback)->getParameters() as $i => $parameter) {
|
|
||||||
if (isset($parameters[$i]) && $parameter->hasType() && ($type = $parameter->getType())) {
|
|
||||||
if ($type instanceof \ReflectionNamedType && gettype($parameters[$i]) === $type->getName()) {
|
|
||||||
$dependencies[] = $parameters[$i];
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
static::addDependencyForCallParameter($container, $parameter, $parameters, $dependencies);
|
|
||||||
}
|
|
||||||
|
|
||||||
return array_merge($dependencies, array_values($parameters));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the dependency for the given call parameter.
|
|
||||||
*
|
|
||||||
* @throws EntryResolutionException
|
|
||||||
*/
|
|
||||||
protected static function addDependencyForCallParameter(
|
|
||||||
ContainerInterface $container,
|
|
||||||
ReflectionParameter $parameter,
|
|
||||||
array &$parameters,
|
|
||||||
array &$dependencies
|
|
||||||
): void {
|
|
||||||
if (array_key_exists($param_name = $parameter->getName(), $parameters)) {
|
|
||||||
$dependencies[] = $parameters[$param_name];
|
|
||||||
|
|
||||||
unset($parameters[$param_name]);
|
|
||||||
} elseif (!is_null($class_name = ReflectionUtil::getParameterClassName($parameter))) {
|
|
||||||
if (array_key_exists($class_name, $parameters)) {
|
|
||||||
$dependencies[] = $parameters[$class_name];
|
|
||||||
|
|
||||||
unset($parameters[$class_name]);
|
|
||||||
} elseif ($parameter->isVariadic()) {
|
|
||||||
$variadic_dependencies = $container->make($class_name);
|
|
||||||
|
|
||||||
$dependencies = array_merge($dependencies, is_array($variadic_dependencies)
|
|
||||||
? $variadic_dependencies
|
|
||||||
: [$variadic_dependencies]);
|
|
||||||
} else {
|
|
||||||
$dependencies[] = $container->make($class_name);
|
|
||||||
}
|
|
||||||
} elseif ($parameter->isDefaultValueAvailable()) {
|
|
||||||
$dependencies[] = $parameter->getDefaultValue();
|
|
||||||
} elseif (!array_key_exists($param_name, $parameters) && !$parameter->isOptional()) {
|
|
||||||
$message = "无法解析类 {$parameter->getDeclaringClass()->getName()} 的依赖 {$parameter}";
|
|
||||||
|
|
||||||
throw new EntryResolutionException($message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,61 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\Container;
|
|
||||||
|
|
||||||
use ZM\Utils\SingletonTrait;
|
|
||||||
|
|
||||||
class Container implements ContainerInterface
|
|
||||||
{
|
|
||||||
use SingletonTrait;
|
|
||||||
use ContainerTrait {
|
|
||||||
make as protected traitMake;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取父容器
|
|
||||||
*/
|
|
||||||
public function getParent(): ContainerInterface
|
|
||||||
{
|
|
||||||
return WorkerContainer::getInstance();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the container can return an entry for the given identifier.
|
|
||||||
* Returns false otherwise.
|
|
||||||
*
|
|
||||||
* `has($id)` returning true does not mean that `get($id)` will not throw an exception.
|
|
||||||
* It does however mean that `get($id)` will not throw a `NotFoundExceptionInterface`.
|
|
||||||
*
|
|
||||||
* @param string $id identifier of the entry to look for
|
|
||||||
*/
|
|
||||||
public function has(string $id): bool
|
|
||||||
{
|
|
||||||
return $this->bound($id) || $this->getParent()->has($id);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取一个绑定的实例
|
|
||||||
*
|
|
||||||
* @template T
|
|
||||||
* @param class-string<T> $abstract 类或接口名
|
|
||||||
* @param array $parameters 参数
|
|
||||||
* @throws EntryResolutionException
|
|
||||||
* @return Closure|mixed|T 实例
|
|
||||||
*/
|
|
||||||
public function make(string $abstract, array $parameters = [])
|
|
||||||
{
|
|
||||||
if (isset($this->shared[$abstract])) {
|
|
||||||
return $this->shared[$abstract];
|
|
||||||
}
|
|
||||||
|
|
||||||
// 此类没有,父类有,则从父类中获取
|
|
||||||
if (!$this->bound($abstract) && $this->getParent()->bound($abstract)) {
|
|
||||||
$this->log("{$abstract} is not bound, but in parent container, using parent container");
|
|
||||||
return $this->getParent()->make($abstract, $parameters);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->traitMake($abstract, $parameters);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,110 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\Container;
|
|
||||||
|
|
||||||
use Closure;
|
|
||||||
use Psr\Container\ContainerInterface as PsrContainerInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Interface ContainerInterface
|
|
||||||
*
|
|
||||||
* 从 Illuminate WorkerContainer 简化而来,兼容 PSR-11
|
|
||||||
*/
|
|
||||||
interface ContainerInterface extends PsrContainerInterface
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* 判断对应的类或接口是否已经注册
|
|
||||||
*
|
|
||||||
* @param string $abstract 类或接口名
|
|
||||||
*/
|
|
||||||
public function bound(string $abstract): bool;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 注册一个类别名
|
|
||||||
*
|
|
||||||
* @param string $abstract 类或接口名
|
|
||||||
* @param string $alias 别名
|
|
||||||
*/
|
|
||||||
public function alias(string $abstract, string $alias): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 注册绑定
|
|
||||||
*
|
|
||||||
* @param string $abstract 类或接口名
|
|
||||||
* @param null|Closure|string $concrete 返回类实例的闭包,或是类名
|
|
||||||
* @param bool $shared 是否共享
|
|
||||||
*/
|
|
||||||
public function bind(string $abstract, $concrete = null, bool $shared = false): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 注册绑定
|
|
||||||
*
|
|
||||||
* 在已经绑定时不会重复注册
|
|
||||||
*
|
|
||||||
* @param string $abstract 类或接口名
|
|
||||||
* @param null|Closure|string $concrete 返回类实例的闭包,或是类名
|
|
||||||
* @param bool $shared 是否共享
|
|
||||||
*/
|
|
||||||
public function bindIf(string $abstract, $concrete = null, bool $shared = false): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 注册一个单例绑定
|
|
||||||
*
|
|
||||||
* @param string $abstract 类或接口名
|
|
||||||
* @param null|Closure|string $concrete 返回类实例的闭包,或是类名
|
|
||||||
*/
|
|
||||||
public function singleton(string $abstract, $concrete = null): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 注册一个单例绑定
|
|
||||||
*
|
|
||||||
* 在已经绑定时不会重复注册
|
|
||||||
*
|
|
||||||
* @param string $abstract 类或接口名
|
|
||||||
* @param null|Closure|string $concrete 返回类实例的闭包,或是类名
|
|
||||||
*/
|
|
||||||
public function singletonIf(string $abstract, $concrete = null): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 注册一个已有的实例,效果等同于单例绑定
|
|
||||||
*
|
|
||||||
* @param string $abstract 类或接口名
|
|
||||||
* @param mixed $instance 实例
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function instance(string $abstract, $instance);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取一个解析对应类实例的闭包
|
|
||||||
*
|
|
||||||
* @param string $abstract 类或接口名
|
|
||||||
*/
|
|
||||||
public function factory(string $abstract): Closure;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 清除所有绑定和实例
|
|
||||||
*/
|
|
||||||
public function flush(): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取一个绑定的实例
|
|
||||||
*
|
|
||||||
* @template T
|
|
||||||
* @param class-string<T> $abstract 类或接口名
|
|
||||||
* @param array $parameters 参数
|
|
||||||
* @return Closure|mixed|T 实例
|
|
||||||
*/
|
|
||||||
public function make(string $abstract, array $parameters = []);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 调用对应的方法,并自动注入依赖
|
|
||||||
*
|
|
||||||
* @param callable $callback 对应的方法
|
|
||||||
* @param array $parameters 参数
|
|
||||||
* @param null|string $default_method 默认方法
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function call(callable $callback, array $parameters = [], string $default_method = null);
|
|
||||||
}
|
|
||||||
@ -1,116 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\Container;
|
|
||||||
|
|
||||||
use Closure;
|
|
||||||
use Psr\Log\LoggerInterface;
|
|
||||||
use Swoole\Http\Request;
|
|
||||||
use Swoole\Http\Response;
|
|
||||||
use Swoole\WebSocket\Frame;
|
|
||||||
use ZM\Adapters\AdapterInterface;
|
|
||||||
use ZM\Adapters\OneBot11Adapter;
|
|
||||||
use ZM\Config\ZMConfig;
|
|
||||||
use ZM\ConnectionManager\ConnectionObject;
|
|
||||||
use ZM\Context\Context;
|
|
||||||
use ZM\Context\ContextInterface;
|
|
||||||
use ZM\Framework;
|
|
||||||
use ZM\Utils\DataProvider;
|
|
||||||
|
|
||||||
class ContainerServicesProvider
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* 注册服务
|
|
||||||
*
|
|
||||||
* ```
|
|
||||||
* 作用域:
|
|
||||||
* global: worker start
|
|
||||||
* request: request
|
|
||||||
* message: message
|
|
||||||
* connection: open, close, message
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* @param string $scope 作用域
|
|
||||||
*/
|
|
||||||
public function registerServices(string $scope): void
|
|
||||||
{
|
|
||||||
switch ($scope) {
|
|
||||||
case 'global':
|
|
||||||
$this->registerGlobalServices(WorkerContainer::getInstance());
|
|
||||||
break;
|
|
||||||
case 'request':
|
|
||||||
$this->registerRequestServices(Container::getInstance());
|
|
||||||
break;
|
|
||||||
case 'message':
|
|
||||||
$this->registerConnectionServices(Container::getInstance());
|
|
||||||
$this->registerMessageServices(Container::getInstance());
|
|
||||||
break;
|
|
||||||
case 'connection':
|
|
||||||
$this->registerConnectionServices(Container::getInstance());
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 清理服务
|
|
||||||
*/
|
|
||||||
public function cleanup(): void
|
|
||||||
{
|
|
||||||
container()->flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 注册全局服务
|
|
||||||
*/
|
|
||||||
private function registerGlobalServices(ContainerInterface $container): void
|
|
||||||
{
|
|
||||||
$container->instance('path.working', DataProvider::getWorkingDir());
|
|
||||||
$container->instance('path.source', DataProvider::getSourceRootDir());
|
|
||||||
$container->alias('path.source', 'path.base');
|
|
||||||
$container->instance('path.config', DataProvider::getSourceRootDir() . '/config');
|
|
||||||
$container->instance('path.module_config', ZMConfig::get('global', 'config_dir'));
|
|
||||||
$container->instance('path.data', DataProvider::getDataFolder());
|
|
||||||
$container->instance('path.framework', DataProvider::getFrameworkRootDir());
|
|
||||||
|
|
||||||
$container->instance('server', Framework::$server);
|
|
||||||
$container->instance('worker_id', Framework::$server->worker_id);
|
|
||||||
|
|
||||||
$container->singleton(AdapterInterface::class, OneBot11Adapter::class);
|
|
||||||
$container->instance(LoggerInterface::class, ZMConfig::get('logging.logger')());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 注册请求服务(HTTP请求)
|
|
||||||
*/
|
|
||||||
private function registerRequestServices(ContainerInterface $container): void
|
|
||||||
{
|
|
||||||
$context = Context::$context[zm_cid()];
|
|
||||||
$container->instance(Request::class, $context['request']);
|
|
||||||
$container->instance(Response::class, $context['response']);
|
|
||||||
$container->bind(ContextInterface::class, Closure::fromCallable('ctx'));
|
|
||||||
$container->alias(ContextInterface::class, Context::class);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 注册消息服务(WS消息)
|
|
||||||
*/
|
|
||||||
private function registerMessageServices(ContainerInterface $container): void
|
|
||||||
{
|
|
||||||
$context = Context::$context[zm_cid()];
|
|
||||||
$container->instance(Frame::class, $context['frame']); // WS 消息帧
|
|
||||||
$container->bind(ContextInterface::class, Closure::fromCallable('ctx'));
|
|
||||||
$container->alias(ContextInterface::class, Context::class);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 注册链接服务
|
|
||||||
*/
|
|
||||||
private function registerConnectionServices(ContainerInterface $container): void
|
|
||||||
{
|
|
||||||
$context = Context::$context[zm_cid()];
|
|
||||||
$container->instance(ConnectionObject::class, $context['connection']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,736 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\Container;
|
|
||||||
|
|
||||||
use Closure;
|
|
||||||
use Exception;
|
|
||||||
use InvalidArgumentException;
|
|
||||||
use Psr\Container\ContainerExceptionInterface;
|
|
||||||
use Psr\Container\NotFoundExceptionInterface;
|
|
||||||
use ReflectionClass;
|
|
||||||
use ReflectionException;
|
|
||||||
use ReflectionNamedType;
|
|
||||||
use ReflectionParameter;
|
|
||||||
use ZM\Console\Console;
|
|
||||||
use ZM\Utils\ReflectionUtil;
|
|
||||||
|
|
||||||
trait ContainerTrait
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $shared = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array[]
|
|
||||||
*/
|
|
||||||
protected $build_stack = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array[]
|
|
||||||
*/
|
|
||||||
protected $with = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 日志前缀
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $log_prefix;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array[]
|
|
||||||
*/
|
|
||||||
private static $bindings = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var object[]
|
|
||||||
*/
|
|
||||||
private static $instances = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var string[]
|
|
||||||
*/
|
|
||||||
private static $aliases = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var Closure[][]
|
|
||||||
*/
|
|
||||||
private static $extenders = [];
|
|
||||||
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
if ($this->shouldLog()) {
|
|
||||||
$this->log('Container created');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 判断对应的类或接口是否已经注册
|
|
||||||
*
|
|
||||||
* @param string $abstract 类或接口名
|
|
||||||
*/
|
|
||||||
public function bound(string $abstract): bool
|
|
||||||
{
|
|
||||||
return array_key_exists($abstract, self::$bindings)
|
|
||||||
|| array_key_exists($abstract, self::$instances)
|
|
||||||
|| array_key_exists($abstract, $this->shared)
|
|
||||||
|| $this->isAlias($abstract);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取类别名(如存在)
|
|
||||||
*
|
|
||||||
* @param string $abstract 类或接口名
|
|
||||||
* @return string 别名,不存在时返回传入的类或接口名
|
|
||||||
*/
|
|
||||||
public function getAlias(string $abstract): string
|
|
||||||
{
|
|
||||||
if (!isset(self::$aliases[$abstract])) {
|
|
||||||
return $abstract;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->getAlias(self::$aliases[$abstract]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 注册一个类别名
|
|
||||||
*
|
|
||||||
* @param string $abstract 类或接口名
|
|
||||||
* @param string $alias 别名
|
|
||||||
*/
|
|
||||||
public function alias(string $abstract, string $alias): void
|
|
||||||
{
|
|
||||||
if ($alias === $abstract) {
|
|
||||||
throw new InvalidArgumentException("[{$abstract}] is same as [{$alias}]");
|
|
||||||
}
|
|
||||||
|
|
||||||
self::$aliases[$alias] = $abstract;
|
|
||||||
|
|
||||||
if ($this->shouldLog()) {
|
|
||||||
$this->log("[{$abstract}] is aliased as [{$alias}]");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 注册绑定
|
|
||||||
*
|
|
||||||
* @param string $abstract 类或接口名
|
|
||||||
* @param null|Closure|string $concrete 返回类实例的闭包,或是类名
|
|
||||||
* @param bool $shared 是否共享
|
|
||||||
*/
|
|
||||||
public function bind(string $abstract, $concrete = null, bool $shared = false): void
|
|
||||||
{
|
|
||||||
$this->dropStaleInstances($abstract);
|
|
||||||
|
|
||||||
// 如果没有提供闭包,则默认为自动解析类名
|
|
||||||
if (is_null($concrete)) {
|
|
||||||
$concrete = $abstract;
|
|
||||||
}
|
|
||||||
|
|
||||||
$concrete_name = '';
|
|
||||||
if ($this->shouldLog()) {
|
|
||||||
$concrete_name = ReflectionUtil::variableToString($concrete);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果不是闭包,则认为是类名,此时将其包装在一个闭包中,以方便后续处理
|
|
||||||
if (!$concrete instanceof Closure) {
|
|
||||||
$concrete = $this->getClosure($abstract, $concrete);
|
|
||||||
}
|
|
||||||
|
|
||||||
self::$bindings[$abstract] = compact('concrete', 'shared');
|
|
||||||
|
|
||||||
if ($this->shouldLog()) {
|
|
||||||
$this->log("[{$abstract}] is bound to [{$concrete_name}]" . ($shared ? ' (shared)' : ''));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 注册绑定
|
|
||||||
*
|
|
||||||
* 在已经绑定时不会重复注册
|
|
||||||
*
|
|
||||||
* @param string $abstract 类或接口名
|
|
||||||
* @param null|Closure|string $concrete 返回类实例的闭包,或是类名
|
|
||||||
* @param bool $shared 是否共享
|
|
||||||
*/
|
|
||||||
public function bindIf(string $abstract, $concrete = null, bool $shared = false): void
|
|
||||||
{
|
|
||||||
if (!$this->bound($abstract)) {
|
|
||||||
$this->bind($abstract, $concrete, $shared);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 注册一个单例绑定
|
|
||||||
*
|
|
||||||
* @param string $abstract 类或接口名
|
|
||||||
* @param null|Closure|string $concrete 返回类实例的闭包,或是类名
|
|
||||||
*/
|
|
||||||
public function singleton(string $abstract, $concrete = null): void
|
|
||||||
{
|
|
||||||
$this->bind($abstract, $concrete, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 注册一个单例绑定
|
|
||||||
*
|
|
||||||
* 在已经绑定时不会重复注册
|
|
||||||
*
|
|
||||||
* @param string $abstract 类或接口名
|
|
||||||
* @param null|Closure|string $concrete 返回类实例的闭包,或是类名
|
|
||||||
*/
|
|
||||||
public function singletonIf(string $abstract, $concrete = null): void
|
|
||||||
{
|
|
||||||
if (!$this->bound($abstract)) {
|
|
||||||
$this->singleton($abstract, $concrete);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 注册一个已有的实例,效果等同于单例绑定
|
|
||||||
*
|
|
||||||
* @param string $abstract 类或接口名
|
|
||||||
* @param mixed $instance 实例
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function instance(string $abstract, $instance)
|
|
||||||
{
|
|
||||||
if (isset(self::$instances[$abstract])) {
|
|
||||||
return self::$instances[$abstract];
|
|
||||||
}
|
|
||||||
|
|
||||||
self::$instances[$abstract] = $instance;
|
|
||||||
|
|
||||||
if ($this->shouldLog()) {
|
|
||||||
$class_name = ReflectionUtil::variableToString($instance);
|
|
||||||
$this->log("[{$abstract}] is bound to [{$class_name}] (instance)");
|
|
||||||
}
|
|
||||||
|
|
||||||
return $instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取一个解析对应类实例的闭包
|
|
||||||
*
|
|
||||||
* @param string $abstract 类或接口名
|
|
||||||
*/
|
|
||||||
public function factory(string $abstract): Closure
|
|
||||||
{
|
|
||||||
return function () use ($abstract) {
|
|
||||||
return $this->make($abstract);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 清除所有绑定和实例
|
|
||||||
*/
|
|
||||||
public function flush(): void
|
|
||||||
{
|
|
||||||
self::$aliases = [];
|
|
||||||
self::$bindings = [];
|
|
||||||
self::$instances = [];
|
|
||||||
|
|
||||||
$this->shared = [];
|
|
||||||
$this->build_stack = [];
|
|
||||||
$this->with = [];
|
|
||||||
|
|
||||||
if ($this->shouldLog()) {
|
|
||||||
$this->log('Container flushed');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取一个绑定的实例
|
|
||||||
*
|
|
||||||
* @template T
|
|
||||||
* @param class-string<T> $abstract 类或接口名
|
|
||||||
* @param array $parameters 参数
|
|
||||||
* @throws EntryResolutionException
|
|
||||||
* @return Closure|mixed|T 实例
|
|
||||||
*/
|
|
||||||
public function make(string $abstract, array $parameters = [])
|
|
||||||
{
|
|
||||||
$abstract = $this->getAlias($abstract);
|
|
||||||
|
|
||||||
$needs_contextual_build = !empty($parameters);
|
|
||||||
|
|
||||||
if (isset($this->shared[$abstract])) {
|
|
||||||
if ($this->shouldLog()) {
|
|
||||||
$this->log(sprintf(
|
|
||||||
'[%s] resolved (shared)%s',
|
|
||||||
$abstract,
|
|
||||||
($needs_contextual_build ? ' with ' . implode(', ', $parameters) : '')
|
|
||||||
));
|
|
||||||
}
|
|
||||||
return $this->shared[$abstract];
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果已经存在在实例池中(通常意味着单例绑定),则直接返回该实例
|
|
||||||
if (isset(self::$instances[$abstract]) && !$needs_contextual_build) {
|
|
||||||
if ($this->shouldLog()) {
|
|
||||||
$this->log("[{$abstract}] resolved (instance)");
|
|
||||||
}
|
|
||||||
return self::$instances[$abstract];
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->with[] = $parameters;
|
|
||||||
|
|
||||||
$concrete = $this->getConcrete($abstract);
|
|
||||||
|
|
||||||
// 构造该类的实例,并递归解析所有依赖
|
|
||||||
if ($this->isBuildable($concrete, $abstract)) {
|
|
||||||
$object = $this->build($concrete);
|
|
||||||
} else {
|
|
||||||
$object = $this->make($concrete);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果该类存在扩展器(装饰器),则逐个应用到实例
|
|
||||||
foreach ($this->getExtenders($abstract) as $extender) {
|
|
||||||
$object = $extender($object, $this);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果该类被注册为单例,则需要将其存放在实例池中,方便后续取用同一实例
|
|
||||||
if (!$needs_contextual_build && $this->isShared($abstract)) {
|
|
||||||
$this->shared[$abstract] = $object;
|
|
||||||
if ($this->shouldLog()) {
|
|
||||||
$this->log("[{$abstract}] added to shared pool");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 弹出本次构造的覆盖参数
|
|
||||||
array_pop($this->with);
|
|
||||||
|
|
||||||
if ($this->shouldLog()) {
|
|
||||||
$this->log(sprintf(
|
|
||||||
'[%s] resolved%s',
|
|
||||||
$abstract,
|
|
||||||
($needs_contextual_build ? ' with ' . implode(', ', $parameters) : '')
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
return $object;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 实例化具体的类实例
|
|
||||||
*
|
|
||||||
* @param Closure|string $concrete 类名或对应的闭包
|
|
||||||
* @throws EntryResolutionException
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function build($concrete)
|
|
||||||
{
|
|
||||||
// 如果传入的是闭包,则直接执行并返回
|
|
||||||
if ($concrete instanceof Closure) {
|
|
||||||
return $concrete($this, $this->getLastParameterOverride());
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
$reflection = new ReflectionClass($concrete);
|
|
||||||
} catch (ReflectionException $e) {
|
|
||||||
throw new EntryResolutionException("指定的类 {$concrete} 不存在", 0, $e);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$reflection->isInstantiable()) {
|
|
||||||
$this->notInstantiable($concrete);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->build_stack[] = $concrete;
|
|
||||||
|
|
||||||
$constructor = $reflection->getConstructor();
|
|
||||||
|
|
||||||
// 如果不存在构造函数,则代表不需要进一步解析,直接实例化即可
|
|
||||||
if (is_null($constructor)) {
|
|
||||||
array_pop($this->build_stack);
|
|
||||||
return new $concrete();
|
|
||||||
}
|
|
||||||
|
|
||||||
$dependencies = $constructor->getParameters();
|
|
||||||
|
|
||||||
// 获取所有依赖的实例
|
|
||||||
try {
|
|
||||||
$instances = $this->resolveDependencies($dependencies);
|
|
||||||
} catch (EntryResolutionException $e) {
|
|
||||||
array_pop($this->build_stack);
|
|
||||||
throw $e;
|
|
||||||
}
|
|
||||||
|
|
||||||
array_pop($this->build_stack);
|
|
||||||
|
|
||||||
return $reflection->newInstanceArgs($instances);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 调用对应的方法,并自动注入依赖
|
|
||||||
*
|
|
||||||
* @param callable|string $callback 对应的方法
|
|
||||||
* @param array $parameters 参数
|
|
||||||
* @param null|string $default_method 默认方法
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function call($callback, array $parameters = [], string $default_method = null)
|
|
||||||
{
|
|
||||||
if ($this->shouldLog()) {
|
|
||||||
if (count($parameters)) {
|
|
||||||
$str_parameters = array_map([ReflectionUtil::class, 'variableToString'], $parameters);
|
|
||||||
$str_parameters = implode(', ', $str_parameters);
|
|
||||||
} else {
|
|
||||||
$str_parameters = '';
|
|
||||||
}
|
|
||||||
$this->log(sprintf(
|
|
||||||
'Called %s%s(%s)',
|
|
||||||
ReflectionUtil::variableToString($callback),
|
|
||||||
($default_method ? '@' . $default_method : ''),
|
|
||||||
$str_parameters
|
|
||||||
));
|
|
||||||
}
|
|
||||||
return BoundMethod::call($this, $callback, $parameters, $default_method);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Finds an entry of the container by its identifier and returns it.
|
|
||||||
*
|
|
||||||
* @param string $id identifier of the entry to look for
|
|
||||||
*
|
|
||||||
* @throws NotFoundExceptionInterface no entry was found for **this** identifier
|
|
||||||
* @throws ContainerExceptionInterface error while retrieving the entry
|
|
||||||
*
|
|
||||||
* @return mixed entry
|
|
||||||
*/
|
|
||||||
public function get(string $id)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
return $this->make($id);
|
|
||||||
} catch (Exception $e) {
|
|
||||||
if ($this->has($id)) {
|
|
||||||
throw new EntryResolutionException('', 0, $e);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new EntryNotFoundException('', 0, $e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the container can return an entry for the given identifier.
|
|
||||||
* Returns false otherwise.
|
|
||||||
*
|
|
||||||
* `has($id)` returning true does not mean that `get($id)` will not throw an exception.
|
|
||||||
* It does however mean that `get($id)` will not throw a `NotFoundExceptionInterface`.
|
|
||||||
*
|
|
||||||
* @param string $id identifier of the entry to look for
|
|
||||||
*/
|
|
||||||
public function has(string $id): bool
|
|
||||||
{
|
|
||||||
return $this->bound($id);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 扩展一个类或接口
|
|
||||||
*
|
|
||||||
* @param string $abstract 类或接口名
|
|
||||||
* @param Closure $closure 扩展闭包
|
|
||||||
*/
|
|
||||||
public function extend(string $abstract, Closure $closure): void
|
|
||||||
{
|
|
||||||
$abstract = $this->getAlias($abstract);
|
|
||||||
|
|
||||||
// 如果该类已经被解析过,则直接将扩展器应用到该类的实例上
|
|
||||||
// 否则,将扩展器存入扩展器池,等待解析
|
|
||||||
if (isset(self::$instances[$abstract])) {
|
|
||||||
self::$instances[$abstract] = $closure(self::$instances[$abstract], $this);
|
|
||||||
} else {
|
|
||||||
self::$extenders[$abstract][] = $closure;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->shouldLog()) {
|
|
||||||
$this->log("[{$abstract}] extended");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取日志前缀
|
|
||||||
*/
|
|
||||||
public function getLogPrefix(): string
|
|
||||||
{
|
|
||||||
return ($this->log_prefix ?: '[WorkerContainer(U)]') . ' ';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置日志前缀
|
|
||||||
*/
|
|
||||||
public function setLogPrefix(string $prefix): void
|
|
||||||
{
|
|
||||||
$this->log_prefix = $prefix;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取对应类型的所有扩展器
|
|
||||||
*
|
|
||||||
* @param string $abstract 类或接口名
|
|
||||||
* @return Closure[]
|
|
||||||
*/
|
|
||||||
protected function getExtenders(string $abstract): array
|
|
||||||
{
|
|
||||||
$abstract = $this->getAlias($abstract);
|
|
||||||
|
|
||||||
return self::$extenders[$abstract] ?? [];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 判断传入的是否为别名
|
|
||||||
*/
|
|
||||||
protected function isAlias(string $name): bool
|
|
||||||
{
|
|
||||||
return array_key_exists($name, self::$aliases);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 抛弃所有过时的实例和别名
|
|
||||||
*
|
|
||||||
* @param string $abstract 类或接口名
|
|
||||||
*/
|
|
||||||
protected function dropStaleInstances(string $abstract): void
|
|
||||||
{
|
|
||||||
unset(
|
|
||||||
self::$instances[$abstract],
|
|
||||||
self::$aliases[$abstract],
|
|
||||||
$this->shared[$abstract]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取一个解析对应类的闭包
|
|
||||||
*
|
|
||||||
* @param string $abstract 类或接口名
|
|
||||||
* @param string $concrete 实际类名
|
|
||||||
*/
|
|
||||||
protected function getClosure(string $abstract, string $concrete): Closure
|
|
||||||
{
|
|
||||||
return static function ($container, $parameters = []) use ($abstract, $concrete) {
|
|
||||||
$method = $abstract === $concrete ? 'build' : 'make';
|
|
||||||
|
|
||||||
return $container->{$method}($concrete, $parameters);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取最后一次的覆盖参数
|
|
||||||
*/
|
|
||||||
protected function getLastParameterOverride(): array
|
|
||||||
{
|
|
||||||
return $this->with[count($this->with) - 1] ?? [];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 抛出实例化异常
|
|
||||||
*
|
|
||||||
* @throws EntryResolutionException
|
|
||||||
*/
|
|
||||||
protected function notInstantiable(string $concrete, string $reason = ''): void
|
|
||||||
{
|
|
||||||
if (!empty($this->build_stack)) {
|
|
||||||
$previous = implode(', ', $this->build_stack);
|
|
||||||
$message = "类 {$concrete} 无法实例化,其被 {$previous} 依赖";
|
|
||||||
} else {
|
|
||||||
$message = "类 {$concrete} 无法实例化";
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new EntryResolutionException("{$message}:{$reason}");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 解析依赖
|
|
||||||
*
|
|
||||||
* @param ReflectionParameter[] $dependencies
|
|
||||||
* @throws EntryResolutionException
|
|
||||||
*/
|
|
||||||
protected function resolveDependencies(array $dependencies): array
|
|
||||||
{
|
|
||||||
$results = [];
|
|
||||||
|
|
||||||
foreach ($dependencies as $dependency) {
|
|
||||||
// 如果此依赖存在覆盖参数,则使用覆盖参数
|
|
||||||
// 否则,将尝试解析参数
|
|
||||||
if ($this->hasParameterOverride($dependency)) {
|
|
||||||
$results[] = $this->getParameterOverride($dependency);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果存在临时注入的依赖,则使用临时注入的依赖
|
|
||||||
if ($this->hasParameterTypeOverride($dependency)) {
|
|
||||||
$results[] = $this->getParameterTypeOverride($dependency);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果类名为空,则代表此依赖是基本类型,且无法对其进行依赖解析
|
|
||||||
$class_name = ReflectionUtil::getParameterClassName($dependency);
|
|
||||||
$results[] = is_null($class_name)
|
|
||||||
? $this->resolvePrimitive($dependency)
|
|
||||||
: $this->resolveClass($dependency);
|
|
||||||
|
|
||||||
if ($this->shouldLog()) {
|
|
||||||
if (is_null($class_name)) {
|
|
||||||
if ($dependency->hasType()) {
|
|
||||||
$class_name = $dependency->getType();
|
|
||||||
} else {
|
|
||||||
$class_name = 'Primitive';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$this->log("Dependency [{$class_name} {$dependency->name}] resolved");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $results;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 判断传入的参数是否存在覆盖参数
|
|
||||||
*/
|
|
||||||
protected function hasParameterOverride(ReflectionParameter $parameter): bool
|
|
||||||
{
|
|
||||||
return array_key_exists($parameter->name, $this->getLastParameterOverride());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取覆盖参数
|
|
||||||
*
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
protected function getParameterOverride(ReflectionParameter $parameter)
|
|
||||||
{
|
|
||||||
return $this->getLastParameterOverride()[$parameter->name];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 判断传入的参数是否存在临时注入的参数
|
|
||||||
*/
|
|
||||||
protected function hasParameterTypeOverride(ReflectionParameter $parameter): bool
|
|
||||||
{
|
|
||||||
if (!$parameter->hasType()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$type = $parameter->getType();
|
|
||||||
|
|
||||||
if (!$type instanceof ReflectionNamedType || $type->isBuiltin()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return array_key_exists($type->getName(), $this->getLastParameterOverride());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取临时注入的参数
|
|
||||||
*
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
protected function getParameterTypeOverride(ReflectionParameter $parameter)
|
|
||||||
{
|
|
||||||
$type = $parameter->getType();
|
|
||||||
|
|
||||||
if (!$type instanceof ReflectionNamedType) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->getLastParameterOverride()[$type->getName()];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 解析基本类型
|
|
||||||
*
|
|
||||||
* @throws EntryResolutionException 如参数不存在默认值,则抛出异常
|
|
||||||
* @return mixed 对应类型的默认值
|
|
||||||
*/
|
|
||||||
protected function resolvePrimitive(ReflectionParameter $parameter)
|
|
||||||
{
|
|
||||||
if ($parameter->isDefaultValueAvailable()) {
|
|
||||||
return $parameter->getDefaultValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new EntryResolutionException("无法解析类 {$parameter->getDeclaringClass()->getName()} 的参数 {$parameter}");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 解析类
|
|
||||||
*
|
|
||||||
* @throws EntryResolutionException 如果无法解析类,则抛出异常
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
protected function resolveClass(ReflectionParameter $parameter)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
// 尝试解析
|
|
||||||
return $this->make(ReflectionUtil::getParameterClassName($parameter));
|
|
||||||
} catch (EntryResolutionException $e) {
|
|
||||||
// 如果参数是可选的,则返回默认值
|
|
||||||
if ($parameter->isDefaultValueAvailable()) {
|
|
||||||
array_pop($this->with);
|
|
||||||
return $parameter->getDefaultValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($parameter->isVariadic()) {
|
|
||||||
array_pop($this->with);
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
throw $e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取类名的实际类型
|
|
||||||
*
|
|
||||||
* @param string $abstract 类或接口名
|
|
||||||
* @return Closure|string
|
|
||||||
*/
|
|
||||||
protected function getConcrete(string $abstract)
|
|
||||||
{
|
|
||||||
if (isset(self::$bindings[$abstract])) {
|
|
||||||
return self::$bindings[$abstract]['concrete'];
|
|
||||||
}
|
|
||||||
|
|
||||||
return $abstract;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 判断传入的实际类型是否可以构造
|
|
||||||
*
|
|
||||||
* @param mixed $concrete 实际类型
|
|
||||||
* @param string $abstract 类或接口名
|
|
||||||
*/
|
|
||||||
protected function isBuildable($concrete, string $abstract): bool
|
|
||||||
{
|
|
||||||
return $concrete === $abstract || $concrete instanceof Closure;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 判断传入的类型是否为共享实例
|
|
||||||
*
|
|
||||||
* @param string $abstract 类或接口名
|
|
||||||
*/
|
|
||||||
protected function isShared(string $abstract): bool
|
|
||||||
{
|
|
||||||
return isset($this->instances[$abstract])
|
|
||||||
|| (isset($this->bindings[$abstract]['shared'])
|
|
||||||
&& $this->bindings[$abstract]['shared'] === true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 判断是否输出日志
|
|
||||||
*/
|
|
||||||
protected function shouldLog(): bool
|
|
||||||
{
|
|
||||||
return Console::getLevel() >= 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 记录日志(自动附加容器日志前缀)
|
|
||||||
*/
|
|
||||||
protected function log(string $message): void
|
|
||||||
{
|
|
||||||
logger()->debug($this->getLogPrefix() . $message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,12 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\Container;
|
|
||||||
|
|
||||||
use Exception;
|
|
||||||
use Psr\Container\NotFoundExceptionInterface;
|
|
||||||
|
|
||||||
class EntryNotFoundException extends Exception implements NotFoundExceptionInterface
|
|
||||||
{
|
|
||||||
}
|
|
||||||
@ -1,12 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\Container;
|
|
||||||
|
|
||||||
use Exception;
|
|
||||||
use Psr\Container\ContainerExceptionInterface;
|
|
||||||
|
|
||||||
class EntryResolutionException extends Exception implements ContainerExceptionInterface
|
|
||||||
{
|
|
||||||
}
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\Container;
|
|
||||||
|
|
||||||
use ZM\Utils\SingletonTrait;
|
|
||||||
|
|
||||||
class WorkerContainer implements ContainerInterface
|
|
||||||
{
|
|
||||||
use SingletonTrait;
|
|
||||||
use ContainerTrait;
|
|
||||||
}
|
|
||||||
@ -1,368 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\Context;
|
|
||||||
|
|
||||||
use Closure;
|
|
||||||
use Exception;
|
|
||||||
use Stringable;
|
|
||||||
use Swoole\Coroutine;
|
|
||||||
use Swoole\Http\Request;
|
|
||||||
use Swoole\WebSocket\Frame;
|
|
||||||
use Swoole\WebSocket\Server;
|
|
||||||
use ZM\API\ZMRobot;
|
|
||||||
use ZM\Config\ZMConfig;
|
|
||||||
use ZM\ConnectionManager\ConnectionObject;
|
|
||||||
use ZM\ConnectionManager\ManagerGM;
|
|
||||||
use ZM\Event\EventDispatcher;
|
|
||||||
use ZM\Exception\InterruptException;
|
|
||||||
use ZM\Exception\InvalidArgumentException;
|
|
||||||
use ZM\Exception\WaitTimeoutException;
|
|
||||||
use ZM\Exception\ZMKnownException;
|
|
||||||
use ZM\Http\Response;
|
|
||||||
use ZM\Utils\CoMessage;
|
|
||||||
use ZM\Utils\MessageUtil;
|
|
||||||
|
|
||||||
class Context implements ContextInterface
|
|
||||||
{
|
|
||||||
public static $context = [];
|
|
||||||
|
|
||||||
private $cid;
|
|
||||||
|
|
||||||
public function __construct($cid)
|
|
||||||
{
|
|
||||||
$this->cid = $cid;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Server
|
|
||||||
*/
|
|
||||||
public function getServer(): ?Server
|
|
||||||
{
|
|
||||||
return self::$context[$this->cid]['server'] ?? server();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getFrame(): ?Frame
|
|
||||||
{
|
|
||||||
return self::$context[$this->cid]['frame'] ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getFd(): ?int
|
|
||||||
{
|
|
||||||
return self::$context[$this->cid]['fd'] ?? $this->getFrame()->fd ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function getData()
|
|
||||||
{
|
|
||||||
return self::$context[$this->cid]['data'] ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setData($data)
|
|
||||||
{
|
|
||||||
self::$context[$this->cid]['data'] = $data;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getRequest(): ?Request
|
|
||||||
{
|
|
||||||
return self::$context[$this->cid]['request'] ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getResponse(): ?Response
|
|
||||||
{
|
|
||||||
return self::$context[$this->cid]['response'] ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getConnection(): ?ConnectionObject
|
|
||||||
{
|
|
||||||
return ManagerGM::get($this->getFd());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getCid(): ?int
|
|
||||||
{
|
|
||||||
return $this->cid;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getRobot(): ?ZMRobot
|
|
||||||
{
|
|
||||||
$conn = ManagerGM::get($this->getFrame()->fd);
|
|
||||||
return $conn instanceof ConnectionObject ? new ZMRobot($conn) : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getMessage()
|
|
||||||
{
|
|
||||||
if ((ZMConfig::get('global', 'onebot')['message_convert_string'] ?? true) === true && is_array($msg = $this->getOriginMessage())) {
|
|
||||||
return MessageUtil::arrayToStr($msg);
|
|
||||||
}
|
|
||||||
return self::$context[$this->cid]['data']['message'] ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setMessage($msg)
|
|
||||||
{
|
|
||||||
if (is_string($msg) && is_array($this->getOriginMessage())) {
|
|
||||||
$msg = MessageUtil::strToArray($msg);
|
|
||||||
}
|
|
||||||
self::$context[$this->cid]['data']['message'] = $msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getUserId()
|
|
||||||
{
|
|
||||||
return $this->getData()['user_id'] ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setUserId($id)
|
|
||||||
{
|
|
||||||
self::$context[$this->cid]['data']['user_id'] = $id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getGroupId()
|
|
||||||
{
|
|
||||||
return $this->getData()['group_id'] ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setGroupId($id)
|
|
||||||
{
|
|
||||||
self::$context[$this->cid]['data']['group_id'] = $id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getDiscussId()
|
|
||||||
{
|
|
||||||
return $this->getData()['discuss_id'] ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setDiscussId($id)
|
|
||||||
{
|
|
||||||
self::$context[$this->cid]['data']['discuss_id'] = $id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getMessageType(): ?string
|
|
||||||
{
|
|
||||||
return $this->getData()['message_type'] ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setMessageType($type)
|
|
||||||
{
|
|
||||||
self::$context[$this->cid]['data']['message_type'] = $type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getRobotId()
|
|
||||||
{
|
|
||||||
return $this->getData()['self_id'] ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getCache($key)
|
|
||||||
{
|
|
||||||
return self::$context[$this->cid]['cache'][$key] ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setCache($key, $value)
|
|
||||||
{
|
|
||||||
self::$context[$this->cid]['cache'][$key] = $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getCQResponse()
|
|
||||||
{
|
|
||||||
return self::$context[$this->cid]['cq_response'] ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* only can used by cq->message event function
|
|
||||||
* @param array|string $msg 要回复的消息
|
|
||||||
* @param bool|callable|Closure $yield 是否协程挂起(true),是否绑定异步事件(Closure)
|
|
||||||
* @return array|bool 返回API调用结果
|
|
||||||
*/
|
|
||||||
public function reply($msg, $yield = false)
|
|
||||||
{
|
|
||||||
$data = $this->getData();
|
|
||||||
$conn = $this->getConnection();
|
|
||||||
if (!is_array($msg)) {
|
|
||||||
switch ($this->getData()['message_type']) {
|
|
||||||
case 'group':
|
|
||||||
case 'private':
|
|
||||||
case 'discuss':
|
|
||||||
$this->setCache('has_reply', true);
|
|
||||||
$operation['reply'] = $msg;
|
|
||||||
$operation['at_sender'] = false;
|
|
||||||
return (new ZMRobot($conn))->setCallback($yield)->callExtendedAPI('.handle_quick_operation', [
|
|
||||||
'context' => $data,
|
|
||||||
'operation' => $operation,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$operation = $msg;
|
|
||||||
return (new ZMRobot($conn))->setCallback(false)->callExtendedAPI('.handle_quick_operation', [
|
|
||||||
'context' => $data,
|
|
||||||
'operation' => $operation,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array|string $msg 要回复的消息
|
|
||||||
* @param bool $yield 是否协程挂起(true),是否绑定异步事件(Closure)
|
|
||||||
* @throws InterruptException 阻止消息被后续插件处理
|
|
||||||
*/
|
|
||||||
public function finalReply($msg, $yield = false)
|
|
||||||
{
|
|
||||||
self::$context[$this->cid]['cache']['block_continue'] = true;
|
|
||||||
if ($msg != '') {
|
|
||||||
$this->reply($msg, $yield);
|
|
||||||
}
|
|
||||||
EventDispatcher::interrupt();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $prompt
|
|
||||||
* @param int $timeout
|
|
||||||
* @param string $timeout_prompt
|
|
||||||
* @throws WaitTimeoutException
|
|
||||||
* @throws InvalidArgumentException
|
|
||||||
* @return string 返回用户输入的内容
|
|
||||||
*/
|
|
||||||
public function waitMessage($prompt = '', $timeout = 600, $timeout_prompt = '')
|
|
||||||
{
|
|
||||||
if (!isset($this->getData()['user_id'], $this->getData()['message'], $this->getData()['self_id'])) {
|
|
||||||
throw new InvalidArgumentException('协程等待参数缺失');
|
|
||||||
}
|
|
||||||
|
|
||||||
logger()->debug('==== 开始等待输入 ====');
|
|
||||||
if ($prompt != '') {
|
|
||||||
$this->reply($prompt);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
$r = CoMessage::yieldByWS($this->getData(), ['user_id', 'self_id', 'message_type', get_onebot_target_id_name($this->getMessageType())], $timeout);
|
|
||||||
} catch (Exception $e) {
|
|
||||||
$r = false;
|
|
||||||
}
|
|
||||||
if ($r === false) {
|
|
||||||
throw new WaitTimeoutException($this, $timeout_prompt);
|
|
||||||
}
|
|
||||||
if (is_array($r['message']) && (ZMConfig::get('global', 'onebot')['message_convert_string'] ?? true) === true) {
|
|
||||||
return MessageUtil::arrayToStr($r['message']);
|
|
||||||
}
|
|
||||||
return $r['message'];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据选定的模式获取消息参数
|
|
||||||
* @param int|string $mode 获取的模式
|
|
||||||
* @param string|Stringable $prompt_msg 提示语回复
|
|
||||||
* @throws InvalidArgumentException
|
|
||||||
* @throws ZMKnownException
|
|
||||||
* @throws WaitTimeoutException
|
|
||||||
* @return float|int|string
|
|
||||||
*/
|
|
||||||
public function getArgs($mode, $prompt_msg)
|
|
||||||
{
|
|
||||||
$arg = ctx()->getCache('match') ?? [];
|
|
||||||
switch ($mode) {
|
|
||||||
case ZM_MATCH_ALL:
|
|
||||||
$p = $arg;
|
|
||||||
return trim(implode(' ', $p)) == '' ? $this->waitMessage($prompt_msg) : trim(implode(' ', $p));
|
|
||||||
case ZM_MATCH_NUMBER:
|
|
||||||
foreach ($arg as $k => $v) {
|
|
||||||
if (is_numeric($v)) {
|
|
||||||
array_splice($arg, $k, 1);
|
|
||||||
ctx()->setCache('match', $arg);
|
|
||||||
return $v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $this->waitMessage($prompt_msg);
|
|
||||||
case ZM_MATCH_FIRST:
|
|
||||||
if (isset($arg[0])) {
|
|
||||||
$a = $arg[0];
|
|
||||||
array_splice($arg, 0, 1);
|
|
||||||
ctx()->setCache('match', $arg);
|
|
||||||
return $a;
|
|
||||||
}
|
|
||||||
return $this->waitMessage($prompt_msg);
|
|
||||||
}
|
|
||||||
throw new InvalidArgumentException();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取下一个参数
|
|
||||||
* @param string $prompt_msg 提示语回复
|
|
||||||
* @throws InvalidArgumentException
|
|
||||||
* @throws ZMKnownException
|
|
||||||
* @throws WaitTimeoutException
|
|
||||||
* @return int|mixed|string 返回获取的参数
|
|
||||||
*/
|
|
||||||
public function getNextArg($prompt_msg = '')
|
|
||||||
{
|
|
||||||
return $this->getArgs(ZM_MATCH_FIRST, $prompt_msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取接下来所有的消息当成一个完整的参数(包含空格)
|
|
||||||
* @param string $prompt_msg 提示语回复
|
|
||||||
* @throws InvalidArgumentException
|
|
||||||
* @throws ZMKnownException
|
|
||||||
* @throws WaitTimeoutException
|
|
||||||
* @return int|mixed|string 返回获取的参数
|
|
||||||
*/
|
|
||||||
public function getFullArg($prompt_msg = '')
|
|
||||||
{
|
|
||||||
return $this->getArgs(ZM_MATCH_ALL, $prompt_msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取下一个数字类型的参数
|
|
||||||
* @param string $prompt_msg 提示语回复
|
|
||||||
* @throws InvalidArgumentException
|
|
||||||
* @throws ZMKnownException
|
|
||||||
* @throws WaitTimeoutException
|
|
||||||
* @return int|mixed|string 返回获取的参数
|
|
||||||
*/
|
|
||||||
public function getNumArg($prompt_msg = '')
|
|
||||||
{
|
|
||||||
return $this->getArgs(ZM_MATCH_NUMBER, $prompt_msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @throws ZMKnownException
|
|
||||||
* @return ContextInterface 返回上下文
|
|
||||||
*/
|
|
||||||
public function cloneFromParent(): ContextInterface
|
|
||||||
{
|
|
||||||
set_coroutine_params(self::$context[Coroutine::getPcid()] ?? self::$context[$this->cid]);
|
|
||||||
return context();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function copy()
|
|
||||||
{
|
|
||||||
return self::$context[$this->cid];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getOption()
|
|
||||||
{
|
|
||||||
return self::getCache('match');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getOriginMessage()
|
|
||||||
{
|
|
||||||
return self::$context[$this->cid]['data']['message'] ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getArrayMessage(): array
|
|
||||||
{
|
|
||||||
$msg = $this->getOriginMessage();
|
|
||||||
if (is_array($msg)) {
|
|
||||||
return $msg;
|
|
||||||
}
|
|
||||||
return MessageUtil::strToArray($msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getStringMessage(): string
|
|
||||||
{
|
|
||||||
$msg = $this->getOriginMessage();
|
|
||||||
if (is_string($msg)) {
|
|
||||||
return $msg;
|
|
||||||
}
|
|
||||||
return MessageUtil::arrayToStr($msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,100 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\Context;
|
|
||||||
|
|
||||||
use Swoole\Http\Request;
|
|
||||||
use Swoole\WebSocket\Frame;
|
|
||||||
use Swoole\WebSocket\Server;
|
|
||||||
use ZM\API\ZMRobot;
|
|
||||||
use ZM\ConnectionManager\ConnectionObject;
|
|
||||||
use ZM\Http\Response;
|
|
||||||
|
|
||||||
interface ContextInterface
|
|
||||||
{
|
|
||||||
public function __construct($cid);
|
|
||||||
|
|
||||||
/** @return Server */
|
|
||||||
public function getServer();
|
|
||||||
|
|
||||||
/** @return Frame */
|
|
||||||
public function getFrame();
|
|
||||||
|
|
||||||
/** @return mixed */
|
|
||||||
public function getData();
|
|
||||||
|
|
||||||
public function setData($data);
|
|
||||||
|
|
||||||
/** @return ConnectionObject */
|
|
||||||
public function getConnection();
|
|
||||||
|
|
||||||
/** @return null|int */
|
|
||||||
public function getFd();
|
|
||||||
|
|
||||||
/** @return int */
|
|
||||||
public function getCid();
|
|
||||||
|
|
||||||
/** @return Response */
|
|
||||||
public function getResponse();
|
|
||||||
|
|
||||||
/** @return Request */
|
|
||||||
public function getRequest();
|
|
||||||
|
|
||||||
/** @return ZMRobot */
|
|
||||||
public function getRobot();
|
|
||||||
|
|
||||||
/** @return mixed */
|
|
||||||
public function getUserId();
|
|
||||||
|
|
||||||
/** @return mixed */
|
|
||||||
public function getGroupId();
|
|
||||||
|
|
||||||
/** @return mixed */
|
|
||||||
public function getDiscussId();
|
|
||||||
|
|
||||||
/** @return string */
|
|
||||||
public function getMessageType();
|
|
||||||
|
|
||||||
/** @return mixed */
|
|
||||||
public function getRobotId();
|
|
||||||
|
|
||||||
/** @return mixed */
|
|
||||||
public function getMessage();
|
|
||||||
|
|
||||||
public function setMessage($msg);
|
|
||||||
|
|
||||||
public function setUserId($id);
|
|
||||||
|
|
||||||
public function setGroupId($id);
|
|
||||||
|
|
||||||
public function setDiscussId($id);
|
|
||||||
|
|
||||||
public function setMessageType($type);
|
|
||||||
|
|
||||||
public function getCQResponse();
|
|
||||||
|
|
||||||
public function reply($msg, $yield = false);
|
|
||||||
|
|
||||||
public function finalReply($msg, $yield = false);
|
|
||||||
|
|
||||||
public function waitMessage($prompt = '', $timeout = 600, $timeout_prompt = '');
|
|
||||||
|
|
||||||
public function getArgs($mode, $prompt_msg);
|
|
||||||
|
|
||||||
public function getNextArg($prompt_msg = '');
|
|
||||||
|
|
||||||
public function getFullArg($prompt_msg = '');
|
|
||||||
|
|
||||||
public function setCache($key, $value);
|
|
||||||
|
|
||||||
public function getCache($key);
|
|
||||||
|
|
||||||
public function cloneFromParent();
|
|
||||||
|
|
||||||
public function getNumArg($prompt_msg = '');
|
|
||||||
|
|
||||||
public function copy();
|
|
||||||
|
|
||||||
public function getOption();
|
|
||||||
}
|
|
||||||
139
src/ZM/DB/DB.php
139
src/ZM/DB/DB.php
@ -1,139 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/** @noinspection PhpComposerExtensionStubsInspection */
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
/** @noinspection PhpUnused */
|
|
||||||
|
|
||||||
namespace ZM\DB;
|
|
||||||
|
|
||||||
use PDOException;
|
|
||||||
use ZM\Exception\DbException;
|
|
||||||
use ZM\MySQL\MySQLManager;
|
|
||||||
use ZM\Store\MySQL\SqlPoolStorage;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class DB
|
|
||||||
* @deprecated This will delete in 2.6 or future version, use \ZM\MySQL\MySQLManager::getConnection() instead
|
|
||||||
*/
|
|
||||||
class DB
|
|
||||||
{
|
|
||||||
private static $table_list = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $db_name 数据库名称
|
|
||||||
* @throws DbException
|
|
||||||
*/
|
|
||||||
public static function initTableList(string $db_name)
|
|
||||||
{
|
|
||||||
if (!extension_loaded('mysqlnd')) {
|
|
||||||
throw new DbException('Can not find mysqlnd PHP extension.');
|
|
||||||
}
|
|
||||||
$result = MySQLManager::getWrapper()->fetchAllAssociative('select TABLE_NAME from INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA=?;', [$db_name]);
|
|
||||||
foreach ($result as $v) {
|
|
||||||
self::$table_list[] = $v['TABLE_NAME'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $table_name 表名
|
|
||||||
* @throws DbException
|
|
||||||
* @return Table 返回表对象
|
|
||||||
*/
|
|
||||||
public static function table(string $table_name): Table
|
|
||||||
{
|
|
||||||
if (Table::getTableInstance($table_name) === null) {
|
|
||||||
if (in_array($table_name, self::$table_list)) {
|
|
||||||
return new Table($table_name);
|
|
||||||
}
|
|
||||||
if (SqlPoolStorage::$sql_pool !== null) {
|
|
||||||
throw new DbException('Table ' . $table_name . ' not exist in database.');
|
|
||||||
}
|
|
||||||
throw new DbException('Database connection not exist or connect failed. Please check sql configuration');
|
|
||||||
}
|
|
||||||
return Table::getTableInstance($table_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $line SQL语句
|
|
||||||
* @throws DbException
|
|
||||||
*/
|
|
||||||
public static function statement(string $line)
|
|
||||||
{
|
|
||||||
self::rawQuery($line, []);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $line SQL语句
|
|
||||||
* @return bool 返回查询是否成功的结果
|
|
||||||
*/
|
|
||||||
public static function unprepared(string $line): bool
|
|
||||||
{
|
|
||||||
$conn = SqlPoolStorage::$sql_pool->getConnection();
|
|
||||||
$result = !($conn->query($line) === false);
|
|
||||||
SqlPoolStorage::$sql_pool->putConnection($conn);
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $line SQL语句
|
|
||||||
* @param array $params 查询参数
|
|
||||||
* @param int $fetch_mode fetch规则
|
|
||||||
* @throws DbException
|
|
||||||
* @return array|false 返回结果集或false
|
|
||||||
*/
|
|
||||||
public static function rawQuery(string $line, array $params = [], int $fetch_mode = ZM_DEFAULT_FETCH_MODE)
|
|
||||||
{
|
|
||||||
if (!is_array($params)) {
|
|
||||||
$params = [$params];
|
|
||||||
}
|
|
||||||
logger()->debug('MySQL: ' . $line . ' | ' . implode(', ', $params));
|
|
||||||
try {
|
|
||||||
if (SqlPoolStorage::$sql_pool === null) {
|
|
||||||
throw new DbException('未连接到任何数据库!');
|
|
||||||
}
|
|
||||||
$conn = SqlPoolStorage::$sql_pool->getConnection();
|
|
||||||
$ps = $conn->prepare($line);
|
|
||||||
if ($ps === false) {
|
|
||||||
SqlPoolStorage::$sql_pool->putConnection(null);
|
|
||||||
throw new DbException('SQL语句查询错误,' . $line . ',错误信息:' . $conn->errorInfo()[2]);
|
|
||||||
}
|
|
||||||
if ($params == []) {
|
|
||||||
$result = $ps->execute();
|
|
||||||
} elseif (!is_array($params)) {
|
|
||||||
$result = $ps->execute([$params]);
|
|
||||||
} else {
|
|
||||||
$result = $ps->execute($params);
|
|
||||||
}
|
|
||||||
if ($result !== true) {
|
|
||||||
SqlPoolStorage::$sql_pool->putConnection(null);
|
|
||||||
throw new DBException("语句[{$line}]错误!" . $ps->errorInfo()[2]);
|
|
||||||
// echo json_encode(debug_backtrace(), 128 | 256);
|
|
||||||
}
|
|
||||||
SqlPoolStorage::$sql_pool->putConnection($conn);
|
|
||||||
return $ps->fetchAll($fetch_mode);
|
|
||||||
} catch (DbException $e) {
|
|
||||||
if (mb_strpos($e->getMessage(), 'has gone away') !== false) {
|
|
||||||
zm_sleep();
|
|
||||||
logger()->warning('Gone away of MySQL! retrying!');
|
|
||||||
return self::rawQuery($line, $params);
|
|
||||||
}
|
|
||||||
logger()->warning($e->getMessage());
|
|
||||||
throw $e;
|
|
||||||
} catch (PDOException $e) {
|
|
||||||
if (mb_strpos($e->getMessage(), 'has gone away') !== false) {
|
|
||||||
zm_sleep();
|
|
||||||
logger()->warning('Gone away of MySQL! retrying!');
|
|
||||||
return self::rawQuery($line, $params);
|
|
||||||
}
|
|
||||||
logger()->warning($e->getMessage());
|
|
||||||
throw new DbException($e->getMessage(), $e->getCode(), $e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function isTableExists($table): bool
|
|
||||||
{
|
|
||||||
return in_array($table, self::$table_list);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,39 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\DB;
|
|
||||||
|
|
||||||
use ZM\Exception\DbException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class DeleteBody
|
|
||||||
* @deprecated This will delete in 2.6 or future version, use \ZM\MySQL\MySQLManager::getConnection() instead
|
|
||||||
*/
|
|
||||||
class DeleteBody
|
|
||||||
{
|
|
||||||
use WhereBody;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var Table
|
|
||||||
*/
|
|
||||||
private $table;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DeleteBody constructor.
|
|
||||||
*/
|
|
||||||
public function __construct(Table $table)
|
|
||||||
{
|
|
||||||
$this->table = $table;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @throws DbException
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function save()
|
|
||||||
{
|
|
||||||
[$sql, $param] = $this->getWhereSQL();
|
|
||||||
return DB::rawQuery('DELETE FROM ' . $this->table->getTableName() . ' WHERE ' . $sql, $param);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,40 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\DB;
|
|
||||||
|
|
||||||
use ZM\Exception\DbException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class InsertBody
|
|
||||||
* @deprecated This will delete in 2.6 or future version, use \ZM\MySQL\MySQLManager::getConnection() instead
|
|
||||||
*/
|
|
||||||
class InsertBody
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var Table
|
|
||||||
*/
|
|
||||||
private $table;
|
|
||||||
|
|
||||||
private $row;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* InsertBody constructor.
|
|
||||||
* @param Table $table 表对象
|
|
||||||
* @param array|string $row 行数据
|
|
||||||
*/
|
|
||||||
public function __construct(Table $table, $row)
|
|
||||||
{
|
|
||||||
$this->table = $table;
|
|
||||||
$this->row = $row;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @throws DbException
|
|
||||||
*/
|
|
||||||
public function save()
|
|
||||||
{
|
|
||||||
DB::rawQuery('INSERT INTO ' . $this->table->getTableName() . ' VALUES (' . implode(',', array_fill(0, count($this->row), '?')) . ')', $this->row);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,138 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\DB;
|
|
||||||
|
|
||||||
use ZM\Exception\DbException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class SelectBody
|
|
||||||
* @deprecated This will delete in 2.6 or future version, use \ZM\MySQL\MySQLManager::getConnection() instead
|
|
||||||
*/
|
|
||||||
class SelectBody
|
|
||||||
{
|
|
||||||
use WhereBody;
|
|
||||||
|
|
||||||
/** @var Table */
|
|
||||||
private $table;
|
|
||||||
|
|
||||||
private $select_thing;
|
|
||||||
|
|
||||||
private $result;
|
|
||||||
|
|
||||||
public function __construct($table, $select_thing)
|
|
||||||
{
|
|
||||||
$this->table = $table;
|
|
||||||
$this->select_thing = $select_thing;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @throws DbException
|
|
||||||
*/
|
|
||||||
public function get()
|
|
||||||
{
|
|
||||||
return $this->fetchAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @throws DbException
|
|
||||||
*/
|
|
||||||
public function count(): int
|
|
||||||
{
|
|
||||||
$this->select_thing = ['count(*)'];
|
|
||||||
$str = $this->queryPrepare();
|
|
||||||
$this->result = DB::rawQuery($str[0], $str[1]);
|
|
||||||
return intval($this->result[0]['count(*)']);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param mixed $fetch_mode
|
|
||||||
* @throws DbException
|
|
||||||
*/
|
|
||||||
public function fetchAll($fetch_mode = ZM_DEFAULT_FETCH_MODE)
|
|
||||||
{
|
|
||||||
$this->execute($fetch_mode);
|
|
||||||
return $this->getResult();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @throws DbException
|
|
||||||
* @return null|mixed
|
|
||||||
*/
|
|
||||||
public function fetchFirst()
|
|
||||||
{
|
|
||||||
return $this->fetchAll()[0] ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param null|mixed $key
|
|
||||||
* @throws DbException
|
|
||||||
* @return null|mixed
|
|
||||||
*/
|
|
||||||
public function value($key = null)
|
|
||||||
{
|
|
||||||
$r = $this->fetchFirst();
|
|
||||||
if ($r === null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if ($key === null) {
|
|
||||||
return current($r);
|
|
||||||
}
|
|
||||||
return $r[$key] ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @throws DbException
|
|
||||||
*/
|
|
||||||
public function execute(int $fetch_mode = ZM_DEFAULT_FETCH_MODE)
|
|
||||||
{
|
|
||||||
$str = $this->queryPrepare();
|
|
||||||
$this->result = DB::rawQuery($str[0], $str[1], $fetch_mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getResult()
|
|
||||||
{
|
|
||||||
return $this->result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function equals(SelectBody $body): bool
|
|
||||||
{
|
|
||||||
if ($this->select_thing != $body->getSelectThing()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if ($this->where_thing == $body->getWhereThing()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function getSelectThing()
|
|
||||||
{
|
|
||||||
return $this->select_thing;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getWhereThing(): array
|
|
||||||
{
|
|
||||||
return $this->where_thing;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function queryPrepare(): array
|
|
||||||
{
|
|
||||||
$msg = 'SELECT ' . implode(', ', $this->select_thing) . ' FROM ' . $this->table->getTableName();
|
|
||||||
$sql = $this->table->paintWhereSQL($this->where_thing['='] ?? [], '=');
|
|
||||||
if ($sql[0] != '') {
|
|
||||||
$msg .= ' WHERE ' . $sql[0];
|
|
||||||
$array = $sql[1];
|
|
||||||
$sql = $this->table->paintWhereSQL($this->where_thing['!='] ?? [], '!=');
|
|
||||||
if ($sql[0] != '') {
|
|
||||||
$msg .= ' AND ' . $sql[0];
|
|
||||||
}
|
|
||||||
$array = array_merge($array, $sql[1]);
|
|
||||||
}
|
|
||||||
return [$msg, $array ?? []];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,98 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @noinspection PhpMissingReturnTypeInspection
|
|
||||||
* @noinspection PhpUnused
|
|
||||||
*/
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\DB;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class Table
|
|
||||||
* @deprecated This will delete in 2.6 or future version, use \ZM\MySQL\MySQLManager::getConnection() instead
|
|
||||||
*/
|
|
||||||
class Table
|
|
||||||
{
|
|
||||||
/** @var SelectBody[] */
|
|
||||||
public $cache = [];
|
|
||||||
|
|
||||||
private $table_name;
|
|
||||||
|
|
||||||
private static $table_instance = [];
|
|
||||||
|
|
||||||
public function __construct($table_name)
|
|
||||||
{
|
|
||||||
$this->table_name = $table_name;
|
|
||||||
self::$table_instance[$table_name] = $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getTableInstance($table_name)
|
|
||||||
{
|
|
||||||
if (isset(self::$table_instance[$table_name])) {
|
|
||||||
return self::$table_instance[$table_name];
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function select($what = [])
|
|
||||||
{
|
|
||||||
return new SelectBody($this, $what == [] ? ['*'] : $what);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function where($column, $operation_or_value, $value = null)
|
|
||||||
{
|
|
||||||
return (new SelectBody($this, ['*']))->where($column, $operation_or_value, $value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function insert($row)
|
|
||||||
{
|
|
||||||
$this->cache = [];
|
|
||||||
return new InsertBody($this, $row);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function update(array $set_value)
|
|
||||||
{
|
|
||||||
$this->cache = [];
|
|
||||||
return new UpdateBody($this, $set_value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function delete()
|
|
||||||
{
|
|
||||||
$this->cache = [];
|
|
||||||
return new DeleteBody($this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function statement()
|
|
||||||
{
|
|
||||||
$this->cache = [];
|
|
||||||
// TODO: 无返回的statement语句
|
|
||||||
}
|
|
||||||
|
|
||||||
public function paintWhereSQL($rule, $operator)
|
|
||||||
{
|
|
||||||
if ($rule == []) {
|
|
||||||
return ['', []];
|
|
||||||
}
|
|
||||||
$msg = '';
|
|
||||||
$param = [];
|
|
||||||
foreach ($rule as $k => $v) {
|
|
||||||
if ($msg == '') {
|
|
||||||
$msg .= $k . " {$operator} ? ";
|
|
||||||
} else {
|
|
||||||
$msg .= ' AND ' . $k . " {$operator} ?";
|
|
||||||
}
|
|
||||||
$param[] = $v;
|
|
||||||
}
|
|
||||||
return [$msg, $param];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function getTableName()
|
|
||||||
{
|
|
||||||
return $this->table_name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,58 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\DB;
|
|
||||||
|
|
||||||
use ZM\Exception\DbException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class UpdateBody
|
|
||||||
* @deprecated This will delete in 2.6 or future version, use \ZM\MySQL\MySQLManager::getConnection() instead
|
|
||||||
*/
|
|
||||||
class UpdateBody
|
|
||||||
{
|
|
||||||
use WhereBody;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var Table
|
|
||||||
*/
|
|
||||||
private $table;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
private $set_value;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* UpdateBody constructor.
|
|
||||||
*/
|
|
||||||
public function __construct(Table $table, array $set_value)
|
|
||||||
{
|
|
||||||
$this->table = $table;
|
|
||||||
$this->set_value = $set_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @throws DbException
|
|
||||||
*/
|
|
||||||
public function save()
|
|
||||||
{
|
|
||||||
$arr = [];
|
|
||||||
$msg = [];
|
|
||||||
foreach ($this->set_value as $k => $v) {
|
|
||||||
$msg[] = $k . ' = ?';
|
|
||||||
$arr[] = $v;
|
|
||||||
}
|
|
||||||
if ($msg == []) {
|
|
||||||
throw new DbException('update value sets can not be empty!');
|
|
||||||
}
|
|
||||||
$line = 'UPDATE ' . $this->table->getTableName() . ' SET ' . implode(', ', $msg);
|
|
||||||
if ($this->where_thing != []) {
|
|
||||||
[$sql, $param] = $this->getWhereSQL();
|
|
||||||
$arr = array_merge($arr, $param);
|
|
||||||
$line .= ' WHERE ' . $sql;
|
|
||||||
}
|
|
||||||
return DB::rawQuery($line, $arr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,48 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/** @noinspection PhpMissingReturnTypeInspection */
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\DB;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Trait WhereBody
|
|
||||||
* @deprecated This will delete in 2.6 or future version, use \ZM\MySQL\MySQLManager::getConnection() instead
|
|
||||||
*/
|
|
||||||
trait WhereBody
|
|
||||||
{
|
|
||||||
protected $where_thing = [];
|
|
||||||
|
|
||||||
public function where($column, $operation_or_value, $value = null)
|
|
||||||
{
|
|
||||||
if ($value !== null) {
|
|
||||||
$this->where_thing[$operation_or_value][$column] = $value;
|
|
||||||
} elseif (!in_array($operation_or_value, ['=', '!=', '>', '<', '>=', '<=', 'IN', 'in'])) {
|
|
||||||
$this->where_thing['='][$column] = $operation_or_value;
|
|
||||||
} else {
|
|
||||||
$this->where_thing['='][$column] = $operation_or_value;
|
|
||||||
}
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getWhereSQL()
|
|
||||||
{
|
|
||||||
$param = [];
|
|
||||||
$msg = '';
|
|
||||||
foreach ($this->where_thing as $k => $v) {
|
|
||||||
foreach ($v as $ks => $vs) {
|
|
||||||
if ($param != []) {
|
|
||||||
$msg .= ' AND ' . $ks . " {$k} ?";
|
|
||||||
} else {
|
|
||||||
$msg .= "{$ks} {$k} ?";
|
|
||||||
}
|
|
||||||
$param[] = $vs;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($msg == '') {
|
|
||||||
$msg = 1;
|
|
||||||
}
|
|
||||||
return [$msg, $param];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,31 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\Entity;
|
|
||||||
|
|
||||||
class CQObject
|
|
||||||
{
|
|
||||||
public $type;
|
|
||||||
|
|
||||||
public $params;
|
|
||||||
|
|
||||||
public $start;
|
|
||||||
|
|
||||||
public $end;
|
|
||||||
|
|
||||||
public function __construct($type = '', $params = [], $start = 0, $end = 0)
|
|
||||||
{
|
|
||||||
if ($type !== '') {
|
|
||||||
$this->type = $type;
|
|
||||||
$this->params = $params;
|
|
||||||
$this->start = $start;
|
|
||||||
$this->end = $end;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function fromArray($arr): CQObject
|
|
||||||
{
|
|
||||||
return new CQObject($arr['type'], $arr['params'] ?? [], $arr['start'], $arr['end']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,30 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\Entity;
|
|
||||||
|
|
||||||
class InputArguments
|
|
||||||
{
|
|
||||||
private $arguments;
|
|
||||||
|
|
||||||
public function __construct(array $arguments)
|
|
||||||
{
|
|
||||||
$this->arguments = $arguments;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getArguments(): array
|
|
||||||
{
|
|
||||||
return $this->arguments;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getArgument($name)
|
|
||||||
{
|
|
||||||
return $this->arguments[$name] ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function get($name)
|
|
||||||
{
|
|
||||||
return $this->getArgument($name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,19 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\Entity;
|
|
||||||
|
|
||||||
use ZM\Annotation\CQ\CQCommand;
|
|
||||||
|
|
||||||
class MatchResult
|
|
||||||
{
|
|
||||||
/** @var bool */
|
|
||||||
public $status = false;
|
|
||||||
|
|
||||||
/** @var null|CQCommand */
|
|
||||||
public $object;
|
|
||||||
|
|
||||||
/** @var array */
|
|
||||||
public $match = [];
|
|
||||||
}
|
|
||||||
@ -1,307 +1,12 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
/** @noinspection PhpUnused */
|
|
||||||
|
|
||||||
namespace ZM\Event;
|
namespace ZM\Event;
|
||||||
|
|
||||||
use Closure;
|
use OneBot\Util\Singleton;
|
||||||
use Doctrine\Common\Annotations\AnnotationException;
|
|
||||||
use Error;
|
|
||||||
use Exception;
|
|
||||||
use Throwable;
|
|
||||||
use ZM\Config\ZMConfig;
|
|
||||||
use ZM\Exception\InterruptException;
|
|
||||||
use ZM\Store\LightCacheInside;
|
|
||||||
use ZM\Store\Lock\SpinLock;
|
|
||||||
use ZM\Store\ZMAtomic;
|
|
||||||
use ZM\Utils\ZMUtil;
|
|
||||||
|
|
||||||
class EventDispatcher
|
class EventDispatcher extends \OneBot\Driver\Event\EventDispatcher
|
||||||
{
|
{
|
||||||
public const STATUS_NORMAL = 0; // 正常结束
|
use Singleton;
|
||||||
|
|
||||||
public const STATUS_INTERRUPTED = 1; // 被interrupt了,不管在什么地方
|
|
||||||
|
|
||||||
public const STATUS_EXCEPTION = 2; // 执行过程中抛出了异常
|
|
||||||
|
|
||||||
public const STATUS_BEFORE_FAILED = 3; // 中间件HandleBefore返回了false,所以不执行此方法
|
|
||||||
|
|
||||||
public const STATUS_RULE_FAILED = 4; // 判断事件执行的规则函数判定为false,所以不执行此方法
|
|
||||||
|
|
||||||
/** @var int */
|
|
||||||
public $status = self::STATUS_NORMAL;
|
|
||||||
|
|
||||||
/** @var mixed */
|
|
||||||
public $store;
|
|
||||||
|
|
||||||
/** @var string */
|
|
||||||
private $class;
|
|
||||||
|
|
||||||
/** @var null|callable */
|
|
||||||
private $rule;
|
|
||||||
|
|
||||||
/** @var null|callable */
|
|
||||||
private $return_func;
|
|
||||||
|
|
||||||
/** @var bool */
|
|
||||||
private $log = false;
|
|
||||||
|
|
||||||
/** @var int */
|
|
||||||
private $eid;
|
|
||||||
|
|
||||||
public function __construct(string $class = '')
|
|
||||||
{
|
|
||||||
$this->class = $class;
|
|
||||||
$this->eid = ZMAtomic::get('_event_id')->add(1);
|
|
||||||
$list = LightCacheInside::get('wait_api', 'event_trace');
|
|
||||||
if (isset($list[$class])) {
|
|
||||||
$this->log = true;
|
|
||||||
}
|
|
||||||
if ($this->log) {
|
|
||||||
logger()->debug("[事件分发{$this->eid}] 开始分发事件: " . $class);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param mixed $return_var
|
|
||||||
* @throws InterruptException
|
|
||||||
*/
|
|
||||||
public static function interrupt($return_var = null)
|
|
||||||
{
|
|
||||||
throw new InterruptException($return_var);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function enableEventTrace($event_class)
|
|
||||||
{
|
|
||||||
SpinLock::lock('_event_trace');
|
|
||||||
$list = LightCacheInside::get('wait_api', 'event_trace');
|
|
||||||
$list[$event_class] = true;
|
|
||||||
LightCacheInside::set('wait_api', 'event_trace', $list);
|
|
||||||
SpinLock::unlock('_event_trace');
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function disableEventTrace($event_class)
|
|
||||||
{
|
|
||||||
SpinLock::lock('_event_trace');
|
|
||||||
$list = LightCacheInside::get('wait_api', 'event_trace');
|
|
||||||
unset($list[$event_class]);
|
|
||||||
LightCacheInside::set('wait_api', 'event_trace', $list);
|
|
||||||
SpinLock::unlock('_event_trace');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setRuleFunction(callable $rule = null): EventDispatcher
|
|
||||||
{
|
|
||||||
if ($this->log) {
|
|
||||||
logger()->debug("[事件分发{$this->eid}] 设置事件rule: " . $this->class);
|
|
||||||
}
|
|
||||||
$this->rule = $rule;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setReturnFunction(callable $return_func): EventDispatcher
|
|
||||||
{
|
|
||||||
if ($this->log) {
|
|
||||||
logger()->debug("[事件分发{$this->eid}] 设置事件returnFunc: " . $this->class);
|
|
||||||
}
|
|
||||||
$this->return_func = $return_func;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param mixed ...$params
|
|
||||||
* @throws Throwable
|
|
||||||
*/
|
|
||||||
public function dispatchEvents(...$params)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
foreach ((EventManager::$events[$this->class] ?? []) as $v) {
|
|
||||||
// if ($v->class === QQBot::class && $v->method === 'handleByEvent') {
|
|
||||||
// zm_dump(EventManager::$events[$this->class]);
|
|
||||||
// $v->class = OneBot11Adapter::class;
|
|
||||||
// $v->method = 'handleIncomingRequest';
|
|
||||||
// }
|
|
||||||
$this->dispatchEvent($v, $this->rule, ...$params);
|
|
||||||
if ($this->log) {
|
|
||||||
logger()->debug("[事件分发{$this->eid}] 单一对象 " . $v->class . '::' . (is_string($v->method) ? $v->method : '{closure}') . ' 分发结束。');
|
|
||||||
}
|
|
||||||
if ($this->status == self::STATUS_BEFORE_FAILED || $this->status == self::STATUS_RULE_FAILED) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (is_callable($this->return_func) && $this->status === self::STATUS_NORMAL) {
|
|
||||||
if ($this->log) {
|
|
||||||
logger()->debug("[事件分发{$this->eid}] 单一对象 " . $v->class . '::' . $v->method . ' 正在执行返回值处理函数 ...');
|
|
||||||
}
|
|
||||||
($this->return_func)($this->store);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($this->status === self::STATUS_RULE_FAILED) {
|
|
||||||
$this->status = self::STATUS_NORMAL;
|
|
||||||
}
|
|
||||||
// TODO:没有过滤before的false,可能会导致一些问题,先观望一下
|
|
||||||
} catch (InterruptException $e) {
|
|
||||||
$this->store = $e->return_var;
|
|
||||||
$this->status = self::STATUS_INTERRUPTED;
|
|
||||||
} catch (Throwable $e) {
|
|
||||||
$this->status = self::STATUS_EXCEPTION;
|
|
||||||
throw $e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param mixed $v
|
|
||||||
* @param null|mixed $rule_func
|
|
||||||
* @param mixed ...$params
|
|
||||||
* @throws InterruptException
|
|
||||||
* @throws AnnotationException
|
|
||||||
* @throws Error
|
|
||||||
* @return bool
|
|
||||||
* @noinspection PhpMissingReturnTypeInspection
|
|
||||||
*/
|
|
||||||
public function dispatchEvent($v, $rule_func = null, ...$params)
|
|
||||||
{
|
|
||||||
$q_c = $v->class;
|
|
||||||
$q_f = $v->method;
|
|
||||||
if (($q_c ?? '') === '' && ($q_f instanceof Closure)) {
|
|
||||||
if ($this->log) {
|
|
||||||
logger()->debug("[事件分发{$this->eid}] 闭包函数的事件触发过程!");
|
|
||||||
}
|
|
||||||
if ($rule_func !== null && !$rule_func($v)) {
|
|
||||||
if ($this->log) {
|
|
||||||
logger()->debug("[事件分发{$this->eid}] 闭包函数下的 ruleFunc 判断为 false, 拒绝执行此方法。");
|
|
||||||
}
|
|
||||||
$this->status = self::STATUS_RULE_FAILED;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$this->store = $q_f(...$params);
|
|
||||||
$this->status = self::STATUS_NORMAL;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if ($this->log) {
|
|
||||||
logger()->debug("[事件分发{$this->eid}] 正在判断 " . $q_c . '::' . $q_f . ' 方法下的 ruleFunc ...');
|
|
||||||
}
|
|
||||||
if ($rule_func !== null && !$rule_func($v)) {
|
|
||||||
if ($this->log) {
|
|
||||||
logger()->debug("[事件分发{$this->eid}] " . $q_c . '::' . $q_f . ' 方法下的 ruleFunc 判断为 false, 拒绝执行此方法。');
|
|
||||||
}
|
|
||||||
$this->status = self::STATUS_RULE_FAILED;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if ($this->log) {
|
|
||||||
logger()->debug("[事件分发{$this->eid}] " . $q_c . '::' . $q_f . ' 方法下的 ruleFunc 为真,继续执行方法本身 ...');
|
|
||||||
}
|
|
||||||
if (isset(EventManager::$middleware_map[$q_c][$q_f])) {
|
|
||||||
$middlewares = EventManager::$middleware_map[$q_c][$q_f];
|
|
||||||
if ($this->log) {
|
|
||||||
logger()->debug("[事件分发{$this->eid}] " . $q_c . '::' . $q_f . ' 方法还绑定了 Middleware:' . implode(', ', array_map(function ($x) {
|
|
||||||
return $x->middleware;
|
|
||||||
}, $middlewares)));
|
|
||||||
}
|
|
||||||
$before_result = true;
|
|
||||||
$r = [];
|
|
||||||
foreach ($middlewares as $k => $middleware) {
|
|
||||||
if (!isset(EventManager::$middlewares[$middleware->middleware])) {
|
|
||||||
if ((ZMConfig::get('global', 'runtime')['middleware_error_policy'] ?? 1) == 1) {
|
|
||||||
throw new AnnotationException("Annotation parse error: Unknown MiddlewareClass named \"{$middleware->middleware}\"!");
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$middleware_obj = EventManager::$middlewares[$middleware->middleware];
|
|
||||||
$before = $middleware_obj['class'];
|
|
||||||
// var_dump($middleware_obj);
|
|
||||||
$r[$k] = new $before();
|
|
||||||
$r[$k]->class = $q_c;
|
|
||||||
$r[$k]->method = $q_f;
|
|
||||||
$r[$k]->middleware = $middleware;
|
|
||||||
$r[$k]->current_event = $v;
|
|
||||||
if (isset($middleware_obj['before'])) {
|
|
||||||
if ($this->log) {
|
|
||||||
logger()->debug("[事件分发{$this->eid}] Middleware 存在前置事件,执行中 ...");
|
|
||||||
}
|
|
||||||
$rs = $middleware_obj['before'];
|
|
||||||
$before_result = $r[$k]->{$rs}(...$params);
|
|
||||||
if ($before_result === false) {
|
|
||||||
if ($this->log) {
|
|
||||||
logger()->debug("[事件分发{$this->eid}] Middleware 前置事件为 false,停止执行原事件,开始执行下一事件。");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if ($this->log) {
|
|
||||||
logger()->debug("[事件分发{$this->eid}] Middleware 前置事件为 true,继续执行原事件。");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($before_result) {
|
|
||||||
try {
|
|
||||||
$q_o = ZMUtil::getModInstance($q_c);
|
|
||||||
$q_o->_running_annotation = $v;
|
|
||||||
if ($this->log) {
|
|
||||||
logger()->debug("[事件分发{$this->eid}] 正在执行方法 " . $q_c . '::' . $q_f . ' ...');
|
|
||||||
}
|
|
||||||
$this->store = container()->call([$q_o, $q_f], $params);
|
|
||||||
} catch (Exception $e) {
|
|
||||||
if ($e instanceof InterruptException) {
|
|
||||||
if ($this->log) {
|
|
||||||
logger()->debug("[事件分发{$this->eid}] 检测到事件阻断调用,正在跳出事件分发器 ...");
|
|
||||||
}
|
|
||||||
throw $e;
|
|
||||||
}
|
|
||||||
if ($this->log) {
|
|
||||||
logger()->debug("[事件分发{$this->eid}] 方法 " . $q_c . '::' . $q_f . ' 执行过程中抛出了异常,正在倒序查找 Middleware 中的捕获方法 ...');
|
|
||||||
}
|
|
||||||
for ($i = count($middlewares) - 1; $i >= 0; --$i) {
|
|
||||||
$middleware_obj = EventManager::$middlewares[$middlewares[$i]->middleware];
|
|
||||||
if (!isset($middleware_obj['exceptions'])) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
foreach ($middleware_obj['exceptions'] as $name => $method) {
|
|
||||||
if ($e instanceof $name) {
|
|
||||||
if ($this->log) {
|
|
||||||
logger()->debug("[事件分发{$this->eid}] 方法 " . $q_c . '::' . $q_f . ' 的异常 ' . get_class($e) . ' 被 Middleware:' . $middlewares[$i] . ' 下的 ' . get_class($r[$i]) . '::' . $method . ' 捕获。');
|
|
||||||
}
|
|
||||||
$r[$i]->{$method}($e);
|
|
||||||
self::interrupt();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw $e;
|
|
||||||
}
|
|
||||||
$cnts = count($middlewares) - 1;
|
|
||||||
for ($i = $cnts; $i >= 0; --$i) {
|
|
||||||
$middleware_obj = EventManager::$middlewares[$middlewares[$i]->middleware];
|
|
||||||
if (isset($middleware_obj['after'], $r[$i])) {
|
|
||||||
if ($this->log) {
|
|
||||||
logger()->debug("[事件分发{$this->eid}] Middleware 存在后置事件,执行中 ...");
|
|
||||||
}
|
|
||||||
$r[$i]->{$middleware_obj['after']}(...$params);
|
|
||||||
if ($this->log) {
|
|
||||||
logger()->debug("[事件分发{$this->eid}] Middleware 后置事件执行完毕!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$this->status = self::STATUS_NORMAL;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
$this->status = self::STATUS_BEFORE_FAILED;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$q_o = ZMUtil::getModInstance($q_c);
|
|
||||||
$q_o->_running_annotation = $v;
|
|
||||||
if ($this->log) {
|
|
||||||
logger()->debug("[事件分发{$this->eid}] 正在执行方法 " . $q_c . '::' . $q_f . ' ...');
|
|
||||||
}
|
|
||||||
$this->store = container()->call([$q_o, $q_f], $params);
|
|
||||||
$this->status = self::STATUS_NORMAL;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getEid(): int
|
|
||||||
{
|
|
||||||
return $this->eid;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getClass(): string
|
|
||||||
{
|
|
||||||
return $this->class;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,94 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\Event;
|
|
||||||
|
|
||||||
use Closure;
|
|
||||||
use Error;
|
|
||||||
use Exception;
|
|
||||||
use Swoole\Timer;
|
|
||||||
use ZM\Annotation\AnnotationBase;
|
|
||||||
use ZM\Annotation\AnnotationParser;
|
|
||||||
use ZM\Annotation\Swoole\OnTick;
|
|
||||||
use ZM\Config\ZMConfig;
|
|
||||||
use ZM\Console\Console;
|
|
||||||
use ZM\Exception\AnnotationException;
|
|
||||||
use ZM\Store\LightCache;
|
|
||||||
use ZM\Store\ZMAtomic;
|
|
||||||
|
|
||||||
class EventManager
|
|
||||||
{
|
|
||||||
public static $events = [];
|
|
||||||
|
|
||||||
public static $middleware_map = [];
|
|
||||||
|
|
||||||
public static $event_map = [];
|
|
||||||
|
|
||||||
public static $middlewares = [];
|
|
||||||
|
|
||||||
public static $req_mapping = [];
|
|
||||||
|
|
||||||
public static function addEvent($event_name, ?AnnotationBase $event_obj)
|
|
||||||
{
|
|
||||||
if ($event_obj->method instanceof Closure) {
|
|
||||||
logger()->debug("Adding event {$event_name} at @Anonymous");
|
|
||||||
} else {
|
|
||||||
logger()->debug("Adding event {$event_name} at " . ($event_obj->class) . ':' . ($event_obj->method));
|
|
||||||
self::$event_map[$event_obj->class][$event_obj->method][] = $event_obj;
|
|
||||||
}
|
|
||||||
self::$events[$event_name][] = $event_obj;
|
|
||||||
(new AnnotationParser())->sortByLevel(self::$events, $event_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @throws AnnotationException
|
|
||||||
*/
|
|
||||||
public static function loadEventByParser(AnnotationParser $parser)
|
|
||||||
{
|
|
||||||
self::$events = array_merge(self::$events, $parser->generateAnnotationEvents());
|
|
||||||
self::$middlewares = $parser->getMiddlewares();
|
|
||||||
self::$middleware_map = $parser->getMiddlewareMap();
|
|
||||||
self::$req_mapping = $parser->getReqMapping();
|
|
||||||
$parser->verifyMiddlewares();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 注册所有计时器给每个进程
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
public static function registerTimerTick()
|
|
||||||
{
|
|
||||||
$dispatcher = new EventDispatcher(OnTick::class);
|
|
||||||
foreach (self::$events[OnTick::class] ?? [] as $vss) {
|
|
||||||
if (server()->worker_id !== $vss->worker_id && $vss->worker_id != -1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// echo server()->worker_id.PHP_EOL;
|
|
||||||
$plain_class = $vss->class;
|
|
||||||
logger()->debug('Added Middleware-based timer: ' . $plain_class . ' -> ' . $vss->method);
|
|
||||||
Timer::tick($vss->tick_ms, function () use ($vss, $dispatcher) {
|
|
||||||
set_coroutine_params([]);
|
|
||||||
if (ZMAtomic::get('stop_signal')->get() != 0) {
|
|
||||||
Timer::clearAll();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
$dispatcher->dispatchEvent($vss, null);
|
|
||||||
} catch (Exception $e) {
|
|
||||||
Console::error(zm_internal_errcode('E00034') . 'Uncaught error from TimerTick: ' . $e->getMessage() . ' at ' . $e->getFile() . "({$e->getLine()})");
|
|
||||||
} catch (Error $e) {
|
|
||||||
Console::error(zm_internal_errcode('E00034') . 'Uncaught fatal error from TimerTick: ' . $e->getMessage());
|
|
||||||
echo Console::setColor($e->getTraceAsString(), 'gray');
|
|
||||||
Console::error('Please check your code!');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
$conf = ZMConfig::get('global', 'worker_cache') ?? ['worker' => 0];
|
|
||||||
if (server()->worker_id == $conf['worker']) {
|
|
||||||
zm_timer_tick(ZMConfig::get('global', 'light_cache')['auto_save_interval'] * 1000, static function () {
|
|
||||||
LightCache::savePersistence();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,78 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\Event;
|
|
||||||
|
|
||||||
use Iterator;
|
|
||||||
use ReturnTypeWillChange;
|
|
||||||
use ZM\Console\Console;
|
|
||||||
|
|
||||||
class EventMapIterator implements Iterator
|
|
||||||
{
|
|
||||||
private $offset = 0;
|
|
||||||
|
|
||||||
private $class;
|
|
||||||
|
|
||||||
private $method;
|
|
||||||
|
|
||||||
private $event_name;
|
|
||||||
|
|
||||||
public function __construct($class, $method, $event_name)
|
|
||||||
{
|
|
||||||
$this->class = $class;
|
|
||||||
$this->method = $method;
|
|
||||||
$this->event_name = $event_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[ReturnTypeWillChange]
|
|
||||||
public function current()
|
|
||||||
{
|
|
||||||
logger()->debug('从 [' . $this->offset . '] 开始获取');
|
|
||||||
return EventManager::$event_map[$this->class][$this->method][$this->offset];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function next(): void
|
|
||||||
{
|
|
||||||
logger()->debug('下一个offset为 [' . ++$this->offset . ']');
|
|
||||||
$this->nextToValid();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[ReturnTypeWillChange]
|
|
||||||
public function key()
|
|
||||||
{
|
|
||||||
logger()->debug('返回key:' . $this->offset);
|
|
||||||
return isset(EventManager::$event_map[$this->class][$this->method][$this->offset]) ? $this->offset : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function valid($s = false): bool
|
|
||||||
{
|
|
||||||
logger()->debug(
|
|
||||||
"[{$this->offset}] " .
|
|
||||||
($s ? 'valid' : '') . '存在:' .
|
|
||||||
(!isset(EventManager::$event_map[$this->class][$this->method][$this->offset]) ? Console::setColor('false', 'red') : ('true' .
|
|
||||||
(is_a(EventManager::$event_map[$this->class][$this->method][$this->offset], $this->event_name, true) ? ',是目标对象' : ',不是目标对象')))
|
|
||||||
);
|
|
||||||
return
|
|
||||||
isset(EventManager::$event_map[$this->class][$this->method][$this->offset])
|
|
||||||
&& is_a(EventManager::$event_map[$this->class][$this->method][$this->offset], $this->event_name, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function rewind(): void
|
|
||||||
{
|
|
||||||
logger()->debug('回到0');
|
|
||||||
$this->offset = 0;
|
|
||||||
$this->nextToValid();
|
|
||||||
}
|
|
||||||
|
|
||||||
private function nextToValid()
|
|
||||||
{
|
|
||||||
while (
|
|
||||||
isset(EventManager::$event_map[$this->class][$this->method][$this->offset])
|
|
||||||
&& !is_a(EventManager::$event_map[$this->class][$this->method][$this->offset], $this->event_name, true)
|
|
||||||
) {
|
|
||||||
++$this->offset;
|
|
||||||
}
|
|
||||||
logger()->debug('内部偏移offset为 [' . $this->offset . ']');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
84
src/ZM/Event/EventProvider.php
Normal file
84
src/ZM/Event/EventProvider.php
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace ZM\Event;
|
||||||
|
|
||||||
|
use OneBot\Driver\Interfaces\SortedProviderInterface;
|
||||||
|
use OneBot\Util\Singleton;
|
||||||
|
use Stringable;
|
||||||
|
|
||||||
|
class EventProvider implements SortedProviderInterface
|
||||||
|
{
|
||||||
|
use Singleton;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array<string, array<array<int, callable>>> 已注册的事件监听器
|
||||||
|
*/
|
||||||
|
private static $_events = [];
|
||||||
|
|
||||||
|
/** @var array @phpstan-ignore-next-line */
|
||||||
|
private static $_event_map = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加事件监听器
|
||||||
|
*
|
||||||
|
* @param object|string $event 事件名称
|
||||||
|
* @param callable $callback 事件回调
|
||||||
|
* @param int $level 事件等级
|
||||||
|
*/
|
||||||
|
public function addEventListener($event, callable $callback, int $level = 20)
|
||||||
|
{
|
||||||
|
if (is_object($event)) { // 传入对象时必须带 class 和 method 属性,这时将忽略 callback 参数
|
||||||
|
if (property_exists($event, 'class') && property_exists($event, 'method')) {
|
||||||
|
self::$_events[get_class($event)][] = [$level, [resolve($event->class), $event->method]];
|
||||||
|
self::$_event_map[$event->class][$event->method][] = $event;
|
||||||
|
} elseif (is_array($callback) && is_object($callback[0] ?? '') && is_string($callback[1] ?? null)) {
|
||||||
|
// 如果没有上面两个属性,则可能是回调函数是一个数组,如果是这样,则可以直接使用回调函数
|
||||||
|
self::$_event_map[get_class($callback[0])][$callback[1]][] = $event;
|
||||||
|
$event->class = get_class($callback[0]);
|
||||||
|
$event->method = $callback[1];
|
||||||
|
}
|
||||||
|
$this->sortEvents(get_class($event));
|
||||||
|
} elseif (is_string($event)) {
|
||||||
|
self::$_events[$event][] = [$level, $callback];
|
||||||
|
$this->sortEvents($event);
|
||||||
|
} else {
|
||||||
|
logger()->error('传入了错误的对象');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取事件监听器
|
||||||
|
*
|
||||||
|
* @param string $event_name 事件名称
|
||||||
|
* @return array<callable>
|
||||||
|
*/
|
||||||
|
public function getEventListeners(string $event_name): array
|
||||||
|
{
|
||||||
|
return self::$_events[$event_name] ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取事件监听器
|
||||||
|
*
|
||||||
|
* @param object $event 事件对象
|
||||||
|
* @return iterable<callable>
|
||||||
|
*/
|
||||||
|
public function getListenersForEvent(object $event): iterable
|
||||||
|
{
|
||||||
|
return self::getEventListeners(method_exists($event, 'getName') ? $event->getName() : get_class($event));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 排序事件
|
||||||
|
*
|
||||||
|
* @param string|Stringable $name
|
||||||
|
*/
|
||||||
|
private function sortEvents($name)
|
||||||
|
{
|
||||||
|
usort(self::$_events[$name], function ($a, $b) {
|
||||||
|
return $a[0] <= $b[0] ? 1 : -1;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user