mirror of
https://github.com/zhamao-robot/zhamao-framework.git
synced 2026-07-03 23:05:37 +08:00
Compare commits
21 Commits
2.6.2
...
2.7.0-beta
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d01bd69aa5 | ||
|
|
e6b9ae3ee1 | ||
|
|
73b6b8045d | ||
|
|
3c87abc6e8 | ||
|
|
e95925c129 | ||
|
|
82c44d6c40 | ||
|
|
e0a268e05e | ||
|
|
487892e1d9 | ||
|
|
7ab4e88359 | ||
|
|
4702b6ee75 | ||
|
|
20cd3aa66d | ||
|
|
391114bdef | ||
|
|
ffe1052ecc | ||
|
|
b4159152a7 | ||
|
|
8fc6e4b0f7 | ||
|
|
c8938b7a4b | ||
|
|
34db1626a5 | ||
|
|
7f0c97c5b9 | ||
|
|
74050c46e7 | ||
| 3ed1cb665a | |||
|
|
a6f33ba69d |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -12,9 +12,11 @@ composer.lock
|
||||
.daemon_pid
|
||||
/runtime/
|
||||
/tmp/
|
||||
/temp/
|
||||
/ext/go-cqhttp/data/
|
||||
/ext/go-cqhttp/logs/
|
||||
/ext/go-cqhttp/config.hjson
|
||||
/ext/go-cqhttp/device.json
|
||||
/ext/go-cqhttp/go-cqhttp
|
||||
/ext/go-cqhttp/session.token
|
||||
.zm_worker_*.pid
|
||||
17
.run/Reload Zhamao Server.run.xml
Normal file
17
.run/Reload Zhamao Server.run.xml
Normal file
@@ -0,0 +1,17 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Reload Zhamao Server" type="ShConfigurationType">
|
||||
<option name="SCRIPT_TEXT" value="./zhamao server:reload && exit" />
|
||||
<option name="INDEPENDENT_SCRIPT_PATH" value="true" />
|
||||
<option name="SCRIPT_PATH" value="" />
|
||||
<option name="SCRIPT_OPTIONS" value="" />
|
||||
<option name="INDEPENDENT_SCRIPT_WORKING_DIRECTORY" value="true" />
|
||||
<option name="SCRIPT_WORKING_DIRECTORY" value="$PROJECT_DIR$" />
|
||||
<option name="INDEPENDENT_INTERPRETER_PATH" value="true" />
|
||||
<option name="INTERPRETER_PATH" value="" />
|
||||
<option name="INTERPRETER_OPTIONS" value="" />
|
||||
<option name="EXECUTE_IN_TERMINAL" value="true" />
|
||||
<option name="EXECUTE_SCRIPT_FILE" value="false" />
|
||||
<envs />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
17
.run/Run Zhamao Server.run.xml
Normal file
17
.run/Run Zhamao Server.run.xml
Normal file
@@ -0,0 +1,17 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Run Zhamao Server" type="ShConfigurationType">
|
||||
<option name="SCRIPT_TEXT" value="./zhamao server" />
|
||||
<option name="INDEPENDENT_SCRIPT_PATH" value="true" />
|
||||
<option name="SCRIPT_PATH" value="./zhamao" />
|
||||
<option name="SCRIPT_OPTIONS" value="server" />
|
||||
<option name="INDEPENDENT_SCRIPT_WORKING_DIRECTORY" value="true" />
|
||||
<option name="SCRIPT_WORKING_DIRECTORY" value="$PROJECT_DIR$" />
|
||||
<option name="INDEPENDENT_INTERPRETER_PATH" value="true" />
|
||||
<option name="INTERPRETER_PATH" value="" />
|
||||
<option name="INTERPRETER_OPTIONS" value="" />
|
||||
<option name="EXECUTE_IN_TERMINAL" value="true" />
|
||||
<option name="EXECUTE_SCRIPT_FILE" value="false" />
|
||||
<envs />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
17
.run/Run watcher.run.xml
Normal file
17
.run/Run watcher.run.xml
Normal file
@@ -0,0 +1,17 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Run watcher" type="ShConfigurationType">
|
||||
<option name="SCRIPT_TEXT" value="bin/watcher" />
|
||||
<option name="INDEPENDENT_SCRIPT_PATH" value="true" />
|
||||
<option name="SCRIPT_PATH" value="" />
|
||||
<option name="SCRIPT_OPTIONS" value="" />
|
||||
<option name="INDEPENDENT_SCRIPT_WORKING_DIRECTORY" value="true" />
|
||||
<option name="SCRIPT_WORKING_DIRECTORY" value="$PROJECT_DIR$" />
|
||||
<option name="INDEPENDENT_INTERPRETER_PATH" value="true" />
|
||||
<option name="INTERPRETER_PATH" value="/bin/zsh" />
|
||||
<option name="INTERPRETER_OPTIONS" value="" />
|
||||
<option name="EXECUTE_IN_TERMINAL" value="true" />
|
||||
<option name="EXECUTE_SCRIPT_FILE" value="false" />
|
||||
<envs />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
17
.run/Stop Zhamao Server.run.xml
Normal file
17
.run/Stop Zhamao Server.run.xml
Normal file
@@ -0,0 +1,17 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Stop Zhamao Server" type="ShConfigurationType">
|
||||
<option name="SCRIPT_TEXT" value="./zhamao server:stop && exit" />
|
||||
<option name="INDEPENDENT_SCRIPT_PATH" value="true" />
|
||||
<option name="SCRIPT_PATH" value="" />
|
||||
<option name="SCRIPT_OPTIONS" value="" />
|
||||
<option name="INDEPENDENT_SCRIPT_WORKING_DIRECTORY" value="true" />
|
||||
<option name="SCRIPT_WORKING_DIRECTORY" value="$PROJECT_DIR$" />
|
||||
<option name="INDEPENDENT_INTERPRETER_PATH" value="true" />
|
||||
<option name="INTERPRETER_PATH" value="" />
|
||||
<option name="INTERPRETER_OPTIONS" value="" />
|
||||
<option name="EXECUTE_IN_TERMINAL" value="true" />
|
||||
<option name="EXECUTE_SCRIPT_FILE" value="false" />
|
||||
<envs />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
118
bin/watcher
Executable file
118
bin/watcher
Executable file
@@ -0,0 +1,118 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
_red="\e[33m"
|
||||
_green="\e[32m"
|
||||
_reset="\e[0m"
|
||||
|
||||
unix_s=$(uname -s)
|
||||
|
||||
unix_release=$(
|
||||
marked_release=""
|
||||
if [ "$unix_s" = "Linux" ]; then
|
||||
echo $HOME | grep com.termux >/dev/null
|
||||
if [ $? == 0 ]; then
|
||||
marked_release="termux"
|
||||
elif [ -f "/etc/redhat-release" ]; then
|
||||
if [ "$(cat /etc/redhat-release | awk '{print $1}' | grep -v '^$')" = "CentOS" ]; then
|
||||
marked_release="CentOS"
|
||||
else
|
||||
marked_release="unknown"
|
||||
fi
|
||||
elif [ -f "/etc/os-release" ]; then
|
||||
cat /etc/os-release | grep Alpine > /dev/null
|
||||
if [ $? == 0 ]; then
|
||||
marked_release="Alpine"
|
||||
fi
|
||||
fi
|
||||
if [ "$marked_release" = "" ]; then
|
||||
if [ -f "/etc/issue" ]; then
|
||||
marked_release=$(cat /etc/issue | grep -v '^$' | awk '{print $1}')
|
||||
else
|
||||
marked_release="unknown"
|
||||
fi
|
||||
fi
|
||||
elif [ "$unix_s" = "Darwin" ]; then
|
||||
marked_release=$(sw_vers | grep ProductName | awk '{print $2" "$3" "$4}')
|
||||
fi
|
||||
echo $marked_release
|
||||
)
|
||||
unix_release=$(echo $unix_release | xargs)
|
||||
|
||||
function echo_error() {
|
||||
echo -e "${_red}$1${_reset}"
|
||||
}
|
||||
|
||||
function echo_info() {
|
||||
echo -e "${_green}$1${_reset}"
|
||||
}
|
||||
|
||||
function install_test() {
|
||||
which fswatch >/dev/null
|
||||
if [ $? != 0 ]; then
|
||||
operate_confirm "fswatch还没有安装,是否确认安装?" && install_fswatch
|
||||
fi
|
||||
}
|
||||
function install_fswatch() {
|
||||
if [ "$unix_s" = "Linux" ]; then
|
||||
case $unix_release in
|
||||
"Kali" | "Ubuntu" | "Debian" | "Raspbian" | 'Pop!_OS')
|
||||
sudo apt-get install fswatch -y ;;
|
||||
#"termux") pkg install $1 -y ;;
|
||||
"CentOS")
|
||||
curl -o fswatch.tgz https://download.fastgit.org/emcrisostomo/fswatch/releases/download/1.16.0/fswatch-1.16.0.tar.gz && \
|
||||
tar -xzf fswatch.tgz && \
|
||||
cd fswatch-1.16.0 && \
|
||||
./configure && \
|
||||
make && \
|
||||
sudo make install && \
|
||||
cd .. && \
|
||||
rm -rf fswatch.tgz fswatch-1.16.0
|
||||
;;
|
||||
#"Alpine") apk add $1 ;;
|
||||
*)
|
||||
echo_error "不支持的 Linux 发行版:$unix_release"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
elif [ "$unix_s" = "Darwin" ]; then
|
||||
brew install fswatch
|
||||
else
|
||||
echo_error "不支持的操作系统:$unix_s"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
function operate_confirm() {
|
||||
echo -n $(echo_info "$1 [Y/n] ")
|
||||
read operate
|
||||
operate=$(echo $operate | tr A-Z a-z)
|
||||
if [[ "$operate" = "y" || "$operate" = "" ]]; then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
echo_info "当前系统:$unix_release"
|
||||
install_test
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
else
|
||||
echo_info "程序路径:$(which fswatch)"
|
||||
fi
|
||||
watch_dir="./src"
|
||||
if [ ! -d "$watch_dir" ]; then
|
||||
echo_error "src目录不存在!"
|
||||
exit 1
|
||||
else
|
||||
echo_info "监听目录:$watch_dir"
|
||||
fi
|
||||
_pid=$(cat .daemon_pid | awk -F"\"pid\": " '{print $2}' | grep -v ^$ | sed 's/,//g')
|
||||
if [ "$_pid" = "" ]; then
|
||||
echo_error "未检测到框架进程"
|
||||
exit 1
|
||||
fi
|
||||
fswatch $watch_dir | while read file
|
||||
do
|
||||
echo "Detect file change: $file"
|
||||
kill -USR1 $_pid
|
||||
done
|
||||
@@ -17,7 +17,8 @@
|
||||
"prefer-stable": true,
|
||||
"bin": [
|
||||
"bin/start",
|
||||
"bin/phpunit-swoole"
|
||||
"bin/phpunit-swoole",
|
||||
"bin/watcher"
|
||||
],
|
||||
"require": {
|
||||
"php": ">=7.2",
|
||||
@@ -52,6 +53,12 @@
|
||||
"src/ZM/global_functions.php"
|
||||
]
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Module\\": "src/Module",
|
||||
"Custom\\": "src/Custom"
|
||||
}
|
||||
},
|
||||
"require-dev": {
|
||||
"swoole/ide-helper": "@dev",
|
||||
"phpunit/phpunit": "^8.5 || ^9.0"
|
||||
|
||||
@@ -41,7 +41,8 @@ $config['runtime'] = [
|
||||
'swoole_server_mode' => SWOOLE_PROCESS,
|
||||
'middleware_error_policy' => 1,
|
||||
'reload_delay_time' => 800,
|
||||
'global_middleware_binding' => []
|
||||
'global_middleware_binding' => [],
|
||||
'save_console_log_file' => false, // 改为目标路径,则将 Console 输出的日志保存到文件
|
||||
];
|
||||
|
||||
/** 轻量字符串缓存,默认开启 */
|
||||
|
||||
@@ -769,6 +769,21 @@ vardump($result["retcode"]); //如果成功撤回,输出 int(0)
|
||||
|
||||
响应数据:无
|
||||
|
||||
### getExtendedAPI()
|
||||
|
||||
用来调用 OneBot 标准之外扩展出来的自定义 API。与下方 `callExtendedAPI` 不同的是,为了方便用户使用,炸毛框架内置了热门使用并且相对稳定的机器人客户端的专有 API。
|
||||
|
||||
目前内置了 `go-cqhttp` 频道相关的扩充 API。
|
||||
|
||||
使用示例:`getExtendedAPI('go-cqhttp')->getGuildList()`
|
||||
使用示例2:`getExtendedAPI()->sendGuildChannelMsg($guild_id, $channel_id, '频道的消息')`
|
||||
|
||||
唯一一个参数做保留,用于选择不同客户端,目前仅支持 `go-cqhttp`,所以缺省也默认为 `go-cqhttp`。
|
||||
|
||||
!!! warning "注意"
|
||||
|
||||
由于不同版本的扩展 API 变化可能会很大,改动较多,炸毛框架不会将对应扩展方法写入文档,具体调用情况可根据 IDE 自动补全中的文档或对应类的注释查看。
|
||||
|
||||
### callExtendedAPI() (扩充 API)
|
||||
|
||||
用来调用 OneBot 标准之外扩展出来的自定义 API。
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
此类管理的是 TaskWorker 相关工作。有关使用 TaskWorker 的教程,见 [进阶 - 使用 TaskWorker 进程处理密集运算](/advanced/task-worker)
|
||||
|
||||
类定义:`\ZM\Utils\TaskManager`
|
||||
类定义:`\ZM\Utils\Manager\TaskManager`
|
||||
|
||||
使用 TaskWorker 需要先在 `global.php` 配置文件中开启!
|
||||
|
||||
|
||||
@@ -8,6 +8,10 @@ DataProvider 是框架内提供的一个简易的文件管理类。
|
||||
|
||||
同 `working_dir()`。
|
||||
|
||||
## DataProvider::getSourceRootDir()
|
||||
|
||||
获取用户的源码根目录,除 Phar 模式外与 `getWorkingDir()` 相同。
|
||||
|
||||
## DataProvider::getFrameworkLink()
|
||||
|
||||
同 `ZMConfig::get("global", "http_reverse_link")`,获取反向代理的链接。
|
||||
@@ -46,6 +50,45 @@ DataProvider::getDataFolder("TestModule"); // 例如返回 /root/zhamao-framewor
|
||||
|
||||
文件名同上 `saveToJson()` 的定义,解析后的返回值为原先的内容或 `null`(如果文件不存在或 json 解析失败)。
|
||||
|
||||
## DataProvider::scanDirFiles()
|
||||
|
||||
递归或非递归扫描目录,返回相对目录的文件列表或绝对目录的文件列表。(非常好用)
|
||||
|
||||
定义:`scanDirFiles($dir, $recursive = true, $relative = false)`
|
||||
|
||||
`$dir` 为要扫描的目录,`$recursive` 为是否递归,`$relative` 为是否返回相对目录的文件列表。
|
||||
|
||||
从给定的目录下开始遍历整个目录,如果将 `$recursive` 设置为 `true`,则会递归扫描子目录,否则将返回包含目录的文件列表。
|
||||
|
||||
如果将 `$relative` 设置为 `true`,则会返回文件列表的相对路径,否则返回绝对路径。
|
||||
|
||||
例如:假设目录 `/home/test/` 下有两个文件:`test1.txt` 和 `testdir/test2.txt`:如果将 `$recursive` 设置为 `true`,`$relative` 设置为 `false`,则返回的文件列表为:
|
||||
|
||||
```json
|
||||
[
|
||||
"/home/test/test1.txt",
|
||||
"home/test/testdir/test2.txt"
|
||||
]
|
||||
```
|
||||
|
||||
相同条件下,如果将 `$relative` 设置为 `true`:
|
||||
|
||||
```json
|
||||
[
|
||||
"test1.txt",
|
||||
"testdir/test2.txt"
|
||||
]
|
||||
```
|
||||
|
||||
如果再把 `$recursive` 设置为 `false`:
|
||||
|
||||
```json
|
||||
[
|
||||
"test1.txt",
|
||||
"testdir"
|
||||
]
|
||||
```
|
||||
|
||||
## 其他文件读取
|
||||
|
||||
框架比较贴近原生的 PHP,所以推荐直接使用原生的方法来读写文件(`file_get_contents` 和 `file_put_contents`)。但有一点要注意,框架内最好使用**工作目录或者绝对路径**。
|
||||
|
||||
@@ -6,9 +6,18 @@
|
||||
|
||||
但是如果因为用户的误操作,导致炸毛框架其中的一个或多个进程阻塞,或者比如将框架挂在 screen 等守护但是守护服务进程被杀掉,总之就是无法使用 Ctrl+C 的方式正常关闭框架,这时就需要正确地杀掉所有框架进程(这固然可能会造成内存的缓存数据丢失)。
|
||||
|
||||
### v2.7.0 及以上版本教程
|
||||
|
||||
- 安全关框架指令:`./zhamao server:stop`
|
||||
- 万能杀死所有框架进程指令:`./zhamao server:stop --force`
|
||||
- 监视框架是否在运行:`./zhamao server:status`
|
||||
- Worker 进程卡死:连续按 5 次 Ctrl+C 即可强行杀掉所有进程(SIGKILL)
|
||||
|
||||
### v2.6.6 及以下版本教程
|
||||
|
||||
!!! warning "注意"
|
||||
|
||||
下方涉及 `ps` 命令后使用 `grep` 过滤的框架进程方式,如果你的服务器同时有其他使用 PHP 启动的服务,命令行刚好有 `server` 字样,可能会导致误杀,如果有影响的话,建议将 `grep server` 换成你启动时命令行的特殊参数或手动排除!
|
||||
下方涉及 `ps` 命令后使用 `grep` 过滤的框架进程方式,如果你的服务器同时有其他使用 PHP 启动的服务,命令行刚好有 `server` 字样,可能会导致误杀,如果有影响的话,建议将 `grep server` 换成你启动时命令行的特殊参数或手动排除!
|
||||
|
||||
**一、**首先,使用 `ps`、`htop`、`netstat -nlp` 等命令确定框架的入口进程(也就是 Master 进程的 pid)。
|
||||
|
||||
|
||||
@@ -58,6 +58,7 @@
|
||||
| `middleware_error_policy` | 中间件错误处理策略,见 [中间件 - 错误处理策略](../../event/middleware/#_6) | 1 |
|
||||
| `reload_delay_time` | 框架 reload 重载命令接收后延迟的时间(毫秒,0 为不等待) | 800 |
|
||||
| `global_middleware_binding` | 给注解事件绑定全局中间件,见 [中间件 - 全局中间件](../../event/middleware/#_6) | `[]` |
|
||||
| `save_console_log_file` | 当这里输入字符串路径时,所有 `Console::xxx()` 输出的日志都将保存到目标文件 | false |
|
||||
|
||||
### 子表 **light_cache**
|
||||
|
||||
|
||||
@@ -70,5 +70,6 @@
|
||||
| E00068 | 模块解包时无法正常拷贝文件 | 检查文件夹是否正常可以创建和写入。 |
|
||||
| E00069 | 框架不能启动两个 ConsoleApplication 实例 | 不要重复使用 `new ConsoleApplication()`。 |
|
||||
| E00070 | 框架找不到 `zm_data` 目录 | 检查配置中指定的 `zm_data` 目录是否存在。 |
|
||||
| E00071 | 框架找不到对应类型的 API 调用类 | 检查 `getExtendedAPI($name)` 传入的 `$name` 是否正确 |
|
||||
| E99999 | 未知错误 | |
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# 介绍
|
||||
|
||||
> 本文档为炸毛框架 v2 版本,如需查看 v1 版本,[点我](https://docs-v1.zhamao.xin/)。
|
||||
|
||||
!!! tip "提示"
|
||||
|
||||
编写文档需要较大精力,你也可以参与到本文档的建设中来,比如找错字,增加或更正内容,每页文档可直接点击右上方铅笔图标直接跳转至 GitHub 进行编辑,编辑后自动 Fork 并生成 Pull Request,以此来贡献此文档!
|
||||
|
||||
@@ -4,6 +4,31 @@
|
||||
|
||||
同时此处将只使用 build 版本号进行区分。
|
||||
|
||||
## build 434 (2022-1-8)
|
||||
|
||||
- 修复框架在 PHP 8.1 下运行时的一些问题
|
||||
- 新增 Console 日志输出到文件的功能
|
||||
|
||||
## build 433 (2021-12-28)
|
||||
|
||||
- 修复 OneBotV11 因 IDE 自动优化导致 API 接口发生变化的问题
|
||||
|
||||
## build 432 (2021-12-25)
|
||||
|
||||
- 新增 GoCqhttpAPI 包,用于支持额外的 OneBot Action(API)
|
||||
- 修复 MySQL 查询器中 `fetchOne()` 方法无法正确返回值的 Bug
|
||||
- 修复 Swoole Hook 因配置不当无法正确使用的 Bug
|
||||
|
||||
## build 431 (2021-12-22)
|
||||
|
||||
- 修复 Issue #50
|
||||
- 新增 PhpStorm IDE 直接运行框架的脚本
|
||||
|
||||
## build 430 (2021-12-8)
|
||||
|
||||
- 删除调试信息
|
||||
- 修复 #56 中关于数据库组件的 Bug
|
||||
|
||||
## build 429 (2021-12-7)
|
||||
|
||||
- 新增配置项 `onebot`.`message_command_policy`
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
|
||||
这里将会记录各个主版本的框架升级后,涉及 `global.php` 的更新日志,你可以根据这里描述的内容与你的旧配置文件进行合并。
|
||||
|
||||
## v2.6.6 (build 434)
|
||||
|
||||
- 新增 `$config['runtime']` 下的 `save_console_log_file` 项。
|
||||
|
||||
## v2.6.0 (build 427)
|
||||
|
||||
- 新增 `$config['runtime']` 下的 `reload_delay_time`、`global_middleware_binding` 项。
|
||||
|
||||
@@ -1,5 +1,35 @@
|
||||
# 更新日志(v2 版本)
|
||||
|
||||
## v2.6.6(build 434)
|
||||
|
||||
> 更新时间:2022.1.8
|
||||
|
||||
- 修复框架在 PHP 8.1 下运行时的一些问题
|
||||
- 新增 Console 日志输出到文件的功能
|
||||
|
||||
## v2.6.5(build 433)
|
||||
|
||||
> 更新时间:2021.12.28
|
||||
|
||||
- 修复 OneBotV11 因 IDE 自动优化导致 API 接口发生变化的问题
|
||||
|
||||
## v2.6.4(build 432)
|
||||
|
||||
> 更新时间:2021.12.25
|
||||
|
||||
- 新增 GoCqhttpAPI 包,用于支持额外的 OneBot Action(API)
|
||||
- 修复 MySQL 查询器中 `fetchOne()` 方法无法正确返回值的 Bug
|
||||
- 修复 Swoole Hook 因配置不当无法正确使用的 Bug
|
||||
- 修复 Issue #50
|
||||
- 新增 PhpStorm IDE 直接运行框架的脚本
|
||||
|
||||
## v2.6.3 (build 430)
|
||||
|
||||
> 更新时间:2021.12.8
|
||||
|
||||
- 删除调试信息
|
||||
- 修复 #56 中关于数据库组件的 Bug
|
||||
|
||||
## v2.6.2 (build 429)
|
||||
|
||||
> 更新时间:2021.12.7
|
||||
|
||||
2
install-runtime.sh
Normal file → Executable file
2
install-runtime.sh
Normal file → Executable file
@@ -55,4 +55,4 @@ if [ $? -ne 0 ]; then
|
||||
fi
|
||||
echo "成功下载!" && \
|
||||
echo -e "PHP使用:\truntime/php -v" && \
|
||||
echo -e "Composer使用:\truntime/composer"
|
||||
echo -e "Composer使用:\truntime/composer"
|
||||
|
||||
@@ -106,6 +106,7 @@ nav:
|
||||
- Console 终端: component/common/console.md
|
||||
- TaskWorker 管理: component/common/task-worker.md
|
||||
- Terminal 终端: component/common/remote-terminal.md
|
||||
- EventTracer 事件追踪器: component/comon/event-tracer.md
|
||||
- HTTP 服务器工具类:
|
||||
- HTTP 和 WebSocket 客户端: component/http/zmrequest.md
|
||||
- HTTP 路由管理: component/http/route-manager.md
|
||||
|
||||
@@ -80,6 +80,12 @@ trait CQAPI
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public function __call($name, $arguments) {
|
||||
return false;
|
||||
}
|
||||
|
||||
111
src/ZM/API/GoCqhttpAPIV11.php
Normal file
111
src/ZM/API/GoCqhttpAPIV11.php
Normal file
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
|
||||
namespace ZM\API;
|
||||
|
||||
class GoCqhttpAPIV11
|
||||
{
|
||||
const SUPPORT_VERSION = '1.0.0-beta8';
|
||||
|
||||
use CQAPI;
|
||||
|
||||
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
|
||||
* @link 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
|
||||
*/
|
||||
public function getGuildServiceProfile()
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__)
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取频道列表
|
||||
* @link 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
|
||||
*/
|
||||
public function getGuildList() {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__)
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过访客获取频道元数据
|
||||
* @link 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 $guild_id
|
||||
* @return array|bool
|
||||
*/
|
||||
public function getGuildMetaByGuest($guild_id) {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
||||
'params' => [
|
||||
'guild_id' => $guild_id
|
||||
]
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取子频道列表
|
||||
* @link 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 $guild_id
|
||||
* @param false $no_cache
|
||||
* @return array|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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取频道成员列表
|
||||
* @link 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 $guild_id
|
||||
* @return array|bool
|
||||
*/
|
||||
public function getGuildMembers($guild_id) {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
||||
'params' => [
|
||||
'guild_id' => $guild_id
|
||||
]
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送信息到子频道
|
||||
* @link 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 $guild_id
|
||||
* @param $channel_id
|
||||
* @param $message
|
||||
* @return array|bool
|
||||
*/
|
||||
public function sendGuildChannelMsg($guild_id, $channel_id, $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,4 +1,4 @@
|
||||
<?php
|
||||
<?php /** @noinspection PhpUnused */
|
||||
|
||||
|
||||
namespace ZM\API;
|
||||
@@ -6,6 +6,7 @@ namespace ZM\API;
|
||||
use ZM\ConnectionManager\ConnectionObject;
|
||||
use ZM\ConnectionManager\ManagerGM;
|
||||
use ZM\Exception\RobotNotFoundException;
|
||||
use ZM\Exception\ZMKnownException;
|
||||
|
||||
/**
|
||||
* OneBot V11 的 API 实现类
|
||||
@@ -22,17 +23,18 @@ class OneBotV11
|
||||
const API_RATE_LIMITED = 2;
|
||||
|
||||
/** @var ConnectionObject|null */
|
||||
private $connection;
|
||||
protected $connection;
|
||||
|
||||
private $callback = true;
|
||||
private $prefix = 0;
|
||||
protected $callback = true;
|
||||
protected $prefix = 0;
|
||||
|
||||
/**
|
||||
* @param $robot_id
|
||||
* @return ZMRobot
|
||||
* @throws RobotNotFoundException
|
||||
*/
|
||||
public static function get($robot_id) {
|
||||
public static function get($robot_id)
|
||||
{
|
||||
$r = ManagerGM::getAllByName('qq');
|
||||
foreach ($r as $v) {
|
||||
if ($v->getOption('connect_id') == $robot_id) return new ZMRobot($v);
|
||||
@@ -44,7 +46,8 @@ class OneBotV11
|
||||
* @return ZMRobot
|
||||
* @throws RobotNotFoundException
|
||||
*/
|
||||
public static function getRandom() {
|
||||
public static function getRandom()
|
||||
{
|
||||
$r = ManagerGM::getAllByName('qq');
|
||||
if ($r == []) throw new RobotNotFoundException("没有任何机器人连接到框架!");
|
||||
return new ZMRobot($r[array_rand($r)]);
|
||||
@@ -53,7 +56,8 @@ class OneBotV11
|
||||
/**
|
||||
* @return ZMRobot[]
|
||||
*/
|
||||
public static function getAllRobot() {
|
||||
public static function getAllRobot()
|
||||
{
|
||||
$r = ManagerGM::getAllByName('qq');
|
||||
$obj = [];
|
||||
foreach ($r as $v) {
|
||||
@@ -62,21 +66,25 @@ class OneBotV11
|
||||
return $obj;
|
||||
}
|
||||
|
||||
public function __construct(ConnectionObject $connection) {
|
||||
public function __construct(ConnectionObject $connection)
|
||||
{
|
||||
$this->connection = $connection;
|
||||
}
|
||||
|
||||
public function setCallback($callback = true) {
|
||||
public function setCallback($callback = true)
|
||||
{
|
||||
$this->callback = $callback;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setPrefix($prefix = self::API_NORMAL) {
|
||||
public function setPrefix($prefix = self::API_NORMAL)
|
||||
{
|
||||
$this->prefix = $prefix;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSelfId() {
|
||||
public function getSelfId()
|
||||
{
|
||||
return $this->connection->getOption('connect_id');
|
||||
}
|
||||
|
||||
@@ -90,9 +98,10 @@ class OneBotV11
|
||||
* @param bool $auto_escape
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function sendPrivateMsg($user_id, $message, $auto_escape = false) {
|
||||
public function sendPrivateMsg($user_id, $message, bool $auto_escape = false)
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
||||
'params' => [
|
||||
'user_id' => $user_id,
|
||||
'message' => $message,
|
||||
@@ -109,9 +118,10 @@ class OneBotV11
|
||||
* @param bool $auto_escape
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function sendGroupMsg($group_id, $message, $auto_escape = false) {
|
||||
public function sendGroupMsg($group_id, $message, bool $auto_escape = false)
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
||||
'params' => [
|
||||
'group_id' => $group_id,
|
||||
'message' => $message,
|
||||
@@ -129,9 +139,10 @@ class OneBotV11
|
||||
* @param bool $auto_escape
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function sendMsg($message_type, $target_id, $message, $auto_escape = false) {
|
||||
public function sendMsg($message_type, $target_id, $message, bool $auto_escape = false)
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
||||
'params' => [
|
||||
'message_type' => $message_type,
|
||||
($message_type == 'private' ? 'user' : $message_type) . '_id' => $target_id,
|
||||
@@ -147,9 +158,10 @@ class OneBotV11
|
||||
* @param $message_id
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function deleteMsg($message_id) {
|
||||
public function deleteMsg($message_id)
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
||||
'params' => [
|
||||
'message_id' => $message_id
|
||||
]
|
||||
@@ -162,9 +174,10 @@ class OneBotV11
|
||||
* @param $message_id
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function getMsg($message_id) {
|
||||
public function getMsg($message_id)
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
||||
'params' => [
|
||||
'message_id' => $message_id
|
||||
]
|
||||
@@ -177,9 +190,10 @@ class OneBotV11
|
||||
* @param $id
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function getForwardMsg($id) {
|
||||
public function getForwardMsg($id)
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
||||
'params' => [
|
||||
'id' => $id
|
||||
]
|
||||
@@ -193,9 +207,10 @@ class OneBotV11
|
||||
* @param int $times
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function sendLike($user_id, $times = 1) {
|
||||
public function sendLike($user_id, int $times = 1)
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
||||
'params' => [
|
||||
'user_id' => $user_id,
|
||||
'times' => $times
|
||||
@@ -211,9 +226,10 @@ class OneBotV11
|
||||
* @param bool $reject_add_request
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function setGroupKick($group_id, $user_id, $reject_add_request = false) {
|
||||
public function setGroupKick($group_id, $user_id, bool $reject_add_request = false)
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
||||
'params' => [
|
||||
'group_id' => $group_id,
|
||||
'user_id' => $user_id,
|
||||
@@ -227,12 +243,13 @@ class OneBotV11
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/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 $group_id
|
||||
* @param $user_id
|
||||
* @param $duration
|
||||
* @param int $duration
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function setGroupBan($group_id, $user_id, $duration = 1800) {
|
||||
public function setGroupBan($group_id, $user_id, int $duration = 1800)
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
||||
'params' => [
|
||||
'group_id' => $group_id,
|
||||
'user_id' => $user_id,
|
||||
@@ -249,9 +266,10 @@ class OneBotV11
|
||||
* @param int $duration
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function setGroupAnonymousBan($group_id, $anonymous_or_flag, $duration = 1800) {
|
||||
public function setGroupAnonymousBan($group_id, $anonymous_or_flag, int $duration = 1800)
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
||||
'params' => [
|
||||
'group_id' => $group_id,
|
||||
(is_string($anonymous_or_flag) ? 'flag' : 'anonymous') => $anonymous_or_flag,
|
||||
@@ -267,9 +285,10 @@ class OneBotV11
|
||||
* @param bool $enable
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function setGroupWholeBan($group_id, $enable = true) {
|
||||
public function setGroupWholeBan($group_id, bool $enable = true)
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
||||
'params' => [
|
||||
'group_id' => $group_id,
|
||||
'enable' => $enable
|
||||
@@ -285,9 +304,10 @@ class OneBotV11
|
||||
* @param bool $enable
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function setGroupAdmin($group_id, $user_id, $enable = true) {
|
||||
public function setGroupAdmin($group_id, $user_id, bool $enable = true)
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
||||
'params' => [
|
||||
'group_id' => $group_id,
|
||||
'user_id' => $user_id,
|
||||
@@ -303,9 +323,10 @@ class OneBotV11
|
||||
* @param bool $enable
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function setGroupAnonymous($group_id, $enable = true) {
|
||||
public function setGroupAnonymous($group_id, bool $enable = true)
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
||||
'params' => [
|
||||
'group_id' => $group_id,
|
||||
'enable' => $enable
|
||||
@@ -321,9 +342,10 @@ class OneBotV11
|
||||
* @param string $card
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function setGroupCard($group_id, $user_id, $card = "") {
|
||||
public function setGroupCard($group_id, $user_id, string $card = '')
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
||||
'params' => [
|
||||
'group_id' => $group_id,
|
||||
'user_id' => $user_id,
|
||||
@@ -339,9 +361,10 @@ class OneBotV11
|
||||
* @param $group_name
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function setGroupName($group_id, $group_name) {
|
||||
public function setGroupName($group_id, $group_name)
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
||||
'params' => [
|
||||
'group_id' => $group_id,
|
||||
'group_name' => $group_name
|
||||
@@ -356,9 +379,10 @@ class OneBotV11
|
||||
* @param bool $is_dismiss
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function setGroupLeave($group_id, $is_dismiss = false) {
|
||||
public function setGroupLeave($group_id, bool $is_dismiss = false)
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
||||
'params' => [
|
||||
'group_id' => $group_id,
|
||||
'is_dismiss' => $is_dismiss
|
||||
@@ -375,9 +399,10 @@ class OneBotV11
|
||||
* @param int $duration
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function setGroupSpecialTitle($group_id, $user_id, $special_title = "", $duration = -1) {
|
||||
public function setGroupSpecialTitle($group_id, $user_id, string $special_title = "", int $duration = -1)
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
||||
'params' => [
|
||||
'group_id' => $group_id,
|
||||
'user_id' => $user_id,
|
||||
@@ -395,9 +420,10 @@ class OneBotV11
|
||||
* @param string $remark
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function setFriendAddRequest($flag, $approve = true, $remark = "") {
|
||||
public function setFriendAddRequest($flag, bool $approve = true, string $remark = "")
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
||||
'params' => [
|
||||
'flag' => $flag,
|
||||
'approve' => $approve,
|
||||
@@ -415,9 +441,10 @@ class OneBotV11
|
||||
* @param string $reason
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function setGroupAddRequest($flag, $sub_type, $approve = true, $reason = "") {
|
||||
public function setGroupAddRequest($flag, $sub_type, bool $approve = true, string $reason = "")
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
||||
'params' => [
|
||||
'flag' => $flag,
|
||||
'sub_type' => $sub_type,
|
||||
@@ -432,8 +459,9 @@ class OneBotV11
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/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|null
|
||||
*/
|
||||
public function getLoginInfo() {
|
||||
return $this->processAPI($this->connection, ['action' => $this->getActionName(__FUNCTION__)], $this->callback);
|
||||
public function getLoginInfo()
|
||||
{
|
||||
return $this->processAPI($this->connection, ['action' => $this->getActionName($this->prefix, __FUNCTION__)], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -443,9 +471,10 @@ class OneBotV11
|
||||
* @param bool $no_cache
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function getStrangerInfo($user_id, $no_cache = false) {
|
||||
public function getStrangerInfo($user_id, bool $no_cache = false)
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
||||
'params' => [
|
||||
'user_id' => $user_id,
|
||||
'no_cache' => $no_cache
|
||||
@@ -458,9 +487,10 @@ class OneBotV11
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/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|null
|
||||
*/
|
||||
public function getFriendList() {
|
||||
public function getFriendList()
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__)
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__)
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
@@ -471,9 +501,10 @@ class OneBotV11
|
||||
* @param bool $no_cache
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function getGroupInfo($group_id, $no_cache = false) {
|
||||
public function getGroupInfo($group_id, bool $no_cache = false)
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
||||
'params' => [
|
||||
'group_id' => $group_id,
|
||||
'no_cache' => $no_cache
|
||||
@@ -486,9 +517,10 @@ class OneBotV11
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_group_list-%E8%8E%B7%E5%8F%96%E7%BE%A4%E5%88%97%E8%A1%A8
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function getGroupList() {
|
||||
public function getGroupList()
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__)
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__)
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
@@ -500,9 +532,10 @@ class OneBotV11
|
||||
* @param bool $no_cache
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function getGroupMemberInfo($group_id, $user_id, $no_cache = false) {
|
||||
public function getGroupMemberInfo($group_id, $user_id, bool $no_cache = false)
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
||||
'params' => [
|
||||
'group_id' => $group_id,
|
||||
'user_id' => $user_id,
|
||||
@@ -517,9 +550,10 @@ class OneBotV11
|
||||
* @param $group_id
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function getGroupMemberList($group_id) {
|
||||
public function getGroupMemberList($group_id)
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
||||
'params' => [
|
||||
'group_id' => $group_id
|
||||
]
|
||||
@@ -533,9 +567,10 @@ class OneBotV11
|
||||
* @param $type
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function getGroupHonorInfo($group_id, $type) {
|
||||
public function getGroupHonorInfo($group_id, $type)
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
||||
'params' => [
|
||||
'group_id' => $group_id,
|
||||
'type' => $type
|
||||
@@ -548,9 +583,10 @@ class OneBotV11
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_csrf_token-%E8%8E%B7%E5%8F%96-csrf-token
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function getCsrfToken() {
|
||||
public function getCsrfToken()
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__)
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__)
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
@@ -560,9 +596,10 @@ class OneBotV11
|
||||
* @param string $domain
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function getCredentials($domain = "") {
|
||||
public function getCredentials(string $domain = "")
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
||||
'params' => [
|
||||
'domain' => $domain
|
||||
]
|
||||
@@ -576,9 +613,10 @@ class OneBotV11
|
||||
* @param $out_format
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function getRecord($file, $out_format) {
|
||||
public function getRecord($file, $out_format)
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
||||
'params' => [
|
||||
'file' => $file,
|
||||
'out_format' => $out_format
|
||||
@@ -592,9 +630,10 @@ class OneBotV11
|
||||
* @param $file
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function getImage($file) {
|
||||
public function getImage($file)
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
||||
'params' => [
|
||||
'file' => $file
|
||||
]
|
||||
@@ -606,9 +645,10 @@ class OneBotV11
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/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|null
|
||||
*/
|
||||
public function canSendImage() {
|
||||
public function canSendImage()
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__)
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__)
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
@@ -617,9 +657,10 @@ class OneBotV11
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/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|null
|
||||
*/
|
||||
public function canSendRecord() {
|
||||
public function canSendRecord()
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__)
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__)
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
@@ -628,9 +669,10 @@ class OneBotV11
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/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|null
|
||||
*/
|
||||
public function getStatus() {
|
||||
public function getStatus()
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__)
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__)
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
@@ -639,9 +681,10 @@ class OneBotV11
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/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|null
|
||||
*/
|
||||
public function getVersionInfo() {
|
||||
public function getVersionInfo()
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__)
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__)
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
@@ -651,9 +694,10 @@ class OneBotV11
|
||||
* @param int $delay
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function setRestart($delay = 0) {
|
||||
public function setRestart(int $delay = 0)
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
||||
'params' => [
|
||||
'delay' => $delay
|
||||
]
|
||||
@@ -665,22 +709,37 @@ class OneBotV11
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#clean_cache-%E6%B8%85%E7%90%86%E7%BC%93%E5%AD%98
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function cleanCache() {
|
||||
public function cleanCache()
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__)
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__)
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
public function callExtendedAPI($action, $params = []) {
|
||||
/**
|
||||
* 获取内置支持的扩展API对象
|
||||
* 现支持 go-cqhttp 的扩展API
|
||||
* @param string $package_name
|
||||
* @return mixed
|
||||
* @throws ZMKnownException
|
||||
*/
|
||||
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);
|
||||
} else {
|
||||
throw new ZMKnownException(zm_internal_errcode('E00071'), '无法找到对应的调用扩展类');
|
||||
}
|
||||
}
|
||||
|
||||
public function callExtendedAPI($action, $params = [])
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $action,
|
||||
'params' => $params
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
private function getActionName(string $method) {
|
||||
$prefix = ($this->prefix == self::API_ASYNC ? '_async' : ($this->prefix == self::API_RATE_LIMITED ? '_rate_limited' : ''));
|
||||
$func_name = strtolower(preg_replace('/(?<=[a-z])([A-Z])/', '_$1', $method));
|
||||
return $func_name . $prefix;
|
||||
}
|
||||
}
|
||||
@@ -7,22 +7,17 @@ namespace ZM\Command\Daemon;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use ZM\Utils\DataProvider;
|
||||
use ZM\Framework;
|
||||
|
||||
abstract class DaemonCommand extends Command
|
||||
{
|
||||
protected $daemon_file = null;
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int {
|
||||
$pid_path = DataProvider::getWorkingDir() . "/.daemon_pid";
|
||||
if (!file_exists($pid_path)) {
|
||||
$output->writeln("<comment>没有检测到正在运行的守护进程或框架进程!</comment>");
|
||||
die();
|
||||
}
|
||||
$file = json_decode(file_get_contents($pid_path), true);
|
||||
if ($file === null || posix_getsid(intval($file["pid"])) === false) {
|
||||
$file = Framework::getProcessState(ZM_PROCESS_MASTER);
|
||||
if ($file === false || posix_getsid(intval($file["pid"])) === false) {
|
||||
$output->writeln("<comment>未检测到正在运行的守护进程或框架进程!</comment>");
|
||||
unlink($pid_path);
|
||||
Framework::removeProcessState(ZM_PROCESS_MASTER);
|
||||
die();
|
||||
}
|
||||
$this->daemon_file = $file;
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace ZM\Command\Daemon;
|
||||
use Swoole\Process;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use ZM\Utils\DataProvider;
|
||||
use ZM\Framework;
|
||||
|
||||
class DaemonStopCommand extends DaemonCommand
|
||||
{
|
||||
@@ -20,7 +20,7 @@ class DaemonStopCommand extends DaemonCommand
|
||||
parent::execute($input, $output);
|
||||
Process::kill(intval($this->daemon_file["pid"]), SIGTERM);
|
||||
$i = 10;
|
||||
while (file_exists(DataProvider::getWorkingDir() . "/.daemon_pid") && $i > 0) {
|
||||
while (Framework::getProcessState(ZM_PROCESS_MASTER) !== false && $i > 0) {
|
||||
sleep(1);
|
||||
--$i;
|
||||
}
|
||||
|
||||
@@ -36,7 +36,8 @@ class RunServerCommand extends Command
|
||||
new InputOption("env", null, InputOption::VALUE_REQUIRED, "设置环境类型 (production, development, staging)"),
|
||||
new InputOption("disable-safe-exit", null, null, "关闭安全退出(关闭后按CtrlC时直接杀死进程)"),
|
||||
new InputOption("preview", null, null, "只显示参数,不启动服务器"),
|
||||
new InputOption("force-load-module", null, InputOption::VALUE_OPTIONAL, "强制打包状态下加载模块(使用英文逗号分割多个)")
|
||||
new InputOption("force-load-module", null, InputOption::VALUE_OPTIONAL, "强制打包状态下加载模块(使用英文逗号分割多个)"),
|
||||
new InputOption("polling-watch", null, null, "强制启用轮询模式监听"),
|
||||
]);
|
||||
$this->setDescription("Run zhamao-framework | 启动框架");
|
||||
$this->setHelp("直接运行可以启动");
|
||||
@@ -49,16 +50,11 @@ class RunServerCommand extends Command
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
$pid_path = DataProvider::getWorkingDir() . "/.daemon_pid";
|
||||
if (file_exists($pid_path)) {
|
||||
$pid = json_decode(file_get_contents($pid_path), true)["pid"] ?? null;
|
||||
if ($pid !== null && posix_getsid($pid) !== false) {
|
||||
$output->writeln("<error>检测到已经在 pid: $pid 进程启动了框架!</error>");
|
||||
$output->writeln("<error>不可以同时启动两个框架!</error>");
|
||||
return 1;
|
||||
} else {
|
||||
unlink($pid_path);
|
||||
}
|
||||
$state = Framework::getProcessState(ZM_PROCESS_MASTER);
|
||||
if (is_array($state) && posix_getsid($state['pid'] ?? -1) !== false) {
|
||||
$output->writeln("<error>检测到已经在 pid: {$state['pid']} 进程启动了框架!</error>");
|
||||
$output->writeln("<error>不可以同时启动两个框架!</error>");
|
||||
return 1;
|
||||
}
|
||||
(new Framework($input->getOptions()))->start();
|
||||
return 0;
|
||||
|
||||
@@ -5,8 +5,10 @@ namespace ZM\Command\Server;
|
||||
|
||||
use Swoole\Process;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use ZM\Command\Daemon\DaemonCommand;
|
||||
use ZM\Framework;
|
||||
use ZM\Utils\DataProvider;
|
||||
|
||||
class ServerStopCommand extends DaemonCommand
|
||||
@@ -15,18 +17,38 @@ class ServerStopCommand extends DaemonCommand
|
||||
|
||||
protected function configure() {
|
||||
$this->setDescription("停止运行的框架");
|
||||
$this->setDefinition([
|
||||
new InputOption('force', 'f', InputOption::VALUE_NONE, '强制停止'),
|
||||
]);
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int {
|
||||
parent::execute($input, $output);
|
||||
if ($input->getOption('force') !== false) {
|
||||
$file_path = _zm_pid_dir();
|
||||
$list = DataProvider::scanDirFiles($file_path, false, true);
|
||||
foreach($list as $file) {
|
||||
$name = explode('.', $file);
|
||||
if (end($name) == 'pid') {
|
||||
$pid = file_get_contents($file_path.'/'.$file);
|
||||
Process::kill($pid, SIGKILL);
|
||||
} elseif ($file === 'master.json') {
|
||||
$json = json_decode(file_get_contents($file_path.'/'.$file), true);
|
||||
Process::kill($json['pid'], SIGKILL);
|
||||
}
|
||||
unlink($file_path.'/'.$file);
|
||||
}
|
||||
} else {
|
||||
parent::execute($input, $output);
|
||||
}
|
||||
Process::kill(intval($this->daemon_file["pid"]), SIGTERM);
|
||||
$i = 10;
|
||||
while (file_exists(DataProvider::getWorkingDir() . "/.daemon_pid") && $i > 0) {
|
||||
while (Framework::getProcessState(ZM_PROCESS_MASTER) !== false && $i > 0) {
|
||||
sleep(1);
|
||||
--$i;
|
||||
}
|
||||
if ($i === 0) {
|
||||
$output->writeln("<error>停止失败,请检查进程pid #" . $this->daemon_file["pid"] . " 是否响应!</error>");
|
||||
$output->writeln("<error>或者可以尝试使用参数 --force 来强行杀死所有进程</error>");
|
||||
} else {
|
||||
$output->writeln("<info>成功停止!</info>");
|
||||
}
|
||||
|
||||
@@ -30,8 +30,8 @@ class ConsoleApplication extends Application
|
||||
{
|
||||
private static $obj = null;
|
||||
|
||||
const VERSION_ID = 429;
|
||||
const VERSION = "2.6.2";
|
||||
const VERSION_ID = 435;
|
||||
const VERSION = "2.7.0-beta1";
|
||||
|
||||
/**
|
||||
* @throws InitException
|
||||
@@ -54,7 +54,20 @@ class ConsoleApplication extends Application
|
||||
|
||||
_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());
|
||||
@@ -65,28 +78,6 @@ class ConsoleApplication extends Application
|
||||
define("LOAD_MODE", is_dir(SOURCE_ROOT_DIR . "/src/ZM") ? 0 : 1);
|
||||
define("FRAMEWORK_ROOT_DIR", realpath(__DIR__ . "/../../"));
|
||||
}
|
||||
if (LOAD_MODE == 0) {
|
||||
$composer = json_decode(file_get_contents(SOURCE_ROOT_DIR . "/composer.json"), true);
|
||||
if (!isset($composer["autoload"]["psr-4"]["Module\\"])) {
|
||||
echo "框架源码模式需要在autoload文件中添加Module目录为自动加载,是否添加?[Y/n] ";
|
||||
$r = strtolower(trim(fgets(STDIN)));
|
||||
if ($r === "" || $r === "y") {
|
||||
$composer["autoload"]["psr-4"]["Module\\"] = "src/Module";
|
||||
$composer["autoload"]["psr-4"]["Custom\\"] = "src/Custom";
|
||||
$r = file_put_contents(WORKING_DIR . "/composer.json", json_encode($composer, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
|
||||
if ($r !== false) {
|
||||
echo "成功添加!请运行 'composer dump-autoload'\n";
|
||||
exit(0);
|
||||
} else {
|
||||
echo zm_internal_errcode("E00006") . "添加失败!请按任意键继续!";
|
||||
fgets(STDIN);
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->addCommands([
|
||||
new DaemonStatusCommand(),
|
||||
|
||||
@@ -17,6 +17,7 @@ use ZM\Event\SwooleEvent;
|
||||
use ZM\Framework;
|
||||
use ZM\Store\ZMBuf;
|
||||
use ZM\Utils\DataProvider;
|
||||
use ZM\Utils\Manager\ProcessManager;
|
||||
use ZM\Utils\SignalListener;
|
||||
use ZM\Utils\Terminal;
|
||||
use ZM\Utils\ZMUtil;
|
||||
@@ -28,15 +29,20 @@ use ZM\Utils\ZMUtil;
|
||||
*/
|
||||
class OnManagerStart implements SwooleEvent
|
||||
{
|
||||
/** @var null|Process */
|
||||
public static $process = null;
|
||||
private static $last_hash = "";
|
||||
private static $watch = -1;
|
||||
|
||||
public function onCall(Server $server) {
|
||||
Console::debug("Calling onManagerStart event(1)");
|
||||
if (!Framework::$argv["disable-safe-exit"]) {
|
||||
SignalListener::signalManager();
|
||||
}
|
||||
self::$process = new Process(function() {
|
||||
Framework::saveProcessState(ZM_PROCESS_MANAGER, $server->manager_pid);
|
||||
|
||||
ProcessManager::createUserProcess('monitor', function() use ($server){
|
||||
Process::signal(SIGINT, function() {
|
||||
Console::success("用户进程检测到了Ctrl+C");
|
||||
});
|
||||
if (Framework::$argv["watch"]) {
|
||||
if (extension_loaded('inotify')) {
|
||||
Console::info("Enabled File watcher, framework will reload automatically.");
|
||||
@@ -48,10 +54,27 @@ class OnManagerStart implements SwooleEvent
|
||||
Console::verbose("File updated: " . $r[0]["name"]);
|
||||
ZMUtil::reload();
|
||||
});
|
||||
Framework::$argv["polling-watch"] = false; // 如果开启了inotify则关闭轮询热更新
|
||||
} else {
|
||||
Console::warning(zm_internal_errcode("E00024") . "You have not loaded \"inotify\" extension, please install first.");
|
||||
Console::warning(zm_internal_errcode("E00024") . "你还没有安装或启用 inotify 扩展,将默认使用轮询检测模式开启热更新!");
|
||||
Framework::$argv["polling-watch"] = true;
|
||||
}
|
||||
}
|
||||
if (Framework::$argv["polling-watch"]) {
|
||||
self::$watch = swoole_timer_tick(3000, function () use ($server) {
|
||||
$data = (DataProvider::scanDirFiles(DataProvider::getSourceRootDir() . '/src/'));
|
||||
$hash = md5("");
|
||||
foreach ($data as $file) {
|
||||
$hash = md5($hash . md5_file($file));
|
||||
}
|
||||
if (self::$last_hash == "") {
|
||||
self::$last_hash = $hash;
|
||||
} elseif (self::$last_hash !== $hash) {
|
||||
self::$last_hash = $hash;
|
||||
$server->reload();
|
||||
}
|
||||
});
|
||||
}
|
||||
if (Framework::$argv["interact"]) {
|
||||
Console::info("Interact mode");
|
||||
ZMBuf::$terminal = $r = STDIN;
|
||||
@@ -73,8 +96,16 @@ class OnManagerStart implements SwooleEvent
|
||||
});
|
||||
}
|
||||
});
|
||||
self::$process->set(['enable_coroutine' => true]);
|
||||
self::$process->start();
|
||||
|
||||
ProcessManager::getUserProcess('monitor')->set(['enable_coroutine' => true]);
|
||||
ProcessManager::getUserProcess('monitor')->start();
|
||||
|
||||
/*$dispatcher = new EventDispatcher(OnManagerStartEvent::class);
|
||||
$dispatcher->setRuleFunction(function($v) {
|
||||
return eval("return " . $v->getRule() . ";");
|
||||
});
|
||||
$dispatcher->dispatchEvents($server);
|
||||
*/
|
||||
Console::verbose("进程 Manager 已启动");
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,8 @@ use Swoole\Process;
|
||||
use ZM\Annotation\Swoole\SwooleHandler;
|
||||
use ZM\Console\Console;
|
||||
use ZM\Event\SwooleEvent;
|
||||
use ZM\Utils\DataProvider;
|
||||
use ZM\Framework;
|
||||
use ZM\Utils\Manager\ProcessManager;
|
||||
|
||||
/**
|
||||
* Class OnManagerStop
|
||||
@@ -17,15 +18,12 @@ use ZM\Utils\DataProvider;
|
||||
*/
|
||||
class OnManagerStop implements SwooleEvent
|
||||
{
|
||||
public function onCall() {
|
||||
if (OnManagerStart::$process !== null) {
|
||||
if (Process::kill(OnManagerStart::$process->pid, 0)) {
|
||||
Process::kill(OnManagerStart::$process->pid, SIGTERM);
|
||||
}
|
||||
public function onCall()
|
||||
{
|
||||
foreach (ProcessManager::$user_process as $v) {
|
||||
if (posix_getsid($v->pid) !== false) Process::kill($v->pid, SIGTERM);
|
||||
}
|
||||
Console::verbose("进程 Manager 已停止!");
|
||||
if (file_exists(DataProvider::getWorkingDir()."/.daemon_pid")) {
|
||||
unlink(DataProvider::getWorkingDir()."/.daemon_pid");
|
||||
}
|
||||
Framework::removeProcessState(ZM_PROCESS_MANAGER);
|
||||
}
|
||||
}
|
||||
@@ -25,9 +25,6 @@ use ZM\Event\SwooleEvent;
|
||||
*/
|
||||
class OnMessage implements SwooleEvent
|
||||
{
|
||||
/**
|
||||
* @noinspection PhpUnreachableStatementInspection
|
||||
*/
|
||||
public function onCall($server, Frame $frame) {
|
||||
Console::debug("Calling Swoole \"message\" from fd=" . $frame->fd . ": " . TermColor::ITALIC . $frame->data . TermColor::RESET);
|
||||
unset(Context::$context[Coroutine::getCid()]);
|
||||
|
||||
@@ -10,7 +10,7 @@ use Swoole\Server;
|
||||
use ZM\Annotation\Swoole\SwooleHandler;
|
||||
use ZM\Console\Console;
|
||||
use ZM\Event\SwooleEvent;
|
||||
use ZM\Utils\Manager\ProcessManager;
|
||||
use ZM\Utils\Manager\WorkerManager;
|
||||
|
||||
/**
|
||||
* Class OnPipeMessage
|
||||
@@ -22,7 +22,7 @@ class OnPipeMessage implements SwooleEvent
|
||||
public function onCall(Server $server, $src_worker_id, $data) {
|
||||
$data = json_decode($data, true);
|
||||
try {
|
||||
ProcessManager::workerAction($src_worker_id, $data);
|
||||
WorkerManager::workerAction($src_worker_id, $data);
|
||||
} catch (Exception $e) {
|
||||
$error_msg = $e->getMessage() . " at " . $e->getFile() . "(" . $e->getLine() . ")";
|
||||
Console::error(zm_internal_errcode("E00021") . "Uncaught exception " . get_class($e) . " when calling \"pipeMessage\": " . $error_msg);
|
||||
|
||||
@@ -8,6 +8,7 @@ use Swoole\Server;
|
||||
use ZM\Annotation\Swoole\SwooleHandler;
|
||||
use ZM\Console\Console;
|
||||
use ZM\Event\SwooleEvent;
|
||||
use ZM\Framework;
|
||||
use ZM\Utils\DataProvider;
|
||||
|
||||
/**
|
||||
@@ -19,9 +20,9 @@ class OnShutdown implements SwooleEvent
|
||||
{
|
||||
public function onCall(Server $server) {
|
||||
Console::verbose("正在关闭 Master 进程,pid=" . posix_getpid());
|
||||
$pid_path = DataProvider::getWorkingDir() . "/.daemon_pid";
|
||||
if (file_exists($pid_path)) {
|
||||
unlink($pid_path);
|
||||
Framework::removeProcessState(ZM_PROCESS_MASTER);
|
||||
if (DataProvider::scanDirFiles(_zm_pid_dir()) == []) {
|
||||
rmdir(_zm_pid_dir());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,13 +25,9 @@ class OnStart implements SwooleEvent
|
||||
if (!Framework::$argv["disable-safe-exit"]) {
|
||||
SignalListener::signalMaster($server);
|
||||
}
|
||||
$daemon_data = json_encode([
|
||||
"pid" => $server->master_pid,
|
||||
"stdout" => ZMConfig::get("global")["swoole"]["log_file"],
|
||||
"daemon" => (bool)Framework::$argv["daemon"]
|
||||
], 128 | 256);
|
||||
file_put_contents(DataProvider::getWorkingDir() . "/.daemon_pid", $daemon_data);
|
||||
Framework::saveProcessState(ZM_PROCESS_MASTER, $server->master_pid, [
|
||||
'stdout' => ZMConfig::get("global")["swoole"]["log_file"],
|
||||
'daemon' => (bool)Framework::$argv["daemon"]
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -43,13 +43,15 @@ use ZM\Utils\SignalListener;
|
||||
*/
|
||||
class OnWorkerStart implements SwooleEvent
|
||||
{
|
||||
public function onCall(Server $server, $worker_id) {
|
||||
public function onCall(Server $server, $worker_id)
|
||||
{
|
||||
Console::debug("Calling onWorkerStart event(1)");
|
||||
if (!Framework::$argv["disable-safe-exit"]) {
|
||||
SignalListener::signalWorker($server, $worker_id);
|
||||
}
|
||||
unset(Context::$context[Coroutine::getCid()]);
|
||||
if ($server->taskworker === false) {
|
||||
Framework::saveProcessState(ZM_PROCESS_WORKER, $server->worker_pid, ['worker_id' => $worker_id]);
|
||||
zm_atomic("_#worker_" . $worker_id)->set($server->worker_pid);
|
||||
if (LightCacheInside::get("wait_api", "wait_api") !== null) {
|
||||
LightCacheInside::unset("wait_api", "wait_api");
|
||||
@@ -105,10 +107,13 @@ class OnWorkerStart implements SwooleEvent
|
||||
Console::error(zm_internal_errcode("E00030") . "PHP Error: " . $e->getMessage() . " in " . $e->getFile() . " on line " . $e->getLine());
|
||||
Console::error("Maybe it caused by your own code if in your own Module directory.");
|
||||
Console::log($e->getTraceAsString(), 'gray');
|
||||
Process::kill($server->master_pid, SIGTERM);
|
||||
if (!Framework::$argv["watch"]) { // 在热更新模式下不能退出
|
||||
Process::kill($server->master_pid, SIGTERM);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 这里是TaskWorker初始化的内容部分
|
||||
Framework::saveProcessState(ZM_PROCESS_TASKWORKER, $server->worker_pid, ['worker_id' => $worker_id]);
|
||||
try {
|
||||
Framework::$server = $server;
|
||||
$this->loadAnnotations();
|
||||
@@ -131,13 +136,15 @@ class OnWorkerStart implements SwooleEvent
|
||||
* @throws ReflectionException
|
||||
* @throws Exception
|
||||
*/
|
||||
private function loadAnnotations() {
|
||||
private function loadAnnotations()
|
||||
{
|
||||
if (Framework::$instant_mode) goto skip;
|
||||
//加载各个模块的注解类,以及反射
|
||||
Console::debug("Mapping annotations");
|
||||
$parser = new AnnotationParser();
|
||||
$composer = json_decode(file_get_contents(DataProvider::getSourceRootDir() . "/composer.json"), true);
|
||||
foreach ($composer["autoload"]["psr-4"] as $k => $v) {
|
||||
$merge = array_merge($composer["autoload"]["psr-4"] ?? [], $composer["autoload-dev"]["psr-4"] ?? []);
|
||||
foreach ($merge as $k => $v) {
|
||||
if (is_dir(DataProvider::getSourceRootDir() . "/" . $v)) {
|
||||
if (in_array(trim($k, "\\") . "\\", $composer["extra"]["exclude_annotate"] ?? [])) continue;
|
||||
if (trim($k, "\\") == "ZM") continue;
|
||||
@@ -150,12 +157,12 @@ class OnWorkerStart implements SwooleEvent
|
||||
$plugin_enable_hotload = ZMConfig::get("global", "module_loader")["enable_hotload"] ?? false;
|
||||
if ($plugin_enable_hotload) {
|
||||
$list = ModuleManager::getPackedModules();
|
||||
foreach($list as $k => $v) {
|
||||
if (\server()->worker_id === 0) Console::info("Loading packed module: ".$k);
|
||||
foreach ($list as $k => $v) {
|
||||
if (\server()->worker_id === 0) Console::info("Loading packed module: " . $k);
|
||||
require_once $v["phar-path"];
|
||||
$func = "loader".$v["generated-id"];
|
||||
$func = "loader" . $v["generated-id"];
|
||||
$func();
|
||||
$parser->addRegisterPath("phar://".$v["phar-path"]."/".$v["module-root-path"], $v["namespace"]);
|
||||
$parser->addRegisterPath("phar://" . $v["phar-path"] . "/" . $v["module-root-path"], $v["namespace"]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,7 +196,8 @@ class OnWorkerStart implements SwooleEvent
|
||||
}
|
||||
}
|
||||
|
||||
private function initMySQLPool() {
|
||||
private function initMySQLPool()
|
||||
{
|
||||
if (SqlPoolStorage::$sql_pool !== null) {
|
||||
SqlPoolStorage::$sql_pool->close();
|
||||
SqlPoolStorage::$sql_pool = null;
|
||||
|
||||
@@ -9,7 +9,9 @@ use ZM\Annotation\Swoole\SwooleHandler;
|
||||
use ZM\Config\ZMConfig;
|
||||
use ZM\Console\Console;
|
||||
use ZM\Event\SwooleEvent;
|
||||
use ZM\Framework;
|
||||
use ZM\Store\LightCache;
|
||||
use ZM\Utils\DataProvider;
|
||||
|
||||
/**
|
||||
* Class OnWorkerStop
|
||||
@@ -22,6 +24,7 @@ class OnWorkerStop implements SwooleEvent
|
||||
if ($worker_id == (ZMConfig::get("worker_cache")["worker"] ?? 0)) {
|
||||
LightCache::savePersistence();
|
||||
}
|
||||
Console::verbose(($server->taskworker ? "Task" : "") . "Worker #$worker_id 已停止: ".$server->getWorkerStatus($worker_id));
|
||||
Console::verbose(($server->taskworker ? "Task" : "") . "Worker #$worker_id 已停止 (Worker 状态码: ".$server->getWorkerStatus($worker_id).")");
|
||||
Framework::removeProcessState($server->taskworker ? ZM_PROCESS_TASKWORKER : ZM_PROCESS_WORKER, $worker_id);
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ use Throwable;
|
||||
use ZM\Config\ZMConfig;
|
||||
use ZM\ConnectionManager\ManagerGM;
|
||||
use ZM\Console\TermColor;
|
||||
use ZM\Exception\ZMKnownException;
|
||||
use ZM\Store\LightCache;
|
||||
use ZM\Store\LightCacheInside;
|
||||
use ZM\Store\Lock\SpinLock;
|
||||
@@ -78,7 +79,8 @@ class Framework
|
||||
|
||||
public static $instant_mode = false;
|
||||
|
||||
public function __construct($args = [], $instant_mode = false) {
|
||||
public function __construct($args = [], $instant_mode = false)
|
||||
{
|
||||
$tty_width = $this->getTtyWidth();
|
||||
self::$instant_mode = $instant_mode;
|
||||
self::$argv = $args;
|
||||
@@ -118,6 +120,9 @@ class Framework
|
||||
$args["log-theme"] ?? "default",
|
||||
($o = ZMConfig::get("console_color")) === false ? [] : $o
|
||||
);
|
||||
if ((ZMConfig::get("global", "runtime")["save_console_log_file"] ?? false) !== false) {
|
||||
Console::setOutputFile(ZMConfig::get("global", "runtime")["save_console_log_file"]);
|
||||
}
|
||||
|
||||
$timezone = ZMConfig::get("global", "timezone") ?? "Asia/Shanghai";
|
||||
date_default_timezone_set($timezone);
|
||||
@@ -126,9 +131,9 @@ class Framework
|
||||
$this->server_set["log_level"] = SWOOLE_LOG_DEBUG;
|
||||
$add_port = ZMConfig::get("global", "remote_terminal")["status"] ?? false;
|
||||
|
||||
if ($instant_mode) {
|
||||
$this->loadServerEvents();
|
||||
}
|
||||
//if ($instant_mode) {
|
||||
$this->loadServerEvents();
|
||||
//}
|
||||
|
||||
$this->parseCliArgs(self::$argv, $add_port);
|
||||
|
||||
@@ -329,7 +334,8 @@ class Framework
|
||||
}
|
||||
}
|
||||
|
||||
private static function printMotd($tty_width) {
|
||||
private static function printMotd($tty_width)
|
||||
{
|
||||
if (file_exists(DataProvider::getSourceRootDir() . "/config/motd.txt")) {
|
||||
$motd = file_get_contents(DataProvider::getSourceRootDir() . "/config/motd.txt");
|
||||
} else {
|
||||
@@ -341,7 +347,118 @@ class Framework
|
||||
echo $motd;
|
||||
}
|
||||
|
||||
public function start() {
|
||||
/**
|
||||
* 将各进程的pid写入文件,以备后续崩溃及僵尸进程处理使用
|
||||
* @param int $type
|
||||
* @param int|string $pid
|
||||
* @param array $data
|
||||
* @internal
|
||||
*/
|
||||
public static function saveProcessState(int $type, $pid, array $data = [])
|
||||
{
|
||||
switch ($type) {
|
||||
case ZM_PROCESS_MASTER:
|
||||
$file = _zm_pid_dir() . '/master.json';
|
||||
$json = [
|
||||
'pid' => intval($pid),
|
||||
'stdout' => $data['stdout'],
|
||||
'daemon' => $data['daemon']
|
||||
];
|
||||
file_put_contents($file, json_encode($json, JSON_UNESCAPED_UNICODE));
|
||||
return;
|
||||
case ZM_PROCESS_MANAGER:
|
||||
$file = _zm_pid_dir() . '/manager.pid';
|
||||
file_put_contents($file, strval($pid));
|
||||
return;
|
||||
case ZM_PROCESS_WORKER:
|
||||
$file = _zm_pid_dir() . '/worker.' . $data['worker_id'] . '.pid';
|
||||
file_put_contents($file, strval($pid));
|
||||
return;
|
||||
case ZM_PROCESS_USER:
|
||||
$file = _zm_pid_dir() . '/user.' . $data['process_name'] . '.pid';
|
||||
file_put_contents($file, strval($pid));
|
||||
return;
|
||||
case ZM_PROCESS_TASKWORKER:
|
||||
$file = _zm_pid_dir() . '/taskworker.' . $data['worker_id'] . '.pid';
|
||||
file_put_contents($file, strval($pid));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 用于框架内部获取多进程运行状态的函数
|
||||
* @param int $type
|
||||
* @param null $id_or_name
|
||||
* @return false|int|mixed
|
||||
* @throws ZMKnownException
|
||||
* @internal
|
||||
*/
|
||||
public static function getProcessState(int $type, $id_or_name = null)
|
||||
{
|
||||
$file = _zm_pid_dir();
|
||||
switch ($type) {
|
||||
case ZM_PROCESS_MASTER:
|
||||
if (!file_exists($file . '/master.json')) return false;
|
||||
$json = json_decode(file_get_contents($file . '/master.json'), true);
|
||||
if ($json !== null) return $json;
|
||||
else return false;
|
||||
case ZM_PROCESS_MANAGER:
|
||||
if (!file_exists($file . '/manager.pid')) return false;
|
||||
return intval(file_get_contents($file . '/manager.pid'));
|
||||
case ZM_PROCESS_WORKER:
|
||||
if (!is_int($id_or_name)) throw new ZMKnownException('E99999', 'worker_id必须为整数');
|
||||
if (!file_exists($file . '/worker.' . $id_or_name . '.pid')) return false;
|
||||
return intval(file_get_contents($file . '/worker.' . $id_or_name . '.pid'));
|
||||
case ZM_PROCESS_USER:
|
||||
if (!is_string($id_or_name)) throw new ZMKnownException('E99999', 'process_name必须为字符串');
|
||||
if (!file_exists($file . '/user.' . $id_or_name . '.pid')) return false;
|
||||
return intval(file_get_contents($file . '/user.' . $id_or_name . '.pid'));
|
||||
case ZM_PROCESS_TASKWORKER:
|
||||
if (!is_int($id_or_name)) throw new ZMKnownException('E99999', 'worker_id必须为整数');
|
||||
if (!file_exists($file . '/taskworker.' . $id_or_name . '.pid')) return false;
|
||||
return intval(file_get_contents($file . '/taskworker.' . $id_or_name . '.pid'));
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $type
|
||||
* @param null $id_or_name
|
||||
* @throws ZMKnownException
|
||||
* @internal
|
||||
*/
|
||||
public static function removeProcessState(int $type, $id_or_name = null)
|
||||
{
|
||||
switch ($type) {
|
||||
case ZM_PROCESS_MASTER:
|
||||
$file = _zm_pid_dir() . '/master.json';
|
||||
if (file_exists($file)) unlink($file);
|
||||
return;
|
||||
case ZM_PROCESS_MANAGER:
|
||||
$file = _zm_pid_dir() . '/manager.pid';
|
||||
if (file_exists($file)) unlink($file);
|
||||
return;
|
||||
case ZM_PROCESS_WORKER:
|
||||
if (!is_int($id_or_name)) throw new ZMKnownException('E99999', 'worker_id必须为整数');
|
||||
$file = _zm_pid_dir() . '/worker.' . $id_or_name . '.pid';
|
||||
if (file_exists($file)) unlink($file);
|
||||
return;
|
||||
case ZM_PROCESS_USER:
|
||||
if (!is_string($id_or_name)) throw new ZMKnownException('E99999', 'process_name必须为字符串');
|
||||
$file = _zm_pid_dir() . '/user.' . $id_or_name . '.pid';
|
||||
if (file_exists($file)) unlink($file);
|
||||
return;
|
||||
case ZM_PROCESS_TASKWORKER:
|
||||
if (!is_int($id_or_name)) throw new ZMKnownException('E99999', 'worker_id必须为整数');
|
||||
$file = _zm_pid_dir() . '/taskworker.' . $id_or_name . '.pid';
|
||||
if (file_exists($file)) unlink($file);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public function start()
|
||||
{
|
||||
try {
|
||||
self::$loaded_files = get_included_files();
|
||||
self::$server->start();
|
||||
@@ -355,7 +472,8 @@ class Framework
|
||||
/**
|
||||
* @noinspection PhpIncludeInspection
|
||||
*/
|
||||
private function loadServerEvents() {
|
||||
private function loadServerEvents()
|
||||
{
|
||||
if (Phar::running() !== "") {
|
||||
ob_start();
|
||||
include_once DataProvider::getFrameworkRootDir() . "/src/ZM/script_setup_loader.php";
|
||||
@@ -380,7 +498,8 @@ class Framework
|
||||
* @throws ReflectionException
|
||||
* @throws ReflectionException
|
||||
*/
|
||||
private function registerServerEvents() {
|
||||
private function registerServerEvents()
|
||||
{
|
||||
$reader = new AnnotationReader();
|
||||
$all = ZMUtil::getClassesPsr4(FRAMEWORK_ROOT_DIR . "/src/ZM/Event/SwooleEvent/", "ZM\\Event\\SwooleEvent");
|
||||
foreach ($all as $v) {
|
||||
@@ -397,6 +516,7 @@ class Framework
|
||||
}
|
||||
|
||||
foreach (($this->setup_events["setup"] ?? []) as $v) {
|
||||
Console::debug('Calling @OnSetup: ' . $v["class"]);
|
||||
$c = ZMUtil::getModInstance($v["class"]);
|
||||
$method = $v["method"];
|
||||
$c->$method();
|
||||
@@ -414,7 +534,8 @@ class Framework
|
||||
* @param $args
|
||||
* @param $add_port
|
||||
*/
|
||||
private function parseCliArgs($args, &$add_port) {
|
||||
private function parseCliArgs($args, &$add_port)
|
||||
{
|
||||
$coroutine_mode = true;
|
||||
global $terminal_id;
|
||||
$terminal_id = uuidgen();
|
||||
@@ -440,6 +561,7 @@ class Framework
|
||||
$coroutine_mode = false;
|
||||
break;
|
||||
case 'debug-mode':
|
||||
self::$argv["disable-safe-exit"] = true;
|
||||
$coroutine_mode = false;
|
||||
$terminal_id = null;
|
||||
self::$argv["watch"] = true;
|
||||
@@ -486,11 +608,12 @@ class Framework
|
||||
}
|
||||
}
|
||||
$global_hook = ZMConfig::get("global", 'runtime')['swoole_coroutine_hook_flags'] ?? (SWOOLE_HOOK_ALL & (~SWOOLE_HOOK_CURL));
|
||||
if ($coroutine_mode && $global_hook === false) Runtime::enableCoroutine(true, $global_hook);
|
||||
if ($coroutine_mode) Runtime::enableCoroutine(true, $global_hook);
|
||||
else Runtime::enableCoroutine(false, SWOOLE_HOOK_ALL);
|
||||
}
|
||||
|
||||
private static function writeNoDouble($k, $v, &$line_data, &$line_width, &$current_line, $colorful, $max_border) {
|
||||
private static function writeNoDouble($k, $v, &$line_data, &$line_width, &$current_line, $colorful, $max_border)
|
||||
{
|
||||
$tmp_line = $k . ": " . $v;
|
||||
//Console::info("写入[".$tmp_line."]");
|
||||
if (strlen($tmp_line) > $line_width[$current_line]) { //输出的内容太多了,以至于一行都放不下一个,要折行
|
||||
@@ -534,7 +657,8 @@ class Framework
|
||||
}
|
||||
}
|
||||
|
||||
public static function printProps($out, $tty_width, $colorful = true) {
|
||||
public static function printProps($out, $tty_width, $colorful = true)
|
||||
{
|
||||
$max_border = $tty_width < 65 ? $tty_width : 65;
|
||||
if (LOAD_MODE == 0) echo Console::setColor("* Framework started with source mode.\n", $colorful ? "yellow" : "");
|
||||
echo str_pad("", $max_border, "=") . PHP_EOL;
|
||||
@@ -576,20 +700,23 @@ class Framework
|
||||
echo str_pad("", $max_border, "=") . PHP_EOL;
|
||||
}
|
||||
|
||||
public static function getTtyWidth(): string {
|
||||
public static function getTtyWidth(): string
|
||||
{
|
||||
$size = exec("stty size");
|
||||
if (empty($size)) return 65;
|
||||
return explode(" ", trim($size))[1];
|
||||
}
|
||||
|
||||
public static function loadFrameworkState() {
|
||||
public static function loadFrameworkState()
|
||||
{
|
||||
if (!file_exists(DataProvider::getDataFolder() . ".state.json")) return [];
|
||||
$r = json_decode(file_get_contents(DataProvider::getDataFolder() . ".state.json"), true);
|
||||
if ($r === null) $r = [];
|
||||
return $r;
|
||||
}
|
||||
|
||||
public static function saveFrameworkState($data) {
|
||||
public static function saveFrameworkState($data)
|
||||
{
|
||||
return file_put_contents(DataProvider::getDataFolder() . ".state.json", json_encode($data, 64 | 128 | 256));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,7 +119,7 @@ class Response
|
||||
* @return mixed
|
||||
*/
|
||||
public function header($key, $value, $ucwords = null) {
|
||||
if (!$this->is_end) return $this->response->header($key, $value, $ucwords);
|
||||
if (!$this->is_end) return $ucwords === null ? $this->response->header($key, $value) : $this->response->header($key, $value, $ucwords);
|
||||
else return false;
|
||||
}
|
||||
|
||||
@@ -130,7 +130,8 @@ class Response
|
||||
* @return mixed
|
||||
*/
|
||||
public function setHeader($key, $value, $ucwords = null) {
|
||||
return !$this->is_end ? $this->response->setHeader($key, $value, $ucwords) : false;
|
||||
if (!$this->is_end) return $ucwords === null ? $this->response->setHeader($key, $value) : $this->response->setHeader($key, $value, $ucwords);
|
||||
else return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -192,7 +192,6 @@ class QQBot
|
||||
$after->setRuleFunction(function ($v) use ($data) {
|
||||
return $v->cq_event == $data["post_type"];
|
||||
});
|
||||
zm_dump("开始触发!", $data);
|
||||
$after->dispatchEvents($data);
|
||||
return $after;
|
||||
}
|
||||
|
||||
@@ -17,19 +17,23 @@ class MySQLStatement implements IteratorAggregate, Statement
|
||||
/** @var PDOStatement|PDOStatementProxy */
|
||||
private $statement;
|
||||
|
||||
public function __construct($obj) {
|
||||
public function __construct($obj)
|
||||
{
|
||||
$this->statement = $obj;
|
||||
}
|
||||
|
||||
public function closeCursor() {
|
||||
public function closeCursor()
|
||||
{
|
||||
return $this->statement->closeCursor();
|
||||
}
|
||||
|
||||
public function columnCount() {
|
||||
public function columnCount()
|
||||
{
|
||||
return $this->statement->columnCount();
|
||||
}
|
||||
|
||||
public function setFetchMode($fetchMode, $arg2 = null, $arg3 = []) {
|
||||
public function setFetchMode($fetchMode, $arg2 = null, $arg3 = [])
|
||||
{
|
||||
if ($arg2 !== null && $arg3 !== [])
|
||||
return $this->statement->setFetchMode($fetchMode, $arg2, $arg3);
|
||||
elseif ($arg2 !== null && $arg3 === [])
|
||||
@@ -40,47 +44,63 @@ class MySQLStatement implements IteratorAggregate, Statement
|
||||
return $this->statement->setFetchMode($fetchMode);
|
||||
}
|
||||
|
||||
public function fetch($fetchMode = PDO::FETCH_ASSOC, $cursorOrientation = PDO::FETCH_ORI_NEXT, $cursorOffset = 0) {
|
||||
public function fetch($fetchMode = PDO::FETCH_ASSOC, $cursorOrientation = PDO::FETCH_ORI_NEXT, $cursorOffset = 0)
|
||||
{
|
||||
return $this->statement->fetch($fetchMode, $cursorOrientation, $cursorOffset);
|
||||
}
|
||||
|
||||
public function fetchAll($fetchMode = PDO::FETCH_ASSOC, $fetchArgument = null, $ctorArgs = null) {
|
||||
return $this->statement->fetchAll($fetchMode, $fetchArgument, $ctorArgs);
|
||||
public function fetchAll($fetchMode = PDO::FETCH_ASSOC, $fetchArgument = null, $ctorArgs = null)
|
||||
{
|
||||
if ($fetchArgument === null && $ctorArgs === null)
|
||||
return $this->statement->fetchAll($fetchMode);
|
||||
elseif ($fetchArgument !== null && $ctorArgs === null)
|
||||
return $this->statement->fetchAll($fetchMode, $fetchArgument);
|
||||
else
|
||||
return $this->statement->fetchAll($fetchMode, $fetchArgument, $ctorArgs);
|
||||
}
|
||||
|
||||
public function fetchColumn($columnIndex = 0) {
|
||||
public function fetchColumn($columnIndex = 0)
|
||||
{
|
||||
return $this->statement->fetchColumn($columnIndex);
|
||||
}
|
||||
|
||||
public function bindValue($param, $value, $type = ParameterType::STRING) {
|
||||
public function bindValue($param, $value, $type = ParameterType::STRING)
|
||||
{
|
||||
return $this->statement->bindValue($param, $value, $type);
|
||||
}
|
||||
|
||||
public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null) {
|
||||
public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null)
|
||||
{
|
||||
return $this->statement->bindParam($param, $variable, $type, $length);
|
||||
}
|
||||
|
||||
public function errorCode() {
|
||||
public function errorCode()
|
||||
{
|
||||
return $this->statement->errorCode();
|
||||
}
|
||||
|
||||
public function errorInfo() {
|
||||
public function errorInfo()
|
||||
{
|
||||
return $this->statement->errorInfo();
|
||||
}
|
||||
|
||||
public function execute($params = null) {
|
||||
public function execute($params = null)
|
||||
{
|
||||
return $this->statement->execute($params);
|
||||
}
|
||||
|
||||
public function rowCount() {
|
||||
public function rowCount()
|
||||
{
|
||||
return $this->statement->rowCount();
|
||||
}
|
||||
|
||||
public function getIterator() {
|
||||
public function getIterator(): StatementIterator
|
||||
{
|
||||
return new StatementIterator($this);
|
||||
}
|
||||
|
||||
public function current() {
|
||||
public function current()
|
||||
{
|
||||
return $this->statement->current();
|
||||
}
|
||||
}
|
||||
@@ -94,7 +94,7 @@ class MySQLWrapper
|
||||
* @return false|mixed
|
||||
* @throws DbException
|
||||
*/
|
||||
public function fetchOne(string $query, array $params = [], array $types = []): bool {
|
||||
public function fetchOne(string $query, array $params = [], array $types = []) {
|
||||
try {
|
||||
return $this->connection->fetchOne($query, $params, $types);
|
||||
} catch (Throwable $e) {
|
||||
|
||||
@@ -4,120 +4,29 @@
|
||||
namespace ZM\Utils\Manager;
|
||||
|
||||
|
||||
use Swoole\Coroutine;
|
||||
use ZM\Annotation\CQ\CQCommand;
|
||||
use ZM\Annotation\Swoole\OnPipeMessageEvent;
|
||||
use ZM\Console\Console;
|
||||
use ZM\Event\EventDispatcher;
|
||||
use ZM\Event\EventManager;
|
||||
use ZM\Store\LightCache;
|
||||
use ZM\Store\LightCacheInside;
|
||||
use ZM\Store\WorkerCache;
|
||||
use Swoole\Process;
|
||||
|
||||
class ProcessManager
|
||||
{
|
||||
public static function workerAction($src_worker_id, $data) {
|
||||
$server = server();
|
||||
switch ($data["action"] ?? '') {
|
||||
case 'add_short_command':
|
||||
Console::verbose("Adding short command " . $data["data"][0]);
|
||||
$obj = new CQCommand();
|
||||
$obj->method = quick_reply_closure($data["data"][1]);
|
||||
$obj->match = $data["data"][0];
|
||||
$obj->class = "";
|
||||
EventManager::addEvent(CQCommand::class, $obj);
|
||||
break;
|
||||
case "eval":
|
||||
eval($data["data"]);
|
||||
break;
|
||||
case "call_static":
|
||||
call_user_func_array([$data["data"]["class"], $data["data"]["method"]], $data["data"]["params"]);
|
||||
break;
|
||||
case "save_persistence":
|
||||
LightCache::savePersistence();
|
||||
break;
|
||||
case "resume_ws_message":
|
||||
$obj = $data["data"];
|
||||
Coroutine::resume($obj["coroutine"]);
|
||||
break;
|
||||
case "getWorkerCache":
|
||||
$r = WorkerCache::get($data["key"]);
|
||||
$action = ["action" => "returnWorkerCache", "cid" => $data["cid"], "value" => $r];
|
||||
$server->sendMessage(json_encode($action, 256), $src_worker_id);
|
||||
break;
|
||||
case "setWorkerCache":
|
||||
$r = WorkerCache::set($data["key"], $data["value"]);
|
||||
$action = ["action" => "returnWorkerCache", "cid" => $data["cid"], "value" => $r];
|
||||
$server->sendMessage(json_encode($action, 256), $src_worker_id);
|
||||
break;
|
||||
case "unsetWorkerCache":
|
||||
$r = WorkerCache::unset($data["key"]);
|
||||
$action = ["action" => "returnWorkerCache", "cid" => $data["cid"], "value" => $r];
|
||||
$server->sendMessage(json_encode($action, 256), $src_worker_id);
|
||||
break;
|
||||
case "hasKeyWorkerCache":
|
||||
$r = WorkerCache::hasKey($data["key"], $data["subkey"]);
|
||||
$action = ["action" => "returnWorkerCache", "cid" => $data["cid"], "value" => $r];
|
||||
$server->sendMessage(json_encode($action, 256), $src_worker_id);
|
||||
break;
|
||||
case "asyncAddWorkerCache":
|
||||
WorkerCache::add($data["key"], $data["value"], true);
|
||||
break;
|
||||
case "asyncSubWorkerCache":
|
||||
WorkerCache::sub($data["key"], $data["value"], true);
|
||||
break;
|
||||
case "asyncSetWorkerCache":
|
||||
WorkerCache::set($data["key"], $data["value"], true);
|
||||
break;
|
||||
case "asyncUnsetWorkerCache":
|
||||
WorkerCache::unset($data["key"], true);
|
||||
break;
|
||||
case "addWorkerCache":
|
||||
$r = WorkerCache::add($data["key"], $data["value"]);
|
||||
$action = ["action" => "returnWorkerCache", "cid" => $data["cid"], "value" => $r];
|
||||
$server->sendMessage(json_encode($action, 256), $src_worker_id);
|
||||
break;
|
||||
case "subWorkerCache":
|
||||
$r = WorkerCache::sub($data["key"], $data["value"]);
|
||||
$action = ["action" => "returnWorkerCache", "cid" => $data["cid"], "value" => $r];
|
||||
$server->sendMessage(json_encode($action, 256), $src_worker_id);
|
||||
break;
|
||||
case "returnWorkerCache":
|
||||
WorkerCache::$transfer[$data["cid"]] = $data["value"];
|
||||
zm_resume($data["cid"]);
|
||||
break;
|
||||
default:
|
||||
$dispatcher = new EventDispatcher(OnPipeMessageEvent::class);
|
||||
$dispatcher->setRuleFunction(function (OnPipeMessageEvent $v) use ($data) {
|
||||
return $v->action == $data["action"];
|
||||
});
|
||||
$dispatcher->dispatchEvents($data);
|
||||
break;
|
||||
}
|
||||
/** @var Process[] */
|
||||
public static $user_process = [];
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param callable $callable
|
||||
* @return Process
|
||||
*/
|
||||
public static function createUserProcess(string $name, callable $callable): Process
|
||||
{
|
||||
return self::$user_process[$name] = new Process($callable);
|
||||
}
|
||||
|
||||
public static function sendActionToWorker($worker_id, $action, $data) {
|
||||
$obj = ["action" => $action, "data" => $data];
|
||||
if (server()->worker_id === -1 && server()->getManagerPid() != posix_getpid()) {
|
||||
Console::warning(zm_internal_errcode("E00022") . "Cannot send worker action from master or manager process!");
|
||||
return;
|
||||
}
|
||||
if (server()->worker_id == $worker_id) {
|
||||
self::workerAction($worker_id, $obj);
|
||||
} else {
|
||||
server()->sendMessage(json_encode($obj), $worker_id);
|
||||
}
|
||||
}
|
||||
|
||||
public static function resumeAllWorkerCoroutines() {
|
||||
if (server()->worker_id === -1) {
|
||||
Console::warning("Cannot call '" . __FUNCTION__ . "' in non-worker process!");
|
||||
return;
|
||||
}
|
||||
foreach ((LightCacheInside::get("wait_api", "wait_api") ?? []) as $v) {
|
||||
if (isset($v["coroutine"], $v["worker_id"])) {
|
||||
if (server()->worker_id == $v["worker_id"]) Coroutine::resume($v["coroutine"]);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @param string $string
|
||||
* @return Process|null
|
||||
*/
|
||||
public static function getUserProcess(string $string): ?Process
|
||||
{
|
||||
return self::$user_process[$string] ?? null;
|
||||
}
|
||||
}
|
||||
141
src/ZM/Utils/Manager/WorkerManager.php
Normal file
141
src/ZM/Utils/Manager/WorkerManager.php
Normal file
@@ -0,0 +1,141 @@
|
||||
<?php
|
||||
|
||||
namespace ZM\Utils\Manager;
|
||||
|
||||
use Exception;
|
||||
use Swoole\Coroutine;
|
||||
use ZM\Annotation\CQ\CQCommand;
|
||||
use ZM\Annotation\Swoole\OnPipeMessageEvent;
|
||||
use ZM\Console\Console;
|
||||
use ZM\Event\EventDispatcher;
|
||||
use ZM\Event\EventManager;
|
||||
use ZM\Store\LightCache;
|
||||
use ZM\Store\LightCacheInside;
|
||||
use ZM\Store\WorkerCache;
|
||||
|
||||
class WorkerManager
|
||||
{
|
||||
/**
|
||||
* Worker 进程间通信触发的动作类型函数
|
||||
* @param $src_worker_id
|
||||
* @param $data
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function workerAction($src_worker_id, $data)
|
||||
{
|
||||
$server = server();
|
||||
switch ($data["action"] ?? '') {
|
||||
case 'add_short_command':
|
||||
Console::verbose("Adding short command " . $data["data"][0]);
|
||||
$obj = new CQCommand();
|
||||
$obj->method = quick_reply_closure($data["data"][1]);
|
||||
$obj->match = $data["data"][0];
|
||||
$obj->class = "";
|
||||
EventManager::addEvent(CQCommand::class, $obj);
|
||||
break;
|
||||
case "eval":
|
||||
eval($data["data"]);
|
||||
break;
|
||||
case "call_static":
|
||||
call_user_func_array([$data["data"]["class"], $data["data"]["method"]], $data["data"]["params"]);
|
||||
break;
|
||||
case "save_persistence":
|
||||
LightCache::savePersistence();
|
||||
break;
|
||||
case "resume_ws_message":
|
||||
$obj = $data["data"];
|
||||
Coroutine::resume($obj["coroutine"]);
|
||||
break;
|
||||
case "getWorkerCache":
|
||||
$r = WorkerCache::get($data["key"]);
|
||||
$action = ["action" => "returnWorkerCache", "cid" => $data["cid"], "value" => $r];
|
||||
$server->sendMessage(json_encode($action, 256), $src_worker_id);
|
||||
break;
|
||||
case "setWorkerCache":
|
||||
$r = WorkerCache::set($data["key"], $data["value"]);
|
||||
$action = ["action" => "returnWorkerCache", "cid" => $data["cid"], "value" => $r];
|
||||
$server->sendMessage(json_encode($action, 256), $src_worker_id);
|
||||
break;
|
||||
case "unsetWorkerCache":
|
||||
$r = WorkerCache::unset($data["key"]);
|
||||
$action = ["action" => "returnWorkerCache", "cid" => $data["cid"], "value" => $r];
|
||||
$server->sendMessage(json_encode($action, 256), $src_worker_id);
|
||||
break;
|
||||
case "hasKeyWorkerCache":
|
||||
$r = WorkerCache::hasKey($data["key"], $data["subkey"]);
|
||||
$action = ["action" => "returnWorkerCache", "cid" => $data["cid"], "value" => $r];
|
||||
$server->sendMessage(json_encode($action, 256), $src_worker_id);
|
||||
break;
|
||||
case "asyncAddWorkerCache":
|
||||
WorkerCache::add($data["key"], $data["value"], true);
|
||||
break;
|
||||
case "asyncSubWorkerCache":
|
||||
WorkerCache::sub($data["key"], $data["value"], true);
|
||||
break;
|
||||
case "asyncSetWorkerCache":
|
||||
WorkerCache::set($data["key"], $data["value"], true);
|
||||
break;
|
||||
case "asyncUnsetWorkerCache":
|
||||
WorkerCache::unset($data["key"], true);
|
||||
break;
|
||||
case "addWorkerCache":
|
||||
$r = WorkerCache::add($data["key"], $data["value"]);
|
||||
$action = ["action" => "returnWorkerCache", "cid" => $data["cid"], "value" => $r];
|
||||
$server->sendMessage(json_encode($action, 256), $src_worker_id);
|
||||
break;
|
||||
case "subWorkerCache":
|
||||
$r = WorkerCache::sub($data["key"], $data["value"]);
|
||||
$action = ["action" => "returnWorkerCache", "cid" => $data["cid"], "value" => $r];
|
||||
$server->sendMessage(json_encode($action, 256), $src_worker_id);
|
||||
break;
|
||||
case "returnWorkerCache":
|
||||
WorkerCache::$transfer[$data["cid"]] = $data["value"];
|
||||
zm_resume($data["cid"]);
|
||||
break;
|
||||
default:
|
||||
$dispatcher = new EventDispatcher(OnPipeMessageEvent::class);
|
||||
$dispatcher->setRuleFunction(function (OnPipeMessageEvent $v) use ($data) {
|
||||
return $v->action == $data["action"];
|
||||
});
|
||||
$dispatcher->dispatchEvents($data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 给 Worker 进程发送动作指令(包括自身,自身将直接执行)
|
||||
* @param $worker_id
|
||||
* @param $action
|
||||
* @param $data
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function sendActionToWorker($worker_id, $action, $data)
|
||||
{
|
||||
$obj = ["action" => $action, "data" => $data];
|
||||
if (server()->worker_id === -1 && server()->getManagerPid() != posix_getpid()) {
|
||||
Console::warning(zm_internal_errcode("E00022") . "Cannot send worker action from master or manager process!");
|
||||
return;
|
||||
}
|
||||
if (server()->worker_id == $worker_id) {
|
||||
self::workerAction($worker_id, $obj);
|
||||
} else {
|
||||
server()->sendMessage(json_encode($obj), $worker_id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 向所有 Worker 进程发送动作指令
|
||||
*/
|
||||
public static function resumeAllWorkerCoroutines()
|
||||
{
|
||||
if (server()->worker_id === -1) {
|
||||
Console::warning("Cannot call '" . __FUNCTION__ . "' in non-worker process!");
|
||||
return;
|
||||
}
|
||||
foreach ((LightCacheInside::get("wait_api", "wait_api") ?? []) as $v) {
|
||||
if (isset($v["coroutine"], $v["worker_id"])) {
|
||||
if (server()->worker_id == $v["worker_id"]) Coroutine::resume($v["coroutine"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ use ZM\Console\Console;
|
||||
*/
|
||||
class SignalListener
|
||||
{
|
||||
private static $manager_kill_time = 0;
|
||||
/**
|
||||
* 监听Master进程的Ctrl+C
|
||||
* @param Server $server
|
||||
@@ -29,6 +30,7 @@ class SignalListener
|
||||
} else {
|
||||
echo "\r";
|
||||
Console::warning("Server interrupted(SIGINT) on Master.");
|
||||
Console::warning("Server will be shutdown.");
|
||||
Process::kill($server->master_pid, SIGTERM);
|
||||
}
|
||||
});
|
||||
@@ -48,6 +50,7 @@ class SignalListener
|
||||
} else {
|
||||
Console::verbose("Interrupted in manager!");
|
||||
}
|
||||
self::processKillerPrompt();
|
||||
};
|
||||
Console::debug("Listening Manager SIGINT");
|
||||
if (version_compare(SWOOLE_VERSION, "4.6.7") >= 0) {
|
||||
@@ -65,15 +68,40 @@ class SignalListener
|
||||
public static function signalWorker(Server $server, $worker_id) {
|
||||
Console::debug("Listening Worker #".$worker_id." SIGINT");
|
||||
Process::signal(SIGINT, function () use ($worker_id, $server) {
|
||||
if ($server->master_pid == $server->worker_pid) {
|
||||
if ($server->master_pid == $server->worker_pid) { // 当Swoole以单进程模型运行的时候,Worker需要监听杀死的信号
|
||||
echo "\r";
|
||||
Console::warning("Server interrupted(SIGINT) on Worker.");
|
||||
swoole_timer_after(2, function() {
|
||||
Process::kill(posix_getpid(), SIGTERM);
|
||||
});
|
||||
self::processKillerPrompt();
|
||||
}
|
||||
//Console::verbose("Interrupted in worker");
|
||||
// do nothing
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 按5次Ctrl+C后强行杀死框架的处理函数
|
||||
*/
|
||||
private static function processKillerPrompt() {
|
||||
if (self::$manager_kill_time > 0) {
|
||||
if (self::$manager_kill_time >= 5) {
|
||||
$file_path = _zm_pid_dir();
|
||||
$flist = DataProvider::scanDirFiles($file_path, false, true);
|
||||
foreach($flist as $file) {
|
||||
$name = explode('.', $file);
|
||||
if (end($name) == 'pid' && $name[0] !== 'manager') {
|
||||
$pid = file_get_contents($file_path.'/'.$file);
|
||||
Process::kill($pid, SIGKILL);
|
||||
}
|
||||
unlink($file_path.'/'.$file);
|
||||
}
|
||||
} else {
|
||||
echo "\r";
|
||||
Console::log("再按" . (5 - self::$manager_kill_time) . "次Ctrl+C所有Worker进程就会被强制杀死", 'red');
|
||||
}
|
||||
}
|
||||
self::$manager_kill_time++;
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,8 @@ use Swoole\Coroutine\System;
|
||||
use ZM\Context\ContextInterface;
|
||||
|
||||
|
||||
function getClassPath($class_name) {
|
||||
function getClassPath($class_name)
|
||||
{
|
||||
$dir = str_replace("\\", "/", $class_name);
|
||||
$dir2 = DataProvider::getSourceRootDir() . "/src/" . $dir . ".php";
|
||||
//echo "@@@".$dir2.PHP_EOL;
|
||||
@@ -29,11 +30,13 @@ function getClassPath($class_name) {
|
||||
if (file_exists($dir2)) return $dir2;
|
||||
else return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查炸毛框架运行的环境
|
||||
* @internal
|
||||
*/
|
||||
function _zm_env_check() {
|
||||
function _zm_env_check()
|
||||
{
|
||||
if (!extension_loaded("swoole")) die(zm_internal_errcode("E00001") . "Can not find swoole extension.\n");
|
||||
if (version_compare(SWOOLE_VERSION, "4.5.0") == -1) die(zm_internal_errcode("E00002") . "You must install swoole version >= 4.5.0 !");
|
||||
if (version_compare(PHP_VERSION, "7.2") == -1) die(zm_internal_errcode("E00003") . "PHP >= 7.2 required.");
|
||||
@@ -49,7 +52,8 @@ function _zm_env_check() {
|
||||
* @param bool $ban_comma
|
||||
* @return array
|
||||
*/
|
||||
function explodeMsg($msg, $ban_comma = false): array {
|
||||
function explodeMsg($msg, $ban_comma = false): array
|
||||
{
|
||||
$msg = str_replace(" ", "\n", trim($msg));
|
||||
if (!$ban_comma) {
|
||||
//$msg = str_replace(",", "\n", $msg);
|
||||
@@ -65,13 +69,15 @@ function explodeMsg($msg, $ban_comma = false): array {
|
||||
}
|
||||
|
||||
/** @noinspection PhpUnused */
|
||||
function unicode_decode($str) {
|
||||
function unicode_decode($str)
|
||||
{
|
||||
return preg_replace_callback('/\\\\u([0-9a-f]{4})/i', function ($matches) {
|
||||
return mb_convert_encoding(pack("H*", $matches[1]), "UTF-8", "UCS-2BE");
|
||||
}, $str);
|
||||
}
|
||||
|
||||
function matchPattern($pattern, $context): bool {
|
||||
function matchPattern($pattern, $context): bool
|
||||
{
|
||||
if (mb_substr($pattern, 0, 1) == "" && mb_substr($context, 0, 1) == "")
|
||||
return true;
|
||||
if ('*' == mb_substr($pattern, 0, 1) && "" != mb_substr($pattern, 1, 1) && "" == mb_substr($context, 0, 1))
|
||||
@@ -83,7 +89,8 @@ function matchPattern($pattern, $context): bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
function split_explode($del, $str, $divide_en = false): array {
|
||||
function split_explode($del, $str, $divide_en = false): array
|
||||
{
|
||||
$str = explode($del, $str);
|
||||
for ($i = 0; $i < mb_strlen($str[0]); $i++) {
|
||||
if (
|
||||
@@ -115,7 +122,8 @@ function split_explode($del, $str, $divide_en = false): array {
|
||||
return $ls == [] ? [""] : $ls;
|
||||
}
|
||||
|
||||
function matchArgs($pattern, $context) {
|
||||
function matchArgs($pattern, $context)
|
||||
{
|
||||
$result = [];
|
||||
if (matchPattern($pattern, $context)) {
|
||||
if (mb_strpos($pattern, "*") === false) return [];
|
||||
@@ -145,19 +153,23 @@ function matchArgs($pattern, $context) {
|
||||
} else return false;
|
||||
}
|
||||
|
||||
function connectIsQQ(): bool {
|
||||
function connectIsQQ(): bool
|
||||
{
|
||||
return ctx()->getConnection()->getName() == 'qq';
|
||||
}
|
||||
|
||||
function connectIsDefault(): bool {
|
||||
function connectIsDefault(): bool
|
||||
{
|
||||
return ctx()->getConnection()->getName() == 'default';
|
||||
}
|
||||
|
||||
function connectIs($type): bool {
|
||||
function connectIs($type): bool
|
||||
{
|
||||
return ctx()->getConnection()->getName() == $type;
|
||||
}
|
||||
|
||||
function getAnnotations(): array {
|
||||
function getAnnotations(): array
|
||||
{
|
||||
$s = debug_backtrace()[1];
|
||||
//echo json_encode($s, 128|256);
|
||||
$list = [];
|
||||
@@ -172,9 +184,10 @@ function getAnnotations(): array {
|
||||
return $list;
|
||||
}
|
||||
|
||||
function set_coroutine_params($array) {
|
||||
function set_coroutine_params($array)
|
||||
{
|
||||
$cid = Co::getCid();
|
||||
if ($cid == -1) die(zm_internal_errcode("E00061")."Cannot set coroutine params at none coroutine mode.");
|
||||
if ($cid == -1) die(zm_internal_errcode("E00061") . "Cannot set coroutine params at none coroutine mode.");
|
||||
if (isset(Context::$context[$cid])) Context::$context[$cid] = array_merge(Context::$context[$cid], $array);
|
||||
else Context::$context[$cid] = $array;
|
||||
foreach (Context::$context as $c => $v) {
|
||||
@@ -185,14 +198,16 @@ function set_coroutine_params($array) {
|
||||
/**
|
||||
* @return ContextInterface|null
|
||||
*/
|
||||
function context(): ?ContextInterface {
|
||||
function context(): ?ContextInterface
|
||||
{
|
||||
return ctx();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ContextInterface|null
|
||||
*/
|
||||
function ctx(): ?ContextInterface {
|
||||
function ctx(): ?ContextInterface
|
||||
{
|
||||
$cid = Co::getCid();
|
||||
$c_class = ZMConfig::get("global", "context_class");
|
||||
if (isset(Context::$context[$cid])) {
|
||||
@@ -207,39 +222,47 @@ function ctx(): ?ContextInterface {
|
||||
}
|
||||
}
|
||||
|
||||
function onebot_target_id_name($message_type): string {
|
||||
function onebot_target_id_name($message_type): string
|
||||
{
|
||||
return ($message_type == "group" ? "group_id" : "user_id");
|
||||
}
|
||||
|
||||
function zm_sleep($s = 1): bool {
|
||||
function zm_sleep($s = 1): bool
|
||||
{
|
||||
if (Coroutine::getCid() != -1) System::sleep($s);
|
||||
else usleep($s * 1000 * 1000);
|
||||
return true;
|
||||
}
|
||||
|
||||
function zm_exec($cmd): array {
|
||||
function zm_exec($cmd): array
|
||||
{
|
||||
return System::exec($cmd);
|
||||
}
|
||||
|
||||
function zm_cid() {
|
||||
function zm_cid()
|
||||
{
|
||||
return Co::getCid();
|
||||
}
|
||||
|
||||
function zm_yield() {
|
||||
function zm_yield()
|
||||
{
|
||||
Co::yield();
|
||||
}
|
||||
|
||||
function zm_resume(int $cid) {
|
||||
function zm_resume(int $cid)
|
||||
{
|
||||
Co::resume($cid);
|
||||
}
|
||||
|
||||
function zm_timer_after($ms, callable $callable) {
|
||||
function zm_timer_after($ms, callable $callable)
|
||||
{
|
||||
Swoole\Timer::after($ms, function () use ($callable) {
|
||||
call_with_catch($callable);
|
||||
});
|
||||
}
|
||||
|
||||
function call_with_catch($callable) {
|
||||
function call_with_catch($callable)
|
||||
{
|
||||
try {
|
||||
$callable();
|
||||
} catch (Exception $e) {
|
||||
@@ -253,7 +276,8 @@ function call_with_catch($callable) {
|
||||
}
|
||||
}
|
||||
|
||||
function zm_timer_tick($ms, callable $callable) {
|
||||
function zm_timer_tick($ms, callable $callable)
|
||||
{
|
||||
if (zm_cid() === -1) {
|
||||
return go(function () use ($ms, $callable) {
|
||||
Console::debug("Adding extra timer tick of " . $ms . " ms");
|
||||
@@ -268,24 +292,39 @@ function zm_timer_tick($ms, callable $callable) {
|
||||
}
|
||||
}
|
||||
|
||||
function zm_go(callable $callable) {
|
||||
function zm_go(callable $callable)
|
||||
{
|
||||
call_with_catch($callable);
|
||||
}
|
||||
|
||||
function zm_data_hash($v): string {
|
||||
function zm_data_hash($v): string
|
||||
{
|
||||
return md5($v["user_id"] . "^" . $v["self_id"] . "^" . $v["message_type"] . "^" . ($v[$v["message_type"] . "_id"] ?? $v["user_id"]));
|
||||
}
|
||||
|
||||
function server(): ?Server {
|
||||
function server(): ?Server
|
||||
{
|
||||
return Framework::$server;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取缓存当前框架pid的临时目录
|
||||
* @return string
|
||||
*/
|
||||
function _zm_pid_dir(): string
|
||||
{
|
||||
global $_ZM_HASH;
|
||||
if (!isset($_ZM_HASH)) $_ZM_HASH = md5(DataProvider::getWorkingDir());
|
||||
return '/tmp/.zm_' . $_ZM_HASH;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return OneBotV11
|
||||
* @throws RobotNotFoundException
|
||||
* @throws ZMException
|
||||
*/
|
||||
function bot() {
|
||||
function bot()
|
||||
{
|
||||
if (($conn = LightCacheInside::get("connect", "conn_fd")) == -2) {
|
||||
return OneBotV11::getRandom();
|
||||
} elseif ($conn != -1) {
|
||||
@@ -302,7 +341,8 @@ function bot() {
|
||||
* @return array
|
||||
* @author 854854321
|
||||
*/
|
||||
function getAllFdByConnectType(string $type = 'default'): array {
|
||||
function getAllFdByConnectType(string $type = 'default'): array
|
||||
{
|
||||
$fds = [];
|
||||
foreach (ManagerGM::getAllByName($type) as $obj) {
|
||||
$fds[] = $obj->getFd();
|
||||
@@ -310,11 +350,13 @@ function getAllFdByConnectType(string $type = 'default'): array {
|
||||
return $fds;
|
||||
}
|
||||
|
||||
function zm_atomic($name): ?Atomic {
|
||||
function zm_atomic($name): ?Atomic
|
||||
{
|
||||
return ZMAtomic::get($name);
|
||||
}
|
||||
|
||||
function uuidgen($uppercase = false): string {
|
||||
function uuidgen($uppercase = false): string
|
||||
{
|
||||
try {
|
||||
$data = random_bytes(16);
|
||||
} catch (Exception $e) {
|
||||
@@ -326,11 +368,13 @@ function uuidgen($uppercase = false): string {
|
||||
vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
|
||||
}
|
||||
|
||||
function working_dir() {
|
||||
function working_dir()
|
||||
{
|
||||
return WORKING_DIR;
|
||||
}
|
||||
|
||||
function zm_dump($var, ...$moreVars) {
|
||||
function zm_dump($var, ...$moreVars)
|
||||
{
|
||||
VarDumper::dump($var);
|
||||
|
||||
foreach ($moreVars as $v) {
|
||||
@@ -344,40 +388,49 @@ function zm_dump($var, ...$moreVars) {
|
||||
return $var;
|
||||
}
|
||||
|
||||
function zm_info($obj) {
|
||||
function zm_info($obj)
|
||||
{
|
||||
Console::info($obj);
|
||||
}
|
||||
|
||||
function zm_warning($obj) {
|
||||
function zm_warning($obj)
|
||||
{
|
||||
Console::warning($obj);
|
||||
}
|
||||
|
||||
function zm_success($obj) {
|
||||
function zm_success($obj)
|
||||
{
|
||||
Console::success($obj);
|
||||
}
|
||||
|
||||
function zm_debug($obj) {
|
||||
function zm_debug($obj)
|
||||
{
|
||||
Console::debug($obj);
|
||||
}
|
||||
|
||||
function zm_verbose($obj) {
|
||||
function zm_verbose($obj)
|
||||
{
|
||||
Console::verbose($obj);
|
||||
}
|
||||
|
||||
function zm_error($obj) {
|
||||
function zm_error($obj)
|
||||
{
|
||||
Console::error($obj);
|
||||
}
|
||||
|
||||
function zm_config($name, $key = null) {
|
||||
function zm_config($name, $key = null)
|
||||
{
|
||||
return ZMConfig::get($name, $key);
|
||||
}
|
||||
|
||||
function quick_reply_closure($reply) {
|
||||
function quick_reply_closure($reply)
|
||||
{
|
||||
return function () use ($reply) {
|
||||
return $reply;
|
||||
};
|
||||
}
|
||||
|
||||
function zm_internal_errcode($code): string {
|
||||
function zm_internal_errcode($code): string
|
||||
{
|
||||
return "[ErrCode:$code] ";
|
||||
}
|
||||
|
||||
@@ -24,6 +24,11 @@ try {
|
||||
$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, $composer["extra"]["exclude_annotate"] ?? [])) {
|
||||
$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, ZMUtil::getClassesPsr4($autoload_path, $namespace));
|
||||
@@ -61,3 +66,17 @@ try {
|
||||
fclose($stderr);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/*
|
||||
记迷惑,这里的代码是不是可以放到一个单独的文件里面,这样就不会出现每次都要重新加载的问题了?
|
||||
然后这个文件就实现了,就是这个。
|
||||
但是还有个什么问题呢?为了 reload 牺牲了太多太多,但是关键时刻好像又不是很能用到。
|
||||
但又不能没有。
|
||||
所以我很纠结很纠结。
|
||||
如何让用户的代码能像 php-fpm 那样随时重置呢?
|
||||
我不知道诶。
|
||||
那这段代码干了个啥?
|
||||
在最开始单独启动进程,加载一遍所有类,获取需要在启动前就执行的类,然后在启动的时候执行。
|
||||
这样就可以不在爷进程里面加载所有类,在爹进程里面 Fork 的子进程再加载所有类,每次 reload 时可以重新加载了。
|
||||
以上均为乱写的,请勿完全当真,本人对待框架代码还是比较认真的。
|
||||
*/
|
||||
Reference in New Issue
Block a user