mirror of
https://github.com/zhamao-robot/zhamao-framework.git
synced 2026-07-20 23:25:35 +08:00
Compare commits
74 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c04130fed1 | ||
|
|
7fefcb850a | ||
|
|
1fe54d4b94 | ||
|
|
13a32bec79 | ||
|
|
0f5786c8c4 | ||
|
|
4ed046769f | ||
|
|
690980f72d | ||
|
|
f025eeb34a | ||
|
|
d642f50ef1 | ||
|
|
2900754307 | ||
|
|
dffeac668d | ||
|
|
b6756179f5 | ||
|
|
1adcf76203 | ||
|
|
5b003ab575 | ||
|
|
75f6aa531e | ||
|
|
6e1f4820f8 | ||
|
|
50ce81334b | ||
|
|
0ed0aa089a | ||
|
|
102ba769ec | ||
|
|
e062f484b1 | ||
|
|
3be3e8412a | ||
|
|
ab5abf1c00 | ||
|
|
3aaa72cfb9 | ||
|
|
10f846c214 | ||
|
|
67a42c4be9 | ||
|
|
7e4e58a322 | ||
|
|
5de283d30c | ||
|
|
7513fd1a1d | ||
|
|
c635891e0b | ||
|
|
7b7a2d7010 | ||
|
|
23b1f797ad | ||
|
|
a72e0f705c | ||
|
|
504934f057 | ||
|
|
e976a96d0b | ||
|
|
dc7af105f4 | ||
|
|
fd96543268 | ||
|
|
cebc6fb310 | ||
|
|
c654cf40d5 | ||
|
|
68cc536af3 | ||
|
|
42412fee98 | ||
|
|
500ec4707e | ||
|
|
b09857e3b8 | ||
|
|
7d79126c8f | ||
|
|
a542558503 | ||
|
|
6697591a22 | ||
|
|
aad28f1ec4 | ||
|
|
f1949b1bd0 | ||
|
|
3b8aac5d8f | ||
|
|
5fd45c2542 | ||
|
|
af89c1b1f6 | ||
|
|
3287b96f30 | ||
|
|
00a8683658 | ||
|
|
c0ea068d04 | ||
|
|
9ba58ff90f | ||
|
|
1a1cf0ad30 | ||
|
|
1de93b9dc1 | ||
|
|
99e44eea3d | ||
|
|
e67958a8d1 | ||
|
|
23b3dc34e2 | ||
|
|
775672d515 | ||
|
|
40e17fab62 | ||
|
|
59fde3d075 | ||
|
|
a8183757be | ||
|
|
8ae5844649 | ||
|
|
acc96b78db | ||
|
|
802f975825 | ||
|
|
76ee308b91 | ||
|
|
d9eca5d7b1 | ||
|
|
5144bc2094 | ||
|
|
f7418de868 | ||
|
|
2bcbdcd3ca | ||
|
|
013c78dc77 | ||
|
|
886816e3d5 | ||
|
|
81db9c6ccb |
19
Dockerfile
19
Dockerfile
@@ -1,10 +1,25 @@
|
|||||||
FROM phpswoole/swoole:4.4.15-php7.3
|
FROM ubuntu:18.04
|
||||||
WORKDIR /app/
|
WORKDIR /app/
|
||||||
RUN echo "Asia/Shanghai" > /etc/timezone
|
RUN echo "Asia/Shanghai" > /etc/timezone
|
||||||
#RUN dpkg-configure -f noninteractive tzdata
|
|
||||||
ENV LANG C.UTF_8
|
ENV LANG C.UTF_8
|
||||||
|
ENV LC_ALL C.UTF-8
|
||||||
|
ENV LANGUAGE C.UTF-8
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y software-properties-common tzdata
|
||||||
|
RUN dpkg-reconfigure -f noninteractive tzdata
|
||||||
VOLUME ["/app/zhamao-framework/"]
|
VOLUME ["/app/zhamao-framework/"]
|
||||||
|
RUN add-apt-repository ppa:ondrej/php && \
|
||||||
|
apt-get update && \
|
||||||
|
apt-get install php php-dev php-mbstring gcc make openssl \
|
||||||
|
php-mbstring php-json php-curl php-mysql -y && \
|
||||||
|
apt-get install wget composer -y && \
|
||||||
|
wget https://github.com/swoole/swoole-src/archive/v4.5.0.tar.gz && \
|
||||||
|
tar -zxvf v4.5.0.tar.gz && \
|
||||||
|
cd swoole-src-4.5.0/ && \
|
||||||
|
phpize && ./configure --enable-openssl --enable-mysqlnd && make -j2 && make install && \
|
||||||
|
(echo "extension=swoole.so" >> $(php -i | grep "Loaded Configuration File" | awk '{print $5}'))
|
||||||
|
|
||||||
|
|
||||||
ADD . /app/zhamao-framework
|
ADD . /app/zhamao-framework
|
||||||
ADD . /app/zhamao-framework-bak
|
ADD . /app/zhamao-framework-bak
|
||||||
#RUN cd /app/zhamao-framework && composer update && composer clearcache
|
#RUN cd /app/zhamao-framework && composer update && composer clearcache
|
||||||
|
|||||||
46
README.md
46
README.md
@@ -1,36 +1,44 @@
|
|||||||
# zhamao-framework
|
<div align="center">
|
||||||
|
<img src="/resources/images/logo_trans.png" height = "150" alt="炸毛框架"><br>
|
||||||
|
<h2>炸毛框架</h2>
|
||||||
|
炸毛框架 (zhamao-frameowork) 是一个协程高性能的聊天机器人 + Web 服务器开发框架<br><br>
|
||||||
|
|
||||||
[]()
|
[]()
|
||||||
[](https://github.com/zhamao-robot/zhamao-framework/blob/master/LICENSE)
|
[](https://github.com/zhamao-robot/zhamao-framework/blob/master/LICENSE)
|
||||||
[]()
|
[](https://packagist.org/packages/zhamao/framework)
|
||||||
|
[]()
|
||||||
|
[]()
|
||||||
|
|
||||||
[](https://github.com/zhamao-robot/zhamao-framework/search?q=stupid)
|
[](https://github.com/zhamao-robot/zhamao-framework/search?q=stupid)
|
||||||
[](https://github.com/zhamao-robot/zhamao-framework/search?q=TODO)
|
[](https://github.com/zhamao-robot/zhamao-framework/search?q=TODO)
|
||||||
|
|
||||||
协程高性能的 **QQ 机器人 + Web 服务器** 开发框架(炸毛框架)。
|
</div>
|
||||||
|
|
||||||
<img src="https://avatars0.githubusercontent.com/u/48620312" height = "200" alt="炸毛框架" align=center/>
|
## 开发者注意
|
||||||
|
**由于 CQHTTP 不再提供维护,转为 [OneBot 标准](https://github.com/howmanybots/onebot)(原 CQHTTP 插件衍生而来的机器人 HTTP 接口标准),本框架也将在未来改为兼容此标准。**
|
||||||
|
|
||||||
|
**以上涉及的变更将在下一个大版本 (v2.0.0) 更新,请关注 2.0-dev 分支 和 Project 模块!**
|
||||||
|
|
||||||
|
**v2.0版本即将到来,请持续关注 [新文档](https://zhamao.xin/docs-v2/) 进度和 Project 模块展示的测试进度!**
|
||||||
|
|
||||||
## 简介
|
## 简介
|
||||||
zhamao-framework 是一个基于 酷Q 的 PHP Swoole 的机器人框架,它会对 QQ 机器人收到的消息进行解析处理,并以模块化的形式进行开发,来完成机器人的自然语言对话等功能。
|
zhamao-framework 是一个 PHP Swoole 的聊天机器人框架,兼容 OneBot 标准,它会对微信公众号等终端收到的消息进行解析处理,并以模块化的形式进行开发,来完成机器人的自然语言对话等功能。
|
||||||
|
|
||||||
框架对接 酷Q 的桥梁是 **CQHTTP** 插件,这里是它的[项目地址](https://github.com/richardchien/coolq-http-api/)。
|
|
||||||
|
|
||||||
除了起到解析消息的作用,炸毛框架 还提供了完整的 WebSocket + HTTP 服务器,你还能用此框架构建出高性能的 API 接口服务器。
|
除了起到解析消息的作用,炸毛框架 还提供了完整的 WebSocket + HTTP 服务器,你还能用此框架构建出高性能的 API 接口服务器。
|
||||||
|
|
||||||
## 开始
|
## 开始
|
||||||
1. 你可以使用项目的 `Use this template` 功能将框架克隆到你的公开或私有仓库进行开发
|
先安装环境,环境安装见下方文档。
|
||||||
2. 你也可以直接到 **Release** 中下载最新的 phar 包,放入文件夹后快速启动框架
|
1. `composer create-project zhamao/framework-starter` 从模板新建基础文档结构进行使用
|
||||||
|
2. 你也可以直接拉取本项目,进入文件夹后 `composer update` 加载依赖后使用 `bin/start init` 快速初始化框架文件
|
||||||
3. 还可以使用 Dockerfile 构建 Docker 容器
|
3. 还可以使用 Dockerfile 构建 Docker 容器
|
||||||
|
|
||||||
## 文档
|
## 文档
|
||||||
Pages托管:[https://framework.zhamao.xin/](https://framework.zhamao.xin/)
|
[https://framework.zhamao.xin/](https://framework.zhamao.xin/)
|
||||||
|
|
||||||
国内服务器:[https://framework2.zhamao.xin/](https://framework2.zhamao.xin/)
|
|
||||||
|
|
||||||
## 特点
|
## 特点
|
||||||
- 支持多账号
|
- 支持多账号
|
||||||
- 灵活的注解事件绑定机制
|
- 灵活的注解事件绑定机制
|
||||||
|
- 支持下断点调试(Psysh)
|
||||||
- 易用的上下文,模块内随处可用
|
- 易用的上下文,模块内随处可用
|
||||||
- 采用模块化编写,功能之间高内聚低耦合
|
- 采用模块化编写,功能之间高内聚低耦合
|
||||||
- 常驻内存,全局缓存变量随处使用
|
- 常驻内存,全局缓存变量随处使用
|
||||||
@@ -43,15 +51,13 @@ Pages托管:[https://framework.zhamao.xin/](https://framework.zhamao.xin/)
|
|||||||
|
|
||||||
| 模块名称 | 说明 | 模块地址 |
|
| 模块名称 | 说明 | 模块地址 |
|
||||||
| ------------------ | -------------------------------- | ------------------------------------------------------------ |
|
| ------------------ | -------------------------------- | ------------------------------------------------------------ |
|
||||||
| 微信公众号兼容模块 | 为框架提供微信公众号订阅号兼容层 | [zhamao-wechat-patch](https://github.com/zhamao-robot/zhamao-wechat-patch) |
|
|
||||||
| 通用模块 | 图片上传和下载模块 | [zhamao-general-tools](https://github.com/zhamao-robot/zhamao-general-tools) |
|
| 通用模块 | 图片上传和下载模块 | [zhamao-general-tools](https://github.com/zhamao-robot/zhamao-general-tools) |
|
||||||
|
|
||||||
## 计划开发内容
|
## 计划开发内容
|
||||||
- [ ] WebSocket测试脚本(客户端)
|
- [X] WebSocket测试脚本(客户端)
|
||||||
- [X] Session 和中间层管理模块
|
- [X] Session 和中间层管理模块
|
||||||
- [ ] 支持本地和远程两种方式的定时器(计划任务)
|
|
||||||
- [X] 常驻服务脚本
|
- [X] 常驻服务脚本
|
||||||
- [ ] 一些常用的通用 API 例如经济(用户积分、亲密度等)的模块
|
- [X] 一些常用的通用 API 例如经济(用户积分、亲密度等)的模块
|
||||||
- [ ] 图灵机器人/腾讯AI 聊天模块
|
- [ ] 图灵机器人/腾讯AI 聊天模块
|
||||||
- [ ] 分词模块(可能会放弃计划,因为目前好用的分词都是其他语言的)
|
- [ ] 分词模块(可能会放弃计划,因为目前好用的分词都是其他语言的)
|
||||||
- [ ] HTTP 过滤器、Auth 模块、完整的 MVC 兼容(可能会放弃计划,因为框架主打机器人开发)
|
- [ ] HTTP 过滤器、Auth 模块、完整的 MVC 兼容(可能会放弃计划,因为框架主打机器人开发)
|
||||||
@@ -78,11 +84,15 @@ Pages托管:[https://framework.zhamao.xin/](https://framework.zhamao.xin/)
|
|||||||
### 支付宝
|
### 支付宝
|
||||||

|

|
||||||
|
|
||||||
|
如果你对我们的周边感兴趣,我们还有炸毛机器人定制 logo 的雨伞,详情咨询作者 QQ,我们会作为您捐助了本项目!
|
||||||
|
|
||||||
## 关于
|
## 关于
|
||||||
框架和 SDK 是 炸毛机器人 项目的核心框架开源部分。炸毛机器人(3276124472)是作者写的一个高性能机器人,曾获全国计算机设计大赛一等奖。
|
框架和 SDK 是 炸毛机器人 项目的核心框架开源部分。炸毛机器人是作者写的一个高性能机器人,曾获全国计算机设计大赛一等奖。
|
||||||
|
|
||||||
欢迎随时在 HTTP-API 插件群里提问,当然更好的话可以加作者 QQ(627577391)或提交 Issue 进行疑难解答。
|
欢迎随时在 HTTP-API 插件群里提问,当然更好的话可以加作者 QQ(627577391)或提交 Issue 进行疑难解答。
|
||||||
|
|
||||||
本项目在更行内容时,请及时关注 GitHub 动态,更新前请将自己的模块代码做好备份。
|
本项目在更新内容时,请及时关注 GitHub 动态,更新前请将自己的模块代码做好备份。
|
||||||
|
|
||||||
项目框架采用 Apache-2.0 协议开源,在分发或重写修改等操作时需遵守协议。项目模块部分(`Module` 文件夹) 在非借鉴框架内代码时可不遵守 Apache-2.0 协议进行分发和修改(声明版权)。
|
项目框架采用 Apache-2.0 协议开源,在分发或重写修改等操作时需遵守协议。项目模块部分(`Module` 文件夹) 在非借鉴框架内代码时可不遵守 Apache-2.0 协议进行分发和修改(声明版权)。
|
||||||
|
|
||||||
|
**注意**:在你使用 mirai 等 `AGPL-3.0` 协议的机器人软件与框架连接时,使用本框架需要将你编写或修改的部分使用 `AGPL-3.0` 协议重新分发。
|
||||||
|
|||||||
@@ -4,9 +4,9 @@
|
|||||||
|
|
||||||
| Version | Supported |
|
| Version | Supported |
|
||||||
| ------- | ------------------ |
|
| ------- | ------------------ |
|
||||||
| 1.2.x | :white_check_mark: |
|
| 2.0 | :white_check_mark: |
|
||||||
| 1.1.x | :x: |
|
| 1.6.x | :white_check_mark: |
|
||||||
| 1.0.x | :x: |
|
| 1.x | :x: |
|
||||||
|
|
||||||
## Reporting a Vulnerability
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
92
bin/start
92
bin/start
@@ -10,6 +10,14 @@ require __DIR__ . '/../src/Scheduler/Scheduler.php';
|
|||||||
Swoole\Coroutine::set([
|
Swoole\Coroutine::set([
|
||||||
'max_coroutine' => 30000,
|
'max_coroutine' => 30000,
|
||||||
]);
|
]);
|
||||||
|
global $vendor_mode;
|
||||||
|
$vendor_mode = false;
|
||||||
|
if (mb_strpos(__DIR__, getcwd()) !== false && substr(str_replace(getcwd(), "", __DIR__), 0, 8) == "/vendor/") {
|
||||||
|
define("LOAD_MODE", 1); //composer项目模式
|
||||||
|
define("LOAD_MODE_COMPOSER_PATH", getcwd());
|
||||||
|
} else {
|
||||||
|
define("LOAD_MODE", 0); //正常模式
|
||||||
|
}
|
||||||
|
|
||||||
date_default_timezone_set("Asia/Shanghai");
|
date_default_timezone_set("Asia/Shanghai");
|
||||||
|
|
||||||
@@ -24,13 +32,86 @@ switch ($argv[1] ?? '') {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
case 'phar-build':
|
||||||
|
array_shift($argv);
|
||||||
|
require_once 'phar-build';
|
||||||
|
break;
|
||||||
|
case 'systemd':
|
||||||
|
array_shift($argv);
|
||||||
|
require_once 'systemd';
|
||||||
|
break;
|
||||||
|
case 'init':
|
||||||
|
array_shift($argv);
|
||||||
|
if (LOAD_MODE != 1) {
|
||||||
|
echo "initialization must be started with composer-project mode!\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
$cwd = LOAD_MODE_COMPOSER_PATH;
|
||||||
|
echo "Copying default module file ...";
|
||||||
|
@mkdir($cwd . "/config");
|
||||||
|
@mkdir($cwd . "/src");
|
||||||
|
@mkdir($cwd . "/src/Custom");
|
||||||
|
@mkdir($cwd . "/src/Module");
|
||||||
|
@mkdir($cwd . "/src/Module/Example");
|
||||||
|
@mkdir($cwd . "/src/Module/Middleware");
|
||||||
|
$ls = [
|
||||||
|
"/config/global.php",
|
||||||
|
"/.gitignore",
|
||||||
|
"/config/file_header.json",
|
||||||
|
"/config/motd.txt",
|
||||||
|
"/src/Module/Example/Hello.php",
|
||||||
|
"/src/Module/Middleware/TimerMiddleware.php",
|
||||||
|
"/src/Custom/global_function.php"
|
||||||
|
];
|
||||||
|
foreach($ls as $v) {
|
||||||
|
if(!file_exists($cwd.$v)) {
|
||||||
|
echo "Copying ".$v.PHP_EOL;
|
||||||
|
copy($cwd."/vendor/zhamao/framework".$v, $cwd.$v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$autoload = [
|
||||||
|
"psr-4" => [
|
||||||
|
"Module\\" => "src/Module",
|
||||||
|
"Custom\\" => "src/Custom"
|
||||||
|
],
|
||||||
|
"files" => [
|
||||||
|
"src/Custom/global_function.php"
|
||||||
|
]
|
||||||
|
];
|
||||||
|
$scripts = [
|
||||||
|
"server" => "vendor/bin/start server",
|
||||||
|
"server:log-debug" => "vendor/bin/start server --log-debug",
|
||||||
|
"server:log-verbose" => "vendor/bin/start server --log-verbose",
|
||||||
|
"server:log-info" => "vendor/bin/start server --log-info",
|
||||||
|
"server:log-warning" => "vendor/bin/start server --log-warning",
|
||||||
|
"server:debug-mode" => "vendor/bin/start server --debug-mode",
|
||||||
|
"systemd" => "vendor/bin/start systemd"
|
||||||
|
];
|
||||||
|
echo PHP_EOL;
|
||||||
|
if (file_exists($cwd . "/composer.json")) {
|
||||||
|
echo "Updating composer.json ...";
|
||||||
|
$composer = json_decode(file_get_contents($cwd . "/composer.json"), true);
|
||||||
|
if (!isset($composer["autoload"])) {
|
||||||
|
$composer["autoload"] = $autoload;
|
||||||
|
}
|
||||||
|
if (!isset($composer["scripts"])) {
|
||||||
|
$composer["scripts"] = $scripts;
|
||||||
|
}
|
||||||
|
file_put_contents($cwd . "/composer.json", json_encode($composer, 64 | 128 | 256));
|
||||||
|
echo PHP_EOL;
|
||||||
|
} else {
|
||||||
|
echo("Error occurred. Please check your updates.\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
echo "success!\n";
|
||||||
|
break;
|
||||||
case '':
|
case '':
|
||||||
case 'framework':
|
case 'framework':
|
||||||
case 'server':
|
case 'server':
|
||||||
if(!is_dir(__DIR__.'/../vendor/')){
|
if (!is_dir(__DIR__ . '/../vendor/') && LOAD_MODE == 0) {
|
||||||
echo "Warning: you have not update composer!\n";
|
echo "Warning: you have not update composer!\n";
|
||||||
exec("composer update", $out, $var);
|
exec("composer update", $out, $var);
|
||||||
if($var != 0) {
|
if ($var != 0) {
|
||||||
echo "You need to run \"composer update\" at root of zhamao-framework!\n";
|
echo "You need to run \"composer update\" at root of zhamao-framework!\n";
|
||||||
die;
|
die;
|
||||||
}
|
}
|
||||||
@@ -39,10 +120,13 @@ switch ($argv[1] ?? '') {
|
|||||||
break;
|
break;
|
||||||
case '--help':
|
case '--help':
|
||||||
case '-h':
|
case '-h':
|
||||||
echo "\nUsage: ".$argv[0]." [OPTION]\n";
|
echo "\nUsage: " . $argv[0] . " [OPTION]\n";
|
||||||
echo "\nzhamao-framework start script, provides several startup arguments.";
|
echo "\nzhamao-framework start script, provides several startup arguments.";
|
||||||
echo "\n\n -h, --help\t\tShow this help menu";
|
echo "\n\n -h, --help\t\tShow this help menu";
|
||||||
echo "\n framework, server\tstart main framework, this is default option\n\n";
|
echo "\n framework, server\tstart main framework, this is default option";
|
||||||
|
echo "\n phar-build\t\tbuild a new phar archive";
|
||||||
|
echo "\n init\t\t\tinitialize framework structure in this directory";
|
||||||
|
echo "\n systemd\t\tgenerate a new systemd \".service\" file to use\n\n";
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
echo "Unknown option \"{$argv[1]}\"!\n\"--help\" for more information\n";
|
echo "Unknown option \"{$argv[1]}\"!\n\"--help\" for more information\n";
|
||||||
|
|||||||
11
bin/systemd
11
bin/systemd
@@ -2,7 +2,8 @@
|
|||||||
<?php /** @since 1.2 */
|
<?php /** @since 1.2 */
|
||||||
switch ($argv[1] ?? '') {
|
switch ($argv[1] ?? '') {
|
||||||
case '--generate':
|
case '--generate':
|
||||||
generate();
|
case '':
|
||||||
|
generate($argv);
|
||||||
break;
|
break;
|
||||||
case '--help':
|
case '--help':
|
||||||
case '-h':
|
case '-h':
|
||||||
@@ -14,13 +15,17 @@ switch ($argv[1] ?? '') {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
function generate() {
|
function generate($argv) {
|
||||||
$s = "[Unit]\nDescription=zhamao-framework Daemon\nAfter=rc-local.service\n\n[Service]\nType=simple";
|
$s = "[Unit]\nDescription=zhamao-framework Daemon\nAfter=rc-local.service\n\n[Service]\nType=simple";
|
||||||
$s .= "\nUser=" . exec("whoami");
|
$s .= "\nUser=" . exec("whoami");
|
||||||
$s .= "\nGroup=" . exec("groups | awk '{print $1}'");
|
$s .= "\nGroup=" . exec("groups | awk '{print $1}'");
|
||||||
$s .= "\nWorkingDirectory=" . getcwd();
|
$s .= "\nWorkingDirectory=" . getcwd();
|
||||||
$s .= "\nExecStart=" . getcwd() . "/bin/start server --disable-console-input";
|
if ($argv[0] == "systemd" && !file_exists(getcwd() . '/systemd'))
|
||||||
|
$s .= "\nExecStart=" . getcwd() . "/vendor/bin/start server --disable-console-input";
|
||||||
|
else
|
||||||
|
$s .= "\nExecStart=" . getcwd() . "/bin/start server --disable-console-input";
|
||||||
$s .= "\nRestart=always\n\n[Install]\nWantedBy=multi-user.target\n";
|
$s .= "\nRestart=always\n\n[Install]\nWantedBy=multi-user.target\n";
|
||||||
|
@mkdir(getcwd() . "/resources/");
|
||||||
file_put_contents(getcwd() . "/resources/zhamao.service", $s);
|
file_put_contents(getcwd() . "/resources/zhamao.service", $s);
|
||||||
echo "File successfully generated. Path: " . getcwd() . "/resources/zhamao.service\n";
|
echo "File successfully generated. Path: " . getcwd() . "/resources/zhamao.service\n";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
composer update
|
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
{
|
{
|
||||||
"name": "zhamao/framework",
|
"name": "zhamao/framework",
|
||||||
"description": "high-performance intelligent assistant",
|
"description": "High performance QQ robot and web server development framework",
|
||||||
"minimum-stability": "stable",
|
"minimum-stability": "stable",
|
||||||
"license": "proprietary",
|
"license": "Apache-2.0",
|
||||||
"version": "1.3.0",
|
"version": "1.6.3",
|
||||||
"authors": [
|
"authors": [
|
||||||
{
|
{
|
||||||
"name": "whale",
|
"name": "whale",
|
||||||
@@ -14,20 +14,28 @@
|
|||||||
"email": "hugo_swift@yahoo.com"
|
"email": "hugo_swift@yahoo.com"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"prefer-stable": true,
|
||||||
|
"bin": [
|
||||||
|
"bin/start"
|
||||||
|
],
|
||||||
"require": {
|
"require": {
|
||||||
|
"php": ">=7.2",
|
||||||
"swoole/ide-helper": "@dev",
|
"swoole/ide-helper": "@dev",
|
||||||
"ext-mbstring": "*",
|
"ext-mbstring": "*",
|
||||||
"swlib/saber": "^1.0",
|
"swlib/saber": "^1.0",
|
||||||
"doctrine/annotations": "<1.10.2",
|
"doctrine/annotations": "~1.10",
|
||||||
"ext-json": "*",
|
"ext-json": "*",
|
||||||
"ext-posix": "*",
|
"ext-posix": "*",
|
||||||
"ext-ctype": "*",
|
"ext-ctype": "*",
|
||||||
"nikic/php-parser": "^4.4"
|
"ext-pdo": "*",
|
||||||
|
"psy/psysh": "@stable"
|
||||||
},
|
},
|
||||||
"repositories": {
|
"autoload": {
|
||||||
"packagist": {
|
"psr-4": {
|
||||||
"type": "composer",
|
"Custom\\": "src/Custom",
|
||||||
"url": "https://mirrors.aliyun.com/composer/"
|
"Framework\\": "src/Framework",
|
||||||
|
"ZM\\": "src/ZM",
|
||||||
|
"Module\\": "src/Module"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -8,23 +8,27 @@ $config['host'] = '0.0.0.0';
|
|||||||
$config['port'] = 20001;
|
$config['port'] = 20001;
|
||||||
|
|
||||||
/** 框架开到公网或外部的HTTP访问链接,通过 DataProvider::getFrameworkLink() 获取 */
|
/** 框架开到公网或外部的HTTP访问链接,通过 DataProvider::getFrameworkLink() 获取 */
|
||||||
$config['http_reverse_link'] = "http://127.0.0.1:".$config['port'];
|
$config['http_reverse_link'] = "http://127.0.0.1:" . $config['port'];
|
||||||
|
|
||||||
|
/** 框架是否启动debug模式 */
|
||||||
|
$config['debug_mode'] = false;
|
||||||
|
|
||||||
/** 存放框架内文件数据的目录 */
|
/** 存放框架内文件数据的目录 */
|
||||||
$config['zm_data'] = WORKING_DIR.'/zm_data/';
|
$config['zm_data'] = realpath(__DIR__ . "/../") . '/zm_data/';
|
||||||
|
|
||||||
/** 存放各个模块配置文件的目录 */
|
/** 存放各个模块配置文件的目录 */
|
||||||
$config['config_dir'] = $config['zm_data'].'config/';
|
$config['config_dir'] = $config['zm_data'] . 'config/';
|
||||||
|
|
||||||
/** 存放崩溃和运行日志的目录 */
|
/** 存放崩溃和运行日志的目录 */
|
||||||
$config['crash_dir'] = $config['zm_data'].'crash/';
|
$config['crash_dir'] = $config['zm_data'] . 'crash/';
|
||||||
|
|
||||||
/** 对应swoole的server->set参数 */
|
/** 对应swoole的server->set参数 */
|
||||||
$config['swoole'] = [
|
$config['swoole'] = [
|
||||||
'log_file' => $config['crash_dir'].'swoole_error.log',
|
'log_file' => $config['crash_dir'] . 'swoole_error.log',
|
||||||
'worker_num' => 1,
|
'worker_num' => 1,
|
||||||
'dispatch_mode' => 2,
|
'dispatch_mode' => 2,
|
||||||
'task_worker_num' => 0
|
//'task_worker_num' => 1,
|
||||||
|
//'task_enable_coroutine' => true
|
||||||
];
|
];
|
||||||
|
|
||||||
/** MySQL数据库连接信息,host留空则启动时不创建sql连接池 */
|
/** MySQL数据库连接信息,host留空则启动时不创建sql连接池 */
|
||||||
@@ -35,7 +39,13 @@ $config['sql_config'] = [
|
|||||||
'sql_database' => 'db_name',
|
'sql_database' => 'db_name',
|
||||||
'sql_password' => '',
|
'sql_password' => '',
|
||||||
'sql_enable_cache' => true,
|
'sql_enable_cache' => true,
|
||||||
'sql_reset_cache' => '0300'
|
'sql_reset_cache' => '0300',
|
||||||
|
'sql_options' => [
|
||||||
|
PDO::ATTR_STRINGIFY_FETCHES => false,
|
||||||
|
PDO::ATTR_EMULATE_PREPARES => false
|
||||||
|
],
|
||||||
|
'sql_no_exception' => false,
|
||||||
|
'sql_default_fetch_mode' => PDO::FETCH_BOTH // added in 1.5.6
|
||||||
];
|
];
|
||||||
|
|
||||||
/** CQHTTP连接约定的token */
|
/** CQHTTP连接约定的token */
|
||||||
@@ -70,10 +80,15 @@ $config['context_class'] = \ZM\Context\Context::class;
|
|||||||
/** 静态文件访问 */
|
/** 静态文件访问 */
|
||||||
$config['static_file_server'] = [
|
$config['static_file_server'] = [
|
||||||
'status' => false,
|
'status' => false,
|
||||||
'document_root' => WORKING_DIR . '/resources/html',
|
'document_root' => realpath(__DIR__ . "/../") . '/resources/html',
|
||||||
'document_index' => [
|
'document_index' => [
|
||||||
'index.html'
|
'index.html'
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/** 注册 Swoole Server 事件注解的类列表 */
|
||||||
|
$config['server_event_handler_class'] = [
|
||||||
|
\Framework\ServerEventHandler::class,
|
||||||
|
];
|
||||||
|
|
||||||
return $config;
|
return $config;
|
||||||
|
|||||||
6
config/motd.txt
Normal file
6
config/motd.txt
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
______
|
||||||
|
|__ / |__ __ _ _ __ ___ __ _ ___
|
||||||
|
/ /| '_ \ / _` | '_ ` _ \ / _` |/ _ \
|
||||||
|
/ /_| | | | (_| | | | | | | (_| | (_) |
|
||||||
|
/____|_| |_|\__,_|_| |_| |_|\__,_|\___/
|
||||||
|
|
||||||
@@ -1,14 +1,15 @@
|
|||||||
FROM richardchien/cqhttp:latest
|
FROM richardchien/cqhttp:latest
|
||||||
RUN apt-get update && apt-get install -y software-properties-common && \
|
RUN apt-get update && apt-get install -y software-properties-common tzdata
|
||||||
add-apt-repository ppa:ondrej/php && \
|
RUN dpkg-reconfigure -f noninteractive tzdata
|
||||||
|
RUN add-apt-repository ppa:ondrej/php && \
|
||||||
apt-get update && \
|
apt-get update && \
|
||||||
apt-get install php7.3 php7.3-dev php7.3-mbstring gcc make openssl \
|
apt-get install php php-dev php-mbstring gcc make openssl \
|
||||||
php7.3-mbstring php7.3-json php7.3-ctype php7.3-curl -y && \
|
php-mbstring php-json php-curl php-mysql -y && \
|
||||||
apt-get install wget composer -y && \
|
apt-get install wget composer -y && \
|
||||||
wget https://github.com/swoole/swoole-src/archive/v4.5.0.tar.gz && \
|
wget https://github.com/swoole/swoole-src/archive/v4.5.0.tar.gz && \
|
||||||
tar -zxvf v4.5.0.tar.gz && \
|
tar -zxvf v4.5.0.tar.gz && \
|
||||||
cd swoole-src-4.5.0/ && \
|
cd swoole-src-4.5.0/ && \
|
||||||
phpize7.3 && ./configure --enable-openssl && make -j2 && make install && \
|
phpize && ./configure --enable-openssl --enable-mysqlnd && make -j2 && make install && \
|
||||||
(echo "extension=swoole.so" >> $(php -i | grep "Loaded Configuration File" | awk '{print $5}'))
|
(echo "extension=swoole.so" >> $(php -i | grep "Loaded Configuration File" | awk '{print $5}'))
|
||||||
ADD start.sh /home/user/start.sh
|
ADD start.sh /home/user/start.sh
|
||||||
RUN chown user:user /home/user/start.sh && chmod +x /home/user/start.sh
|
RUN chown user:user /home/user/start.sh && chmod +x /home/user/start.sh
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ Swoole\Coroutine::set([
|
|||||||
date_default_timezone_set("Asia/Shanghai");
|
date_default_timezone_set("Asia/Shanghai");
|
||||||
|
|
||||||
define('WORKING_DIR', __DIR__);
|
define('WORKING_DIR', __DIR__);
|
||||||
|
define('FRAMEWORK_DIR', __DIR__);
|
||||||
|
define('LOAD_MODE', 2);
|
||||||
|
|
||||||
$s = new FrameworkLoader($argv);
|
$s = new FrameworkLoader($argv);
|
||||||
|
|
||||||
@@ -41,6 +43,7 @@ function loadPhp($dir) {
|
|||||||
loadPhp($path);
|
loadPhp($path);
|
||||||
} else {
|
} else {
|
||||||
if (pathinfo($dir . '/' . $v)['extension'] == 'php') {
|
if (pathinfo($dir . '/' . $v)['extension'] == 'php') {
|
||||||
|
if(pathinfo($dir . '/' . $v)['basename'] == 'terminal_listener.php') continue;
|
||||||
//echo 'loading '.$path.PHP_EOL;
|
//echo 'loading '.$path.PHP_EOL;
|
||||||
require_once $path;
|
require_once $path;
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
resources/images/logo_trans.png
Normal file
BIN
resources/images/logo_trans.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 61 KiB |
@@ -1,7 +1,3 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
//这里写你的全局函数
|
//这里写你的全局函数
|
||||||
|
|
||||||
function phptest(){
|
|
||||||
echo "Nothing.\n";
|
|
||||||
}
|
|
||||||
@@ -8,12 +8,19 @@
|
|||||||
|
|
||||||
namespace Framework;
|
namespace Framework;
|
||||||
|
|
||||||
use co;
|
use ZM\Annotation\Swoole\SwooleEventAt;
|
||||||
|
use ZM\Connection\WSConnection;
|
||||||
use ZM\Utils\ZMUtil;
|
use ZM\Utils\ZMUtil;
|
||||||
use Exception;
|
use Exception;
|
||||||
|
|
||||||
class Console
|
class Console
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @var false|resource
|
||||||
|
*/
|
||||||
|
public static $console_proc = null;
|
||||||
|
public static $pipes = [];
|
||||||
|
|
||||||
static function setColor($string, $color = "") {
|
static function setColor($string, $color = "") {
|
||||||
switch ($color) {
|
switch ($color) {
|
||||||
case "red":
|
case "red":
|
||||||
@@ -119,8 +126,8 @@ class Console
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static function debug($obj) {
|
static function debug($msg) {
|
||||||
debug($obj);
|
if (ZMBuf::$atomics["info_level"]->get() >= 4) Console::log(date("[H:i:s] ") . "[D] " . $msg, 'gray');
|
||||||
}
|
}
|
||||||
|
|
||||||
static function log($obj, $color = "") {
|
static function log($obj, $color = "") {
|
||||||
@@ -153,15 +160,57 @@ class Console
|
|||||||
}
|
}
|
||||||
|
|
||||||
static function listenConsole() {
|
static function listenConsole() {
|
||||||
if (in_array('--disable-console-input', FrameworkLoader::$argv)) {
|
if (in_array('--disable-console-input', FrameworkLoader::$argv) || in_array('--debug-mode', FrameworkLoader::$argv)) {
|
||||||
self::info("ConsoleCommand disabled.");
|
self::info("ConsoleCommand disabled.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
go(function () {
|
global $terminal_id;
|
||||||
while (true) {
|
global $port;
|
||||||
$cmd = trim(co::fread(STDIN));
|
$port = ZMBuf::globals("port");
|
||||||
if (self::executeCommand($cmd) === false) break;
|
$vss = new SwooleEventAt();
|
||||||
|
$vss->type = "open";
|
||||||
|
$vss->level = 256;
|
||||||
|
$vss->rule = "connectType:terminal";
|
||||||
|
$terminal_id = call_user_func(function () {
|
||||||
|
try {
|
||||||
|
$data = random_bytes(16);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
return "";
|
||||||
}
|
}
|
||||||
|
$data[6] = chr(ord($data[6]) & 0x0f | 0x40);
|
||||||
|
$data[8] = chr(ord($data[8]) & 0x3f | 0x80);
|
||||||
|
return strtoupper(vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4)));
|
||||||
|
});
|
||||||
|
$vss->callback = function(?WSConnection $conn) use ($terminal_id){
|
||||||
|
$req = ctx()->getRequest();
|
||||||
|
if($conn->getType() != "terminal") return false;
|
||||||
|
Console::debug("Terminal fd: ".$conn->fd);
|
||||||
|
if(($req->header["x-terminal-id"] ?? "") != $terminal_id) {
|
||||||
|
$conn->close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
ZMBuf::$events[SwooleEventAt::class][] = $vss;
|
||||||
|
$vss2 = new SwooleEventAt();
|
||||||
|
$vss2->type = "message";
|
||||||
|
$vss2->rule = "connectType:terminal";
|
||||||
|
$vss2->callback = function(?WSConnection $conn){
|
||||||
|
if ($conn === null) return false;
|
||||||
|
if($conn->getType() != "terminal") return false;
|
||||||
|
$cmd = ctx()->getFrame()->data;
|
||||||
|
self::executeCommand($cmd);
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
ZMBuf::$events[SwooleEventAt::class][] = $vss2;
|
||||||
|
go(function () {
|
||||||
|
global $terminal_id, $port;
|
||||||
|
$descriptorspec = array(
|
||||||
|
0 => STDIN,
|
||||||
|
1 => STDOUT,
|
||||||
|
2 => STDERR
|
||||||
|
);
|
||||||
|
self::$console_proc = proc_open('php -r \'$terminal_id = "'.$terminal_id.'";$port = '.$port.';require "'.__DIR__.'/terminal_listener.php";\'', $descriptorspec, $pipes);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,14 +258,14 @@ class Console
|
|||||||
return false;
|
return false;
|
||||||
case 'save':
|
case 'save':
|
||||||
$origin = ZMBuf::$atomics["info_level"]->get();
|
$origin = ZMBuf::$atomics["info_level"]->get();
|
||||||
ZMBuf::$atomics["info_level"]->set(3);
|
//ZMBuf::$atomics["info_level"]->set(3);
|
||||||
DataProvider::saveBuffer();
|
DataProvider::saveBuffer();
|
||||||
ZMBuf::$atomics["info_level"]->set($origin);
|
//ZMBuf::$atomics["info_level"]->set($origin);
|
||||||
return true;
|
return true;
|
||||||
case '':
|
case '':
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
Console::info("Command not found: " . $it[0]);
|
Console::info("Command not found: " . $cmd);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
namespace Framework;
|
namespace Framework;
|
||||||
|
|
||||||
|
|
||||||
|
use ZM\Annotation\Swoole\OnSave;
|
||||||
|
|
||||||
class DataProvider
|
class DataProvider
|
||||||
{
|
{
|
||||||
public static $buffer_list = [];
|
public static $buffer_list = [];
|
||||||
@@ -13,12 +15,10 @@ class DataProvider
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static function getWorkingDir() {
|
public static function getWorkingDir() {
|
||||||
global $is_phar;
|
if(LOAD_MODE == 0) return WORKING_DIR;
|
||||||
if ($is_phar === true) {
|
elseif (LOAD_MODE == 1) return LOAD_MODE_COMPOSER_PATH;
|
||||||
return realpath('.');
|
elseif (LOAD_MODE == 2) return realpath('.');
|
||||||
} else {
|
return null;
|
||||||
return WORKING_DIR;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getDataConfig() {
|
public static function getDataConfig() {
|
||||||
@@ -40,6 +40,13 @@ class DataProvider
|
|||||||
Console::debug("Saving " . $k . " to " . $v);
|
Console::debug("Saving " . $k . " to " . $v);
|
||||||
self::setJsonData($v, ZMBuf::get($k));
|
self::setJsonData($v, ZMBuf::get($k));
|
||||||
}
|
}
|
||||||
|
foreach (ZMBuf::$events[OnSave::class] ?? [] as $v) {
|
||||||
|
$c = $v->class;
|
||||||
|
$method = $v->method;
|
||||||
|
$class = new $c();
|
||||||
|
Console::debug("Calling @OnSave: $c -> $method");
|
||||||
|
$class->$method();
|
||||||
|
}
|
||||||
if (ZMBuf::$atomics["info_level"]->get() >= 3)
|
if (ZMBuf::$atomics["info_level"]->get() >= 3)
|
||||||
echo Console::setColor("saved", "blue") . PHP_EOL;
|
echo Console::setColor("saved", "blue") . PHP_EOL;
|
||||||
}
|
}
|
||||||
@@ -53,9 +60,12 @@ class DataProvider
|
|||||||
return json_decode(file_get_contents(self::getDataConfig() . $string), true);
|
return json_decode(file_get_contents(self::getDataConfig() . $string), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function setJsonData($filename, array $args) {
|
public static function setJsonData($filename, array $args) {
|
||||||
$pathinfo = pathinfo($filename);
|
$pathinfo = pathinfo($filename);
|
||||||
if (!is_dir($pathinfo["dirname"])) mkdir(self::getDataConfig() . $pathinfo["dirname"]);
|
if (!is_dir(self::getDataConfig() . $pathinfo["dirname"])) {
|
||||||
|
Console::debug("Making Directory: " . self::getDataConfig() . $pathinfo["dirname"]);
|
||||||
|
mkdir(self::getDataConfig() . $pathinfo["dirname"]);
|
||||||
|
}
|
||||||
$r = file_put_contents(self::getDataConfig() . $filename, json_encode($args, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_BIGINT_AS_STRING));
|
$r = file_put_contents(self::getDataConfig() . $filename, json_encode($args, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_BIGINT_AS_STRING));
|
||||||
if ($r === false) {
|
if ($r === false) {
|
||||||
Console::warning("无法保存文件: " . $filename);
|
Console::warning("无法保存文件: " . $filename);
|
||||||
|
|||||||
@@ -3,14 +3,13 @@
|
|||||||
|
|
||||||
namespace Framework;
|
namespace Framework;
|
||||||
|
|
||||||
use Co;
|
use Doctrine\Common\Annotations\AnnotationReader;
|
||||||
use Swoole\Http\Request;
|
use ReflectionClass;
|
||||||
|
use ReflectionMethod;
|
||||||
use Swoole\Runtime;
|
use Swoole\Runtime;
|
||||||
use Swoole\WebSocket\Frame;
|
use ZM\Annotation\Swoole\OnEvent;
|
||||||
use ZM\Event\EventHandler;
|
|
||||||
use Exception;
|
use Exception;
|
||||||
use Swoole\WebSocket\Server;
|
use Swoole\WebSocket\Server;
|
||||||
use ZM\Http\Response;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class FrameworkLoader
|
* Class FrameworkLoader
|
||||||
@@ -36,19 +35,31 @@ class FrameworkLoader
|
|||||||
private $server;
|
private $server;
|
||||||
|
|
||||||
public function __construct($args = []) {
|
public function __construct($args = []) {
|
||||||
if (self::$instance !== null) die("Cannot run two FrameworkLoader in one process!");
|
|
||||||
self::$instance = $this;
|
|
||||||
self::$argv = $args;
|
|
||||||
$this->requireGlobalFunctions();
|
$this->requireGlobalFunctions();
|
||||||
if (!isPharMode()) {
|
if (LOAD_MODE == 0) define("WORKING_DIR", getcwd());
|
||||||
define('WORKING_DIR', getcwd());
|
elseif (LOAD_MODE == 1) define("WORKING_DIR", realpath(__DIR__ . "/../../"));
|
||||||
} else {
|
elseif (LOAD_MODE == 2) echo "Phar mode: " . WORKING_DIR . PHP_EOL;
|
||||||
echo "Phar mode: " . WORKING_DIR . PHP_EOL;
|
//$this->registerAutoloader('classLoader');
|
||||||
|
require_once "DataProvider.php";
|
||||||
|
if (file_exists(DataProvider::getWorkingDir() . "/vendor/autoload.php")) {
|
||||||
|
/** @noinspection PhpIncludeInspection */
|
||||||
|
require_once DataProvider::getWorkingDir() . "/vendor/autoload.php";
|
||||||
|
}
|
||||||
|
if (LOAD_MODE == 2) {
|
||||||
|
require_once FRAMEWORK_DIR . "/vendor/autoload.php";
|
||||||
|
spl_autoload_register('phar_classloader');
|
||||||
}
|
}
|
||||||
$this->registerAutoloader('classLoader');
|
|
||||||
Runtime::enableCoroutine();
|
|
||||||
|
|
||||||
|
|
||||||
|
self::$settings = new GlobalConfig();
|
||||||
|
if (self::$settings->get("debug_mode") === true) {
|
||||||
|
$args[] = "--debug-mode";
|
||||||
|
$args[] = "--disable-console-input";
|
||||||
|
}
|
||||||
|
self::$argv = $args;
|
||||||
|
if (!in_array("--debug-mode", self::$argv)) {
|
||||||
|
Runtime::enableCoroutine(true, SWOOLE_HOOK_ALL);
|
||||||
|
}
|
||||||
self::$settings = new GlobalConfig();
|
self::$settings = new GlobalConfig();
|
||||||
ZMBuf::$globals = self::$settings;
|
ZMBuf::$globals = self::$settings;
|
||||||
if (!self::$settings->success) die("Failed to load global config. Please check config/global.php file");
|
if (!self::$settings->success) die("Failed to load global config. Please check config/global.php file");
|
||||||
@@ -58,7 +69,6 @@ class FrameworkLoader
|
|||||||
$this->selfCheck();
|
$this->selfCheck();
|
||||||
try {
|
try {
|
||||||
$this->server = new Server(self::$settings->get("host"), self::$settings->get("port"));
|
$this->server = new Server(self::$settings->get("host"), self::$settings->get("port"));
|
||||||
if (in_array("--remote-shell", $args)) RemoteShell::listen($this->server, "127.0.0.1");
|
|
||||||
$settings = self::$settings->get("swoole");
|
$settings = self::$settings->get("swoole");
|
||||||
if (in_array("--daemon", $args)) {
|
if (in_array("--daemon", $args)) {
|
||||||
$settings["daemonize"] = 1;
|
$settings["daemonize"] = 1;
|
||||||
@@ -66,25 +76,38 @@ class FrameworkLoader
|
|||||||
self::$argv[] = "--disable-console-input";
|
self::$argv[] = "--disable-console-input";
|
||||||
}
|
}
|
||||||
$this->server->set($settings);
|
$this->server->set($settings);
|
||||||
$this->server->on("WorkerStart", [$this, "onWorkerStart"]);
|
$all_event_class = self::$settings->get("server_event_handler_class") ?? [];
|
||||||
$this->server->on("message", function ($server, Frame $frame) {
|
if (!in_array(ServerEventHandler::class, $all_event_class)) {
|
||||||
Console::debug("Calling Swoole \"message\" from fd=" . $frame->fd);
|
$all_event_class[] = ServerEventHandler::class;
|
||||||
EventHandler::callSwooleEvent("message", $server, $frame);
|
}
|
||||||
});
|
$event_list = [];
|
||||||
$this->server->on("request", function ($request, $response) {
|
foreach ($all_event_class as $v) {
|
||||||
$response = new Response($response);
|
$reader = new AnnotationReader();
|
||||||
Console::debug("Receiving Http request event, cid=" . Co::getCid());
|
$reflection_class = new ReflectionClass($v);
|
||||||
EventHandler::callSwooleEvent("request", $request, $response);
|
$methods = $reflection_class->getMethods(ReflectionMethod::IS_PUBLIC);
|
||||||
});
|
foreach ($methods as $vs) {
|
||||||
$this->server->on("open", function ($server, Request $request) {
|
$method_annotations = $reader->getMethodAnnotations($vs);
|
||||||
Console::debug("Calling Swoole \"open\" event from fd=" . $request->fd);
|
if ($method_annotations != []) {
|
||||||
EventHandler::callSwooleEvent("open", $server, $request);
|
$annotation = $method_annotations[0];
|
||||||
});
|
if ($annotation instanceof OnEvent) {
|
||||||
$this->server->on("close", function ($server, $fd) {
|
$annotation->class = $v;
|
||||||
Console::debug("Calling Swoole \"close\" event from fd=" . $fd);
|
$annotation->method = $vs->getName();
|
||||||
EventHandler::callSwooleEvent("close", $server, $fd);
|
$event_list[strtolower($annotation->event)] = $annotation;
|
||||||
});
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach ($event_list as $k => $v) {
|
||||||
|
$this->server->on($k, function (...$param) use ($v) {
|
||||||
|
$c = $v->class;
|
||||||
|
//echo $c.PHP_EOL;
|
||||||
|
$c = new $c();
|
||||||
|
call_user_func_array([$c, $v->method], $param);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
ZMBuf::initAtomic();
|
ZMBuf::initAtomic();
|
||||||
|
if (in_array("--remote-shell", $args)) RemoteShell::listen($this->server, "127.0.0.1");
|
||||||
if (in_array("--log-error", $args)) ZMBuf::$atomics["info_level"]->set(0);
|
if (in_array("--log-error", $args)) ZMBuf::$atomics["info_level"]->set(0);
|
||||||
if (in_array("--log-warning", $args)) ZMBuf::$atomics["info_level"]->set(1);
|
if (in_array("--log-warning", $args)) ZMBuf::$atomics["info_level"]->set(1);
|
||||||
if (in_array("--log-info", $args)) ZMBuf::$atomics["info_level"]->set(2);
|
if (in_array("--log-info", $args)) ZMBuf::$atomics["info_level"]->set(2);
|
||||||
@@ -94,11 +117,17 @@ class FrameworkLoader
|
|||||||
"host: " . self::$settings->get("host") .
|
"host: " . self::$settings->get("host") .
|
||||||
", port: " . self::$settings->get("port") .
|
", port: " . self::$settings->get("port") .
|
||||||
", log_level: " . ZMBuf::$atomics["info_level"]->get() .
|
", log_level: " . ZMBuf::$atomics["info_level"]->get() .
|
||||||
", version: " . json_decode(file_get_contents(WORKING_DIR . "/composer.json"), true)["version"] .
|
", version: " . ZM_VERSION .
|
||||||
"\nworking_dir: ".(isPharMode() ? realpath('.') : WORKING_DIR)
|
"\nworking_dir: " . DataProvider::getWorkingDir()
|
||||||
);
|
);
|
||||||
global $motd;
|
global $motd;
|
||||||
echo $motd . PHP_EOL;
|
if (!file_exists(DataProvider::getWorkingDir() . "/config/motd.txt")) {
|
||||||
|
echo $motd;
|
||||||
|
} else {
|
||||||
|
echo file_get_contents(DataProvider::getWorkingDir() . "/config/motd.txt");
|
||||||
|
}
|
||||||
|
if (in_array("--debug-mode", self::$argv))
|
||||||
|
Console::warning("You are in debug mode, do not use in production!");
|
||||||
$this->server->start();
|
$this->server->start();
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
Console::error("Framework初始化出现错误,请检查!");
|
Console::error("Framework初始化出现错误,请检查!");
|
||||||
@@ -111,13 +140,10 @@ class FrameworkLoader
|
|||||||
require_once __DIR__ . '/global_functions.php';
|
require_once __DIR__ . '/global_functions.php';
|
||||||
}
|
}
|
||||||
|
|
||||||
private function registerAutoloader(string $string) {
|
|
||||||
if (!spl_autoload_register($string)) die("Failed to register autoloader named \"$string\" !");
|
|
||||||
}
|
|
||||||
|
|
||||||
private function defineProperties() {
|
private function defineProperties() {
|
||||||
define("ZM_START_TIME", microtime(true));
|
define("ZM_START_TIME", microtime(true));
|
||||||
define("ZM_DATA", self::$settings->get("zm_data"));
|
define("ZM_DATA", self::$settings->get("zm_data"));
|
||||||
|
define("ZM_VERSION", json_decode(file_get_contents(__DIR__ . "/../../composer.json"), true)["version"] ?? "unknown");
|
||||||
define("CONFIG_DIR", self::$settings->get("config_dir"));
|
define("CONFIG_DIR", self::$settings->get("config_dir"));
|
||||||
define("CRASH_DIR", self::$settings->get("crash_dir"));
|
define("CRASH_DIR", self::$settings->get("crash_dir"));
|
||||||
@mkdir(ZM_DATA);
|
@mkdir(ZM_DATA);
|
||||||
@@ -127,12 +153,17 @@ class FrameworkLoader
|
|||||||
define("ZM_MATCH_FIRST", 1);
|
define("ZM_MATCH_FIRST", 1);
|
||||||
define("ZM_MATCH_NUMBER", 2);
|
define("ZM_MATCH_NUMBER", 2);
|
||||||
define("ZM_MATCH_SECOND", 3);
|
define("ZM_MATCH_SECOND", 3);
|
||||||
|
define("ZM_BREAKPOINT", 'if(in_array("--debug-mode", \Framework\FrameworkLoader::$argv)) extract(\Psy\debug(get_defined_vars(), isset($this) ? $this : @get_called_class()));');
|
||||||
|
define("BP", ZM_BREAKPOINT);
|
||||||
|
define("ZM_DEFAULT_FETCH_MODE", self::$settings->get("sql_config")["sql_default_fetch_mode"] ?? 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function selfCheck() {
|
private function selfCheck() {
|
||||||
if (!extension_loaded("swoole")) die("Can not find swoole extension.\n");
|
if (!extension_loaded("swoole")) die("Can not find swoole extension.\n");
|
||||||
|
if (version_compare(SWOOLE_VERSION, "4.4.13") == -1) die("You must install swoole version >= 4.4.13 !");
|
||||||
//if (!extension_loaded("gd")) die("Can not find gd extension.\n");
|
//if (!extension_loaded("gd")) die("Can not find gd extension.\n");
|
||||||
if (!extension_loaded("sockets")) die("Can not find sockets extension.\n");
|
if (!extension_loaded("sockets")) die("Can not find sockets extension.\n");
|
||||||
|
if (!extension_loaded("ctype")) die("Can not find ctype extension.\n");
|
||||||
if (!function_exists("mb_substr")) die("Can not find mbstring extension.\n");
|
if (!function_exists("mb_substr")) die("Can not find mbstring extension.\n");
|
||||||
if (substr(PHP_VERSION, 0, 1) != "7") die("PHP >=7 required.\n");
|
if (substr(PHP_VERSION, 0, 1) != "7") die("PHP >=7 required.\n");
|
||||||
//if (!function_exists("curl_exec")) die("Can not find curl extension.\n");
|
//if (!function_exists("curl_exec")) die("Can not find curl extension.\n");
|
||||||
@@ -140,12 +171,6 @@ class FrameworkLoader
|
|||||||
//if (!file_exists(CRASH_DIR . "last_error.log")) die("Can not find log file.\n");
|
//if (!file_exists(CRASH_DIR . "last_error.log")) die("Can not find log file.\n");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function onWorkerStart(\Swoole\Server $server, $worker_id) {
|
|
||||||
self::$instance = $this;
|
|
||||||
self::$run_time = microtime(true);
|
|
||||||
EventHandler::callSwooleEvent("WorkerStart", $server, $worker_id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
global $motd;
|
global $motd;
|
||||||
@@ -156,5 +181,6 @@ $motd = <<<EOL
|
|||||||
/ /_| | | | (_| | | | | | | (_| | (_) |
|
/ /_| | | | (_| | | | | | | (_| | (_) |
|
||||||
/____|_| |_|\__,_|_| |_| |_|\__,_|\___/
|
/____|_| |_|\__,_|_| |_| |_|\__,_|\___/
|
||||||
|
|
||||||
|
|
||||||
EOL;
|
EOL;
|
||||||
|
|
||||||
|
|||||||
84
src/Framework/ServerEventHandler.php
Normal file
84
src/Framework/ServerEventHandler.php
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace Framework;
|
||||||
|
|
||||||
|
|
||||||
|
use Co;
|
||||||
|
use Doctrine\Common\Annotations\AnnotationException;
|
||||||
|
use Swoole\Http\Request;
|
||||||
|
use Swoole\Server;
|
||||||
|
use Swoole\WebSocket\Frame;
|
||||||
|
use ZM\Annotation\AnnotationParser;
|
||||||
|
use ZM\Annotation\Swoole\OnEvent;
|
||||||
|
use ZM\Connection\ConnectionManager;
|
||||||
|
use ZM\Event\EventHandler;
|
||||||
|
use ZM\Http\Response;
|
||||||
|
|
||||||
|
class ServerEventHandler
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @OnEvent("WorkerStart")
|
||||||
|
* @param Server $server
|
||||||
|
* @param $worker_id
|
||||||
|
* @throws AnnotationException
|
||||||
|
* @throws \ReflectionException
|
||||||
|
*/
|
||||||
|
public function onWorkerStart(Server $server, $worker_id) {
|
||||||
|
if ($server->taskworker === false) {
|
||||||
|
FrameworkLoader::$run_time = microtime(true);
|
||||||
|
EventHandler::callSwooleEvent("WorkerStart", $server, $worker_id);
|
||||||
|
} else {
|
||||||
|
ob_start();
|
||||||
|
AnnotationParser::registerMods();
|
||||||
|
//加载Custom目录下的自定义的内部类
|
||||||
|
ConnectionManager::registerCustomClass();
|
||||||
|
ob_get_clean();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @OnEvent("message")
|
||||||
|
* @param $server
|
||||||
|
* @param Frame $frame
|
||||||
|
* @throws AnnotationException
|
||||||
|
*/
|
||||||
|
public function onMessage($server, Frame $frame) {
|
||||||
|
Console::debug("Calling Swoole \"message\" from fd=" . $frame->fd);
|
||||||
|
EventHandler::callSwooleEvent("message", $server, $frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @OnEvent("request")
|
||||||
|
* @param $request
|
||||||
|
* @param $response
|
||||||
|
* @throws AnnotationException
|
||||||
|
*/
|
||||||
|
public function onRequest($request, $response) {
|
||||||
|
$response = new Response($response);
|
||||||
|
Console::debug("Receiving Http request event, cid=" . Co::getCid());
|
||||||
|
EventHandler::callSwooleEvent("request", $request, $response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @OnEvent("open")
|
||||||
|
* @param $server
|
||||||
|
* @param Request $request
|
||||||
|
* @throws AnnotationException
|
||||||
|
*/
|
||||||
|
public function onOpen($server, Request $request) {
|
||||||
|
Console::debug("Calling Swoole \"open\" event from fd=" . $request->fd);
|
||||||
|
EventHandler::callSwooleEvent("open", $server, $request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @OnEvent("close")
|
||||||
|
* @param $server
|
||||||
|
* @param $fd
|
||||||
|
* @throws AnnotationException
|
||||||
|
*/
|
||||||
|
public function onClose($server, $fd) {
|
||||||
|
Console::debug("Calling Swoole \"close\" event from fd=" . $fd);
|
||||||
|
EventHandler::callSwooleEvent("close", $server, $fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,9 +9,9 @@
|
|||||||
namespace Framework;
|
namespace Framework;
|
||||||
|
|
||||||
use Swoole\Atomic;
|
use Swoole\Atomic;
|
||||||
|
use Swoole\Database\PDOPool;
|
||||||
use swoole_atomic;
|
use swoole_atomic;
|
||||||
use ZM\connection\WSConnection;
|
use ZM\connection\WSConnection;
|
||||||
use ZM\Utils\SQLPool;
|
|
||||||
|
|
||||||
class ZMBuf
|
class ZMBuf
|
||||||
{
|
{
|
||||||
@@ -24,7 +24,7 @@ class ZMBuf
|
|||||||
static $scheduler = null; //This is stupid warning...
|
static $scheduler = null; //This is stupid warning...
|
||||||
|
|
||||||
//Swoole SQL连接池,多进程下每个进程一个连接池
|
//Swoole SQL连接池,多进程下每个进程一个连接池
|
||||||
/** @var SQLPool */
|
/** @var PDOPool */
|
||||||
static $sql_pool = null;//保存sql连接池的类
|
static $sql_pool = null;//保存sql连接池的类
|
||||||
|
|
||||||
//只读的数据,可以在多worker_num下使用
|
//只读的数据,可以在多worker_num下使用
|
||||||
@@ -51,6 +51,8 @@ class ZMBuf
|
|||||||
public static $config = [];
|
public static $config = [];
|
||||||
public static $context = [];
|
public static $context = [];
|
||||||
public static $instance = [];
|
public static $instance = [];
|
||||||
|
public static $context_class = [];
|
||||||
|
public static $server_events = [];
|
||||||
|
|
||||||
static function get($name, $default = null) {
|
static function get($name, $default = null) {
|
||||||
return self::$cache[$name] ?? $default;
|
return self::$cache[$name] ?? $default;
|
||||||
|
|||||||
@@ -3,17 +3,17 @@
|
|||||||
use Framework\Console;
|
use Framework\Console;
|
||||||
use Framework\DataProvider;
|
use Framework\DataProvider;
|
||||||
use Framework\ZMBuf;
|
use Framework\ZMBuf;
|
||||||
|
use Swoole\Coroutine\System;
|
||||||
use ZM\Context\ContextInterface;
|
use ZM\Context\ContextInterface;
|
||||||
|
use ZM\Utils\ZMUtil;
|
||||||
|
|
||||||
function isPharMode() {
|
|
||||||
return substr(__DIR__, 0, 7) == 'phar://';
|
|
||||||
}
|
|
||||||
|
|
||||||
function classLoader($p) {
|
function phar_classloader($p){
|
||||||
$filepath = getClassPath($p);
|
$filepath = getClassPath($p);
|
||||||
if ($filepath === null)
|
if($filepath === null) {
|
||||||
echo "F:Warning: get class path wrongs.$p\n";
|
Console::debug("F:Warning: get class path wrongs.$p");
|
||||||
//else echo "F:DBG: Found " . $p . "\n";
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
require_once $filepath;
|
require_once $filepath;
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
@@ -70,6 +70,7 @@ function unicode_decode($str) {
|
|||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
function getAllClasses($dir, $indoor_name) {
|
function getAllClasses($dir, $indoor_name) {
|
||||||
|
if(!is_dir($dir)) return [];
|
||||||
$list = scandir($dir);
|
$list = scandir($dir);
|
||||||
$classes = [];
|
$classes = [];
|
||||||
unset($list[0], $list[1]);
|
unset($list[0], $list[1]);
|
||||||
@@ -162,10 +163,10 @@ function matchArgs($pattern, $context) {
|
|||||||
function set_coroutine_params($array) {
|
function set_coroutine_params($array) {
|
||||||
$cid = Co::getCid();
|
$cid = Co::getCid();
|
||||||
if ($cid == -1) die("Cannot set coroutine params at none coroutine mode.");
|
if ($cid == -1) die("Cannot set coroutine params at none coroutine mode.");
|
||||||
if(isset(ZMBuf::$context[$cid])) ZMBuf::$context[$cid] = array_merge(ZMBuf::$context[$cid], $array);
|
if (isset(ZMBuf::$context[$cid])) ZMBuf::$context[$cid] = array_merge(ZMBuf::$context[$cid], $array);
|
||||||
else ZMBuf::$context[$cid] = $array;
|
else ZMBuf::$context[$cid] = $array;
|
||||||
foreach (ZMBuf::$context as $c => $v) {
|
foreach (ZMBuf::$context as $c => $v) {
|
||||||
if (!Co::exists($c)) unset(ZMBuf::$context[$c]);
|
if (!Co::exists($c)) unset(ZMBuf::$context[$c], ZMBuf::$context_class[$c]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,23 +174,54 @@ function set_coroutine_params($array) {
|
|||||||
* @return ContextInterface|null
|
* @return ContextInterface|null
|
||||||
*/
|
*/
|
||||||
function context() {
|
function context() {
|
||||||
|
return ctx();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return ContextInterface|null
|
||||||
|
*/
|
||||||
|
function ctx() {
|
||||||
$cid = Co::getCid();
|
$cid = Co::getCid();
|
||||||
$c_class = ZMBuf::globals("context_class");
|
$c_class = ZMBuf::globals("context_class");
|
||||||
if (isset(ZMBuf::$context[$cid])) {
|
if (isset(ZMBuf::$context[$cid])) {
|
||||||
return new $c_class($cid);
|
return ZMBuf::$context_class[$cid] ?? (ZMBuf::$context_class[$cid] = new $c_class($cid));
|
||||||
} else {
|
} else {
|
||||||
Console::debug("未找到当前协程的上下文($cid),正在找父进程的上下文");
|
Console::debug("未找到当前协程的上下文($cid),正在找父进程的上下文");
|
||||||
while (($pcid = Co::getPcid($cid)) !== -1) {
|
while (($pcid = Co::getPcid($cid)) !== -1) {
|
||||||
$cid = $pcid;
|
$cid = $pcid;
|
||||||
if (isset(ZMBuf::$context[$cid])) return new $c_class($cid);
|
if (isset(ZMBuf::$context[$cid])) return ZMBuf::$context_class[$cid] ?? (ZMBuf::$context_class[$cid] = new $c_class($cid));
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function ctx() { return context(); }
|
function debug($msg) { Console::debug($msg); }
|
||||||
|
|
||||||
function debug($msg) {
|
function zm_sleep($s = 1) { Co::sleep($s); }
|
||||||
if (ZMBuf::$atomics["info_level"]->get() >= 4)
|
|
||||||
Console::log(date("[H:i:s] ") . "[D] " . $msg, 'gray');
|
function zm_exec($cmd): array { return System::exec($cmd); }
|
||||||
|
|
||||||
|
function zm_cid() { return Co::getCid(); }
|
||||||
|
|
||||||
|
function zm_yield() { Co::yield(); }
|
||||||
|
|
||||||
|
function zm_resume(int $cid) { Co::resume($cid); }
|
||||||
|
|
||||||
|
function zm_timer_after($ms, callable $callable) {
|
||||||
|
go(function () use ($ms, $callable) {
|
||||||
|
ZMUtil::checkWait();
|
||||||
|
Swoole\Timer::after($ms, $callable);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function zm_timer_tick($ms, callable $callable) {
|
||||||
|
go(function () use ($ms, $callable) {
|
||||||
|
ZMUtil::checkWait();
|
||||||
|
Console::debug("Adding extra timer tick of " . $ms . " ms");
|
||||||
|
Swoole\Timer::tick($ms, $callable);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
33
src/Framework/terminal_listener.php
Normal file
33
src/Framework/terminal_listener.php
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Swoole\Coroutine\Http\Client;
|
||||||
|
|
||||||
|
Co\run(function (){
|
||||||
|
hello:
|
||||||
|
global $terminal_id, $port;
|
||||||
|
$client = new Client("127.0.0.1", $port);
|
||||||
|
$client->set(['websocket_mask' => true]);
|
||||||
|
$client->setHeaders(["x-terminal-id" => $terminal_id, 'x-pid' => posix_getppid()]);
|
||||||
|
$ret = $client->upgrade("/?type=terminal");
|
||||||
|
if ($ret) {
|
||||||
|
while (true) {
|
||||||
|
$line = fgets(STDIN);
|
||||||
|
if ($line !== false) {
|
||||||
|
$r = $client->push(trim($line));
|
||||||
|
if (trim($line) == "reload" || trim($line) == "r" || trim($line) == "stop") {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if($r === false) {
|
||||||
|
echo "Unable to connect framework terminal, connection closed. Trying to reconnect after 5s.\n";
|
||||||
|
sleep(5);
|
||||||
|
goto hello;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
echo "Unable to connect framework terminal. port: $port\n";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
@@ -10,7 +10,6 @@ use ZM\Annotation\Http\Middleware;
|
|||||||
use ZM\Annotation\Http\RequestMapping;
|
use ZM\Annotation\Http\RequestMapping;
|
||||||
use ZM\Annotation\Swoole\SwooleEventAt;
|
use ZM\Annotation\Swoole\SwooleEventAt;
|
||||||
use ZM\Connection\CQConnection;
|
use ZM\Connection\CQConnection;
|
||||||
use ZM\ModBase;
|
|
||||||
use ZM\Utils\ZMUtil;
|
use ZM\Utils\ZMUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -18,7 +17,7 @@ use ZM\Utils\ZMUtil;
|
|||||||
* @package Module\Example
|
* @package Module\Example
|
||||||
* @since 1.0
|
* @since 1.0
|
||||||
*/
|
*/
|
||||||
class Hello extends ModBase
|
class Hello
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* 在机器人连接后向终端输出信息
|
* 在机器人连接后向终端输出信息
|
||||||
@@ -29,9 +28,18 @@ class Hello extends ModBase
|
|||||||
Console::info("机器人 " . $conn->getQQ() . " 已连接!");
|
Console::info("机器人 " . $conn->getQQ() . " 已连接!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在机器人连接后向终端输出信息
|
||||||
|
* @SwooleEventAt("close",rule="connectType:qq")
|
||||||
|
*/
|
||||||
|
public function onDisconnect() {
|
||||||
|
$conn = ctx()->getConnection();
|
||||||
|
Console::info("机器人 " . $conn->getQQ() . " 已断开连接!");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 向机器人发送"你好",即可回复这句话
|
* 向机器人发送"你好",即可回复这句话
|
||||||
* @CQCommand("你好")
|
* @CQCommand(match="你好",alias={"你好啊","你是谁"})
|
||||||
*/
|
*/
|
||||||
public function hello() {
|
public function hello() {
|
||||||
return "你好啊,我是由炸毛框架构建的机器人!";
|
return "你好啊,我是由炸毛框架构建的机器人!";
|
||||||
@@ -73,6 +81,7 @@ class Hello extends ModBase
|
|||||||
/**
|
/**
|
||||||
* 默认示例页面
|
* 默认示例页面
|
||||||
* @RequestMapping("/index")
|
* @RequestMapping("/index")
|
||||||
|
* @RequestMapping("/")
|
||||||
*/
|
*/
|
||||||
public function index() {
|
public function index() {
|
||||||
return "Hello Zhamao!";
|
return "Hello Zhamao!";
|
||||||
|
|||||||
@@ -201,7 +201,7 @@ class CQAPI
|
|||||||
* @param CQConnection $connection
|
* @param CQConnection $connection
|
||||||
* @param $reply
|
* @param $reply
|
||||||
* @param |null $function
|
* @param |null $function
|
||||||
* @return bool
|
* @return bool|array
|
||||||
*/
|
*/
|
||||||
public static function processAPI($connection, $reply, $function = null) {
|
public static function processAPI($connection, $reply, $function = null) {
|
||||||
$api_id = ZMBuf::$atomics["wait_msg_id"]->get();
|
$api_id = ZMBuf::$atomics["wait_msg_id"]->get();
|
||||||
@@ -243,7 +243,7 @@ class CQAPI
|
|||||||
Console::warning("CQAPI send failed, websocket push error.");
|
Console::warning("CQAPI send failed, websocket push error.");
|
||||||
$response = [
|
$response = [
|
||||||
"status" => "failed",
|
"status" => "failed",
|
||||||
"retcode" => 999,
|
"retcode" => -1000,
|
||||||
"data" => null,
|
"data" => null,
|
||||||
"self_id" => $connection->getQQ()
|
"self_id" => $connection->getQQ()
|
||||||
];
|
];
|
||||||
@@ -251,7 +251,7 @@ class CQAPI
|
|||||||
if (($s["func"] ?? null) !== null)
|
if (($s["func"] ?? null) !== null)
|
||||||
call_user_func($s["func"], $response, $reply);
|
call_user_func($s["func"], $response, $reply);
|
||||||
ZMBuf::unsetByValue("sent_api", $reply["echo"]);
|
ZMBuf::unsetByValue("sent_api", $reply["echo"]);
|
||||||
if ($function === true) return null;
|
if ($function === true) return $response;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,10 @@
|
|||||||
namespace ZM\Annotation;
|
namespace ZM\Annotation;
|
||||||
|
|
||||||
use Doctrine\Common\Annotations\{AnnotationException, AnnotationReader};
|
use Doctrine\Common\Annotations\{AnnotationException, AnnotationReader};
|
||||||
|
use Co;
|
||||||
use Framework\{Console, ZMBuf};
|
use Framework\{Console, ZMBuf};
|
||||||
|
use Error;
|
||||||
|
use Exception;
|
||||||
use ReflectionClass;
|
use ReflectionClass;
|
||||||
use ReflectionException;
|
use ReflectionException;
|
||||||
use ReflectionMethod;
|
use ReflectionMethod;
|
||||||
@@ -16,15 +19,17 @@ use ZM\Annotation\CQ\{CQAfter,
|
|||||||
CQMessage,
|
CQMessage,
|
||||||
CQMetaEvent,
|
CQMetaEvent,
|
||||||
CQNotice,
|
CQNotice,
|
||||||
CQRequest};
|
CQRequest
|
||||||
|
};
|
||||||
use ZM\Annotation\Http\{After, Before, Controller, HandleException, Middleware, MiddlewareClass, RequestMapping};
|
use ZM\Annotation\Http\{After, Before, Controller, HandleException, Middleware, MiddlewareClass, RequestMapping};
|
||||||
use Swoole\Timer;
|
use Swoole\Timer;
|
||||||
use ZM\Annotation\Interfaces\CustomAnnotation;
|
use ZM\Annotation\Interfaces\CustomAnnotation;
|
||||||
use ZM\Annotation\Interfaces\Level;
|
use ZM\Annotation\Interfaces\Level;
|
||||||
use ZM\Annotation\Module\{Closed, InitBuffer, SaveBuffer};
|
use ZM\Annotation\Module\{Closed, InitBuffer, LoadBuffer, SaveBuffer};
|
||||||
use ZM\Annotation\Swoole\{OnStart, OnTick, SwooleEventAfter, SwooleEventAt};
|
use ZM\Annotation\Swoole\{OnSave, OnStart, OnTick, SwooleEventAfter, SwooleEventAt};
|
||||||
use ZM\Annotation\Interfaces\Rule;
|
use ZM\Annotation\Interfaces\Rule;
|
||||||
use ZM\Connection\WSConnection;
|
use ZM\Connection\WSConnection;
|
||||||
|
use ZM\Event\EventHandler;
|
||||||
use ZM\Http\MiddlewareInterface;
|
use ZM\Http\MiddlewareInterface;
|
||||||
use Framework\DataProvider;
|
use Framework\DataProvider;
|
||||||
use ZM\Utils\ZMUtil;
|
use ZM\Utils\ZMUtil;
|
||||||
@@ -51,7 +56,7 @@ class AnnotationParser
|
|||||||
$class_prefix = '';
|
$class_prefix = '';
|
||||||
$methods = $reflection_class->getMethods(ReflectionMethod::IS_PUBLIC);
|
$methods = $reflection_class->getMethods(ReflectionMethod::IS_PUBLIC);
|
||||||
$class_annotations = $reader->getClassAnnotations($reflection_class);
|
$class_annotations = $reader->getClassAnnotations($reflection_class);
|
||||||
$middleware_addon = null;
|
$middleware_addon = [];
|
||||||
foreach ($class_annotations as $vs) {
|
foreach ($class_annotations as $vs) {
|
||||||
if ($vs instanceof Closed) {
|
if ($vs instanceof Closed) {
|
||||||
continue 2;
|
continue 2;
|
||||||
@@ -61,6 +66,9 @@ class AnnotationParser
|
|||||||
} elseif ($vs instanceof SaveBuffer) {
|
} elseif ($vs instanceof SaveBuffer) {
|
||||||
Console::debug("注册自动保存的缓存变量: " . $vs->buf_name . " (Dir:" . $vs->sub_folder . ")");
|
Console::debug("注册自动保存的缓存变量: " . $vs->buf_name . " (Dir:" . $vs->sub_folder . ")");
|
||||||
DataProvider::addSaveBuffer($vs->buf_name, $vs->sub_folder);
|
DataProvider::addSaveBuffer($vs->buf_name, $vs->sub_folder);
|
||||||
|
} elseif ($vs instanceof LoadBuffer) {
|
||||||
|
Console::debug("注册到内存的缓存变量: " . $vs->buf_name . " (Dir:" . $vs->sub_folder . ")");
|
||||||
|
ZMBuf::set($vs->buf_name, DataProvider::getJsonData(($vs->sub_folder ?? "") . "/" . $vs->buf_name . ".json"));
|
||||||
} elseif ($vs instanceof InitBuffer) {
|
} elseif ($vs instanceof InitBuffer) {
|
||||||
ZMBuf::set($vs->buf_name, []);
|
ZMBuf::set($vs->buf_name, []);
|
||||||
} elseif ($vs instanceof MiddlewareClass) {
|
} elseif ($vs instanceof MiddlewareClass) {
|
||||||
@@ -90,18 +98,24 @@ class AnnotationParser
|
|||||||
ZMBuf::$events[MiddlewareClass::class][$result["name"]] = $result;
|
ZMBuf::$events[MiddlewareClass::class][$result["name"]] = $result;
|
||||||
continue 2;
|
continue 2;
|
||||||
} elseif ($vs instanceof Middleware) {
|
} elseif ($vs instanceof Middleware) {
|
||||||
$middleware_addon = $vs;
|
$middleware_addon[] = $vs;
|
||||||
|
} elseif ($vs instanceof CustomAnnotation) {
|
||||||
|
$vs->class = $reflection_class->getName();
|
||||||
|
ZMBuf::$events[get_class($vs)][] = $vs;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
foreach ($methods as $vs) {
|
foreach ($methods as $vs) {
|
||||||
if ($middleware_addon !== null) {
|
if ($middleware_addon !== []) {
|
||||||
Console::debug("Added middleware ".$middleware_addon->middleware . " to $v -> ".$vs->getName());
|
foreach($middleware_addon as $value){
|
||||||
ZMBuf::$events[MiddlewareInterface::class][$v][$vs->getName()][] = $middleware_addon->middleware;
|
Console::debug("Added middleware " . $value->middleware . " to $v -> " . $vs->getName());
|
||||||
|
ZMBuf::$events[MiddlewareInterface::class][$v][$vs->getName()][] = $value->middleware;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
$method_annotations = $reader->getMethodAnnotations($vs);
|
$method_annotations = $reader->getMethodAnnotations($vs);
|
||||||
foreach ($method_annotations as $vss) {
|
foreach ($method_annotations as $vss) {
|
||||||
if ($vss instanceof Rule) $vss = self::registerRuleEvent($vss, $vs, $reflection_class);
|
if ($vss instanceof Rule) $vss = self::registerRuleEvent($vss, $vs, $reflection_class);
|
||||||
else $vss = self::registerMethod($vss, $vs, $reflection_class);
|
else $vss = self::registerMethod($vss, $vs, $reflection_class);
|
||||||
|
Console::debug("寻找 " . $vs->getName() . " -> " . get_class($vss));
|
||||||
|
|
||||||
if ($vss instanceof SwooleEventAt) ZMBuf::$events[SwooleEventAt::class][] = $vss;
|
if ($vss instanceof SwooleEventAt) ZMBuf::$events[SwooleEventAt::class][] = $vss;
|
||||||
elseif ($vss instanceof SwooleEventAfter) ZMBuf::$events[SwooleEventAfter::class][] = $vss;
|
elseif ($vss instanceof SwooleEventAfter) ZMBuf::$events[SwooleEventAfter::class][] = $vss;
|
||||||
@@ -116,6 +130,7 @@ class AnnotationParser
|
|||||||
elseif ($vss instanceof CQBefore) ZMBuf::$events[CQBefore::class][$vss->cq_event][] = $vss;
|
elseif ($vss instanceof CQBefore) ZMBuf::$events[CQBefore::class][$vss->cq_event][] = $vss;
|
||||||
elseif ($vss instanceof CQAfter) ZMBuf::$events[CQAfter::class][$vss->cq_event][] = $vss;
|
elseif ($vss instanceof CQAfter) ZMBuf::$events[CQAfter::class][$vss->cq_event][] = $vss;
|
||||||
elseif ($vss instanceof OnStart) ZMBuf::$events[OnStart::class][] = $vss;
|
elseif ($vss instanceof OnStart) ZMBuf::$events[OnStart::class][] = $vss;
|
||||||
|
elseif ($vss instanceof OnSave) ZMBuf::$events[OnSave::class][] = $vss;
|
||||||
elseif ($vss instanceof Middleware) ZMBuf::$events[MiddlewareInterface::class][$vss->class][$vss->method][] = $vss->middleware;
|
elseif ($vss instanceof Middleware) ZMBuf::$events[MiddlewareInterface::class][$vss->class][$vss->method][] = $vss->middleware;
|
||||||
elseif ($vss instanceof OnTick) self::addTimerTick($vss);
|
elseif ($vss instanceof OnTick) self::addTimerTick($vss);
|
||||||
elseif ($vss instanceof CQAPISend) ZMBuf::$events[CQAPISend::class][] = $vss;
|
elseif ($vss instanceof CQAPISend) ZMBuf::$events[CQAPISend::class][] = $vss;
|
||||||
@@ -141,13 +156,14 @@ class AnnotationParser
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Console::debug("解析注解完毕!");
|
||||||
if (ZMBuf::isset("timer_count")) {
|
if (ZMBuf::isset("timer_count")) {
|
||||||
Console::info("Added " . ZMBuf::get("timer_count") . " timer(s)!");
|
Console::info("Added " . ZMBuf::get("timer_count") . " timer(s)!");
|
||||||
ZMBuf::unsetCache("timer_count");
|
ZMBuf::unsetCache("timer_count");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function getRuleCallback($rule_str) {
|
public static function getRuleCallback($rule_str) {
|
||||||
$func = null;
|
$func = null;
|
||||||
$rule = $rule_str;
|
$rule = $rule_str;
|
||||||
if ($rule != "") {
|
if ($rule != "") {
|
||||||
@@ -157,7 +173,8 @@ class AnnotationParser
|
|||||||
//Swoole 事件时走此switch
|
//Swoole 事件时走此switch
|
||||||
switch ($asp_name) {
|
switch ($asp_name) {
|
||||||
case "connectType": //websocket连接类型
|
case "connectType": //websocket连接类型
|
||||||
$func = function (WSConnection $connection) use ($rest) {
|
$func = function (?WSConnection $connection) use ($rest) {
|
||||||
|
if ($connection === null) return false;
|
||||||
return $connection->getType() == $rest ? true : false;
|
return $connection->getType() == $rest ? true : false;
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
@@ -225,14 +242,14 @@ class AnnotationParser
|
|||||||
return $func;
|
return $func;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function registerRuleEvent(?AnnotationBase $vss, ReflectionMethod $method, ReflectionClass $class) {
|
public static function registerRuleEvent(?AnnotationBase $vss, ReflectionMethod $method, ReflectionClass $class) {
|
||||||
$vss->callback = self::getRuleCallback($vss->getRule());
|
$vss->callback = self::getRuleCallback($vss->getRule());
|
||||||
$vss->method = $method->getName();
|
$vss->method = $method->getName();
|
||||||
$vss->class = $class->getName();
|
$vss->class = $class->getName();
|
||||||
return $vss;
|
return $vss;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function registerMethod(?AnnotationBase $vss, ReflectionMethod $method, ReflectionClass $class) {
|
public static function registerMethod(?AnnotationBase $vss, ReflectionMethod $method, ReflectionClass $class) {
|
||||||
$vss->method = $method->getName();
|
$vss->method = $method->getName();
|
||||||
$vss->class = $class->getName();
|
$vss->class = $class->getName();
|
||||||
return $vss;
|
return $vss;
|
||||||
@@ -315,6 +332,7 @@ class AnnotationParser
|
|||||||
$class = getAllClasses(DataProvider::getWorkingDir() . "/src/Custom/Annotation/", "Custom\\Annotation");
|
$class = getAllClasses(DataProvider::getWorkingDir() . "/src/Custom/Annotation/", "Custom\\Annotation");
|
||||||
foreach ($class as $v) {
|
foreach ($class as $v) {
|
||||||
$s = DataProvider::getWorkingDir() . '/src/' . str_replace("\\", "/", $v) . ".php";
|
$s = DataProvider::getWorkingDir() . '/src/' . str_replace("\\", "/", $v) . ".php";
|
||||||
|
Console::debug("Requiring custom annotation " . $s);
|
||||||
require_once $s;
|
require_once $s;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -331,6 +349,42 @@ class AnnotationParser
|
|||||||
|
|
||||||
private static function addTimerTick(?OnTick $vss) {
|
private static function addTimerTick(?OnTick $vss) {
|
||||||
ZMBuf::set("timer_count", ZMBuf::get("timer_count", 0) + 1);
|
ZMBuf::set("timer_count", ZMBuf::get("timer_count", 0) + 1);
|
||||||
Timer::tick($vss->tick_ms, [ZMUtil::getModInstance($vss->class), $vss->method]);
|
$class = ZMUtil::getModInstance($vss->class);
|
||||||
|
$method = $vss->method;
|
||||||
|
$ms = $vss->tick_ms;
|
||||||
|
$cid = go(function () use ($class, $method, $ms) {
|
||||||
|
Co::suspend();
|
||||||
|
$plain_class = get_class($class);
|
||||||
|
if (!isset(ZMBuf::$events[MiddlewareInterface::class][$plain_class][$method])) {
|
||||||
|
Console::debug("Added timer: " . $plain_class . " -> " . $method);
|
||||||
|
Timer::tick($ms, function () use ($class, $method) {
|
||||||
|
set_coroutine_params([]);
|
||||||
|
try {
|
||||||
|
$class->$method();
|
||||||
|
} catch (Exception $e) {
|
||||||
|
Console::error("Uncaught error from TimerTick: " . $e->getMessage() . " at " . $e->getFile() . "({$e->getLine()})");
|
||||||
|
} catch (Error $e) {
|
||||||
|
Console::error("Uncaught fatal error from TimerTick: " . $e->getMessage());
|
||||||
|
echo Console::setColor($e->getTraceAsString(), "gray");
|
||||||
|
Console::error("Please check your code!");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Console::debug("Added Middleware-based timer: " . $plain_class . " -> " . $method);
|
||||||
|
Timer::tick($ms, function () use ($class, $method) {
|
||||||
|
set_coroutine_params([]);
|
||||||
|
try {
|
||||||
|
EventHandler::callWithMiddleware($class, $method, [], []);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
Console::error("Uncaught error from TimerTick: " . $e->getMessage() . " at " . $e->getFile() . "({$e->getLine()})");
|
||||||
|
} catch (Error $e) {
|
||||||
|
Console::error("Uncaught fatal error from TimerTick: " . $e->getMessage());
|
||||||
|
echo Console::setColor($e->getTraceAsString(), "gray");
|
||||||
|
Console::error("Please check your code!");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ZMBuf::append("paused_tick", $cid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,11 @@ class CQAPISend extends AnnotationBase implements Level
|
|||||||
*/
|
*/
|
||||||
public $action = "";
|
public $action = "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
public $with_result = false;
|
||||||
|
|
||||||
public $level = 20;
|
public $level = 20;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -19,6 +19,18 @@ class CQCommand extends AnnotationBase implements Level
|
|||||||
public $match = "";
|
public $match = "";
|
||||||
/** @var string */
|
/** @var string */
|
||||||
public $regexMatch = "";
|
public $regexMatch = "";
|
||||||
|
/** @var string */
|
||||||
|
public $fullMatch = "";
|
||||||
|
/** @var string[] */
|
||||||
|
public $alias = [];
|
||||||
|
/** @var string */
|
||||||
|
public $message_type = "";
|
||||||
|
/** @var int */
|
||||||
|
public $user_id = 0;
|
||||||
|
/** @var int */
|
||||||
|
public $group_id = 0;
|
||||||
|
/** @var int */
|
||||||
|
public $discuss_id = 0;
|
||||||
/** @var int */
|
/** @var int */
|
||||||
public $level = 20;
|
public $level = 20;
|
||||||
|
|
||||||
@@ -32,4 +44,4 @@ class CQCommand extends AnnotationBase implements Level
|
|||||||
*/
|
*/
|
||||||
public function setLevel(int $level) { $this->level = $level; }
|
public function setLevel(int $level) { $this->level = $level; }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
24
src/ZM/Annotation/Module/LoadBuffer.php
Normal file
24
src/ZM/Annotation/Module/LoadBuffer.php
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace ZM\Annotation\Module;
|
||||||
|
|
||||||
|
use Doctrine\Common\Annotations\Annotation\Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class LoadBuffer
|
||||||
|
* @package ZM\Annotation\Module
|
||||||
|
* @Annotation
|
||||||
|
* @Target("CLASS")
|
||||||
|
*/
|
||||||
|
class LoadBuffer
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
* @Required()
|
||||||
|
*/
|
||||||
|
public $buf_name;
|
||||||
|
|
||||||
|
/** @var string $sub_folder */
|
||||||
|
public $sub_folder = null;
|
||||||
|
}
|
||||||
@@ -17,9 +17,10 @@ class SaveBuffer
|
|||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var string
|
* @var string
|
||||||
*@Required()
|
* @Required()
|
||||||
*/
|
*/
|
||||||
public $buf_name;
|
public $buf_name;
|
||||||
|
|
||||||
/** @var string $sub_folder */
|
/** @var string $sub_folder */
|
||||||
public $sub_folder = null;
|
public $sub_folder = null;
|
||||||
}
|
}
|
||||||
|
|||||||
24
src/ZM/Annotation/Swoole/OnEvent.php
Normal file
24
src/ZM/Annotation/Swoole/OnEvent.php
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace ZM\Annotation\Swoole;
|
||||||
|
|
||||||
|
|
||||||
|
use Doctrine\Common\Annotations\Annotation\Required;
|
||||||
|
use Doctrine\Common\Annotations\Annotation\Target;
|
||||||
|
use ZM\Annotation\AnnotationBase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class OnEvent
|
||||||
|
* @package ZM\Annotation\Swoole
|
||||||
|
* @Annotation
|
||||||
|
* @Target("METHOD")
|
||||||
|
*/
|
||||||
|
class OnEvent extends AnnotationBase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
* @Required()
|
||||||
|
*/
|
||||||
|
public $event;
|
||||||
|
}
|
||||||
18
src/ZM/Annotation/Swoole/OnSave.php
Normal file
18
src/ZM/Annotation/Swoole/OnSave.php
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace ZM\Annotation\Swoole;
|
||||||
|
|
||||||
|
use Doctrine\Common\Annotations\Annotation\Target;
|
||||||
|
use ZM\Annotation\AnnotationBase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class OnSave
|
||||||
|
* @package ZM\Annotation\Swoole
|
||||||
|
* @Annotation
|
||||||
|
* @Target("METHOD")
|
||||||
|
*/
|
||||||
|
class OnSave extends AnnotationBase
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
15
src/ZM/Annotation/Swoole/OnTaskWorkerStart.php
Normal file
15
src/ZM/Annotation/Swoole/OnTaskWorkerStart.php
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace ZM\Annotation\Swoole;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class OnTaskWorkerStart
|
||||||
|
* @package ZM\Annotation\Swoole
|
||||||
|
* @Annotation
|
||||||
|
* @Target("METHOD")
|
||||||
|
*/
|
||||||
|
class OnTaskWorkerStart
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
@@ -47,6 +47,8 @@ class ConnectionManager
|
|||||||
return WCConnection::class;
|
return WCConnection::class;
|
||||||
case "proxy":
|
case "proxy":
|
||||||
return ProxyConnection::class;
|
return ProxyConnection::class;
|
||||||
|
case "terminal":
|
||||||
|
return TerminalConnection::class;
|
||||||
default:
|
default:
|
||||||
foreach (ZMBuf::$custom_connection_class as $v) {
|
foreach (ZMBuf::$custom_connection_class as $v) {
|
||||||
/** @var WSConnection $r */
|
/** @var WSConnection $r */
|
||||||
|
|||||||
13
src/ZM/Connection/TerminalConnection.php
Normal file
13
src/ZM/Connection/TerminalConnection.php
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace ZM\Connection;
|
||||||
|
|
||||||
|
|
||||||
|
class TerminalConnection extends WSConnection
|
||||||
|
{
|
||||||
|
|
||||||
|
public function getType() {
|
||||||
|
return "terminal";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -54,7 +54,7 @@ class Context implements ContextInterface
|
|||||||
public function getResponse() { return ZMBuf::$context[$this->cid]["response"] ?? null; }
|
public function getResponse() { return ZMBuf::$context[$this->cid]["response"] ?? null; }
|
||||||
|
|
||||||
/** @return WSConnection */
|
/** @return WSConnection */
|
||||||
public function getConnection() { return ConnectionManager::get($this->getFrame()->fd); }
|
public function getConnection() { return ConnectionManager::get($this->getFd()); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return int|null
|
* @return int|null
|
||||||
@@ -95,6 +95,8 @@ class Context implements ContextInterface
|
|||||||
|
|
||||||
public function setCache($key, $value) { ZMBuf::$context[$this->cid]["cache"][$key] = $value; }
|
public function setCache($key, $value) { ZMBuf::$context[$this->cid]["cache"][$key] = $value; }
|
||||||
|
|
||||||
|
public function getCQResponse() { return ZMBuf::$context[$this->cid]["cq_response"] ?? null; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* only can used by cq->message event function
|
* only can used by cq->message event function
|
||||||
* @param $msg
|
* @param $msg
|
||||||
@@ -106,13 +108,14 @@ class Context implements ContextInterface
|
|||||||
case "group":
|
case "group":
|
||||||
case "private":
|
case "private":
|
||||||
case "discuss":
|
case "discuss":
|
||||||
|
$this->setCache("has_reply", true);
|
||||||
return CQAPI::quick_reply(ConnectionManager::get($this->getFrame()->fd), $this->getData(), $msg, $yield);
|
return CQAPI::quick_reply(ConnectionManager::get($this->getFrame()->fd), $this->getData(), $msg, $yield);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function finalReply($msg, $yield = false) {
|
public function finalReply($msg, $yield = false) {
|
||||||
ZMBuf::$context[$this->cid]["block_continue"] = true;
|
ZMBuf::$context[$this->cid]["cache"]["block_continue"] = true;
|
||||||
if ($msg == "") return true;
|
if ($msg == "") return true;
|
||||||
return $this->reply($msg, $yield);
|
return $this->reply($msg, $yield);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,6 +72,8 @@ interface ContextInterface
|
|||||||
|
|
||||||
public function setMessageType($type);
|
public function setMessageType($type);
|
||||||
|
|
||||||
|
public function getCQResponse();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param $msg
|
* @param $msg
|
||||||
* @param bool $yield
|
* @param bool $yield
|
||||||
|
|||||||
@@ -4,10 +4,13 @@
|
|||||||
namespace ZM\DB;
|
namespace ZM\DB;
|
||||||
|
|
||||||
|
|
||||||
|
use Exception;
|
||||||
use framework\Console;
|
use framework\Console;
|
||||||
use framework\ZMBuf;
|
use framework\ZMBuf;
|
||||||
|
use PDOException;
|
||||||
|
use PDOStatement;
|
||||||
use Swoole\Coroutine;
|
use Swoole\Coroutine;
|
||||||
use Swoole\Coroutine\MySQL\Statement;
|
use Swoole\Database\PDOStatementProxy;
|
||||||
use ZM\Exception\DbException;
|
use ZM\Exception\DbException;
|
||||||
|
|
||||||
class DB
|
class DB
|
||||||
@@ -16,8 +19,10 @@ class DB
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws DbException
|
* @throws DbException
|
||||||
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public static function initTableList() {
|
public static function initTableList() {
|
||||||
|
if (!extension_loaded("mysqlnd")) throw new Exception("Can not find mysqlnd PHP extension.");
|
||||||
$result = self::rawQuery("select TABLE_NAME from INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA='" . ZMBuf::globals("sql_config")["sql_database"] . "';", []);
|
$result = self::rawQuery("select TABLE_NAME from INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA='" . ZMBuf::globals("sql_config")["sql_database"] . "';", []);
|
||||||
foreach ($result as $v) {
|
foreach ($result as $v) {
|
||||||
self::$table_list[] = $v['TABLE_NAME'];
|
self::$table_list[] = $v['TABLE_NAME'];
|
||||||
@@ -63,9 +68,10 @@ class DB
|
|||||||
try {
|
try {
|
||||||
$conn = ZMBuf::$sql_pool->get();
|
$conn = ZMBuf::$sql_pool->get();
|
||||||
if ($conn === false) {
|
if ($conn === false) {
|
||||||
|
ZMBuf::$sql_pool->put(null);
|
||||||
throw new DbException("无法连接SQL!" . $line);
|
throw new DbException("无法连接SQL!" . $line);
|
||||||
}
|
}
|
||||||
$result = $conn->query($line) === false ? false : ($conn->errno != 0 ? false : true);
|
$result = $conn->query($line) === false ? false : true;
|
||||||
ZMBuf::$sql_pool->put($conn);
|
ZMBuf::$sql_pool->put($conn);
|
||||||
return $result;
|
return $result;
|
||||||
} catch (DBException $e) {
|
} catch (DBException $e) {
|
||||||
@@ -84,10 +90,11 @@ class DB
|
|||||||
/**
|
/**
|
||||||
* @param string $line
|
* @param string $line
|
||||||
* @param array $params
|
* @param array $params
|
||||||
|
* @param int $fetch_mode
|
||||||
* @return mixed
|
* @return mixed
|
||||||
* @throws DbException
|
* @throws DbException
|
||||||
*/
|
*/
|
||||||
public static function rawQuery(string $line, $params = []) {
|
public static function rawQuery(string $line, $params = [], $fetch_mode = ZM_DEFAULT_FETCH_MODE) {
|
||||||
if (ZMBuf::get("sql_log") === true) {
|
if (ZMBuf::get("sql_log") === true) {
|
||||||
$starttime = microtime(true);
|
$starttime = microtime(true);
|
||||||
}
|
}
|
||||||
@@ -95,26 +102,29 @@ class DB
|
|||||||
try {
|
try {
|
||||||
$conn = ZMBuf::$sql_pool->get();
|
$conn = ZMBuf::$sql_pool->get();
|
||||||
if ($conn === false) {
|
if ($conn === false) {
|
||||||
|
ZMBuf::$sql_pool->put(null);
|
||||||
throw new DbException("无法连接SQL!" . $line);
|
throw new DbException("无法连接SQL!" . $line);
|
||||||
}
|
}
|
||||||
$ps = $conn->prepare($line);
|
$ps = $conn->prepare($line);
|
||||||
if ($ps === false) {
|
if ($ps === false) {
|
||||||
$conn->close();
|
ZMBuf::$sql_pool->put(null);
|
||||||
ZMBuf::$sql_pool->connect_cnt -= 1;
|
|
||||||
throw new DbException("SQL语句查询错误," . $line . ",错误信息:" . $conn->error);
|
throw new DbException("SQL语句查询错误," . $line . ",错误信息:" . $conn->error);
|
||||||
} else {
|
} else {
|
||||||
if (!($ps instanceof Statement)) {
|
if (!($ps instanceof PDOStatement) && !($ps instanceof PDOStatementProxy)) {
|
||||||
throw new DbException("语句查询错误!" . $line);
|
var_dump($ps);
|
||||||
|
ZMBuf::$sql_pool->put(null);
|
||||||
|
throw new DbException("语句查询错误!返回的不是 PDOStatement" . $line);
|
||||||
}
|
}
|
||||||
if ($params == []) $result = $ps->execute();
|
if ($params == []) $result = $ps->execute();
|
||||||
elseif (!is_array($params)) {
|
elseif (!is_array($params)) {
|
||||||
$result = $ps->execute([$params]);
|
$result = $ps->execute([$params]);
|
||||||
} else $result = $ps->execute($params);
|
} else $result = $ps->execute($params);
|
||||||
ZMBuf::$sql_pool->put($conn);
|
if ($result !== true) {
|
||||||
if ($ps->errno != 0) {
|
ZMBuf::$sql_pool->put(null);
|
||||||
throw new DBException("语句[$line]错误!" . $ps->error);
|
throw new DBException("语句[$line]错误!" . $ps->errorInfo()[2]);
|
||||||
//echo json_encode(debug_backtrace(), 128 | 256);
|
//echo json_encode(debug_backtrace(), 128 | 256);
|
||||||
}
|
}
|
||||||
|
ZMBuf::$sql_pool->put($conn);
|
||||||
if (ZMBuf::get("sql_log") === true) {
|
if (ZMBuf::get("sql_log") === true) {
|
||||||
$log =
|
$log =
|
||||||
"[" . date("Y-m-d H:i:s") .
|
"[" . date("Y-m-d H:i:s") .
|
||||||
@@ -122,9 +132,9 @@ class DB
|
|||||||
"] " . $line . " " . json_encode($params, JSON_UNESCAPED_UNICODE) . "\n";
|
"] " . $line . " " . json_encode($params, JSON_UNESCAPED_UNICODE) . "\n";
|
||||||
Coroutine::writeFile(CRASH_DIR . "sql.log", $log, FILE_APPEND);
|
Coroutine::writeFile(CRASH_DIR . "sql.log", $log, FILE_APPEND);
|
||||||
}
|
}
|
||||||
return $result;
|
return $ps->fetchAll($fetch_mode);
|
||||||
}
|
}
|
||||||
} catch (DBException $e) {
|
} catch (DbException $e) {
|
||||||
if (ZMBuf::get("sql_log") === true) {
|
if (ZMBuf::get("sql_log") === true) {
|
||||||
$log =
|
$log =
|
||||||
"[" . date("Y-m-d H:i:s") .
|
"[" . date("Y-m-d H:i:s") .
|
||||||
@@ -132,8 +142,28 @@ class DB
|
|||||||
"] " . $line . " " . json_encode($params, JSON_UNESCAPED_UNICODE) . " (Error:" . $e->getMessage() . ")\n";
|
"] " . $line . " " . json_encode($params, JSON_UNESCAPED_UNICODE) . " (Error:" . $e->getMessage() . ")\n";
|
||||||
Coroutine::writeFile(CRASH_DIR . "sql.log", $log, FILE_APPEND);
|
Coroutine::writeFile(CRASH_DIR . "sql.log", $log, FILE_APPEND);
|
||||||
}
|
}
|
||||||
|
if(mb_strpos($e->getMessage(), "has gone away") !== false) {
|
||||||
|
zm_sleep(0.2);
|
||||||
|
Console::warning("Gone away of MySQL! retrying!");
|
||||||
|
return self::rawQuery($line, $params);
|
||||||
|
}
|
||||||
Console::warning($e->getMessage());
|
Console::warning($e->getMessage());
|
||||||
throw $e;
|
throw $e;
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
if (ZMBuf::get("sql_log") === true) {
|
||||||
|
$log =
|
||||||
|
"[" . date("Y-m-d H:i:s") .
|
||||||
|
" " . round(microtime(true) - $starttime, 4) .
|
||||||
|
"] " . $line . " " . json_encode($params, JSON_UNESCAPED_UNICODE) . " (Error:" . $e->getMessage() . ")\n";
|
||||||
|
Coroutine::writeFile(CRASH_DIR . "sql.log", $log, FILE_APPEND);
|
||||||
|
}
|
||||||
|
if(mb_strpos($e->getMessage(), "has gone away") !== false) {
|
||||||
|
zm_sleep(0.2);
|
||||||
|
Console::warning("Gone away of MySQL! retrying!");
|
||||||
|
return self::rawQuery($line, $params);
|
||||||
|
}
|
||||||
|
Console::warning($e->getMessage());
|
||||||
|
throw new DbException($e->getMessage(), $e->getCode(), $e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,18 +31,29 @@ class SelectBody
|
|||||||
public function get() { return $this->fetchAll(); }
|
public function get() { return $this->fetchAll(); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @throws DbException
|
||||||
|
*/
|
||||||
|
public function count() {
|
||||||
|
$this->select_thing = ["count(*)"];
|
||||||
|
$str = $this->queryPrepare();
|
||||||
|
$this->result = DB::rawQuery($str[0], $str[1]);
|
||||||
|
return intval($this->result[0]["count(*)"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $fetch_mode
|
||||||
* @return null
|
* @return null
|
||||||
* @throws DbException
|
* @throws DbException
|
||||||
*/
|
*/
|
||||||
public function fetchAll() {
|
public function fetchAll($fetch_mode = ZM_DEFAULT_FETCH_MODE) {
|
||||||
if ($this->table->isCacheEnabled()) {
|
if ($this->table->isCacheEnabled()) {
|
||||||
$rr = md5(implode(",", $this->select_thing) . serialize($this->where_thing));
|
$rr = md5(implode(",", $this->select_thing) . serialize($this->where_thing));
|
||||||
if (array_key_exists($rr, $this->table->cache)) {
|
if (array_key_exists($rr, $this->table->cache)) {
|
||||||
Console::info('SQL query cached: ' . $rr, date("[H:i:s ") . 'DB] ');
|
Console::debug('SQL query cached: ' . $rr);
|
||||||
return $this->table->cache[$rr]->getResult();
|
return $this->table->cache[$rr]->getResult();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$this->execute();
|
$this->execute($fetch_mode);
|
||||||
if ($this->table->isCacheEnabled() && !in_array($rr, $this->table->cache)) {
|
if ($this->table->isCacheEnabled() && !in_array($rr, $this->table->cache)) {
|
||||||
$this->table->cache[$rr] = $this;
|
$this->table->cache[$rr] = $this;
|
||||||
}
|
}
|
||||||
@@ -71,11 +82,12 @@ class SelectBody
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param int $fetch_mode
|
||||||
* @throws DbException
|
* @throws DbException
|
||||||
*/
|
*/
|
||||||
public function execute() {
|
public function execute($fetch_mode = ZM_DEFAULT_FETCH_MODE) {
|
||||||
$str = $this->queryPrepare();
|
$str = $this->queryPrepare();
|
||||||
$this->result = DB::rawQuery($str[0], $str[1]);
|
$this->result = DB::rawQuery($str[0], $str[1], $fetch_mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getResult() { return $this->result; }
|
public function getResult() { return $this->result; }
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ trait WhereBody
|
|||||||
protected $where_thing = [];
|
protected $where_thing = [];
|
||||||
|
|
||||||
public function where($column, $operation_or_value, $value = null) {
|
public function where($column, $operation_or_value, $value = null) {
|
||||||
if (!in_array($operation_or_value, ['=', '!='])) $this->where_thing['='][$column] = $operation_or_value;
|
if ($value !== null) $this->where_thing[$operation_or_value][$column] = $value;
|
||||||
elseif ($value !== null) $this->where_thing[$operation_or_value][$column] = $value;
|
elseif (!in_array($operation_or_value, ['=', '!=', '>', '<', '>=', '<=', 'IN', 'in'])) $this->where_thing['='][$column] = $operation_or_value;
|
||||||
else $this->where_thing['='][$column] = $operation_or_value;
|
else $this->where_thing['='][$column] = $operation_or_value;
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
@@ -28,6 +28,7 @@ trait WhereBody
|
|||||||
$param []=$vs;
|
$param []=$vs;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if ($msg == '') $msg = 1;
|
||||||
return [$msg, $param];
|
return [$msg, $param];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,18 +38,19 @@ class MessageEvent
|
|||||||
* @throws AnnotationException
|
* @throws AnnotationException
|
||||||
*/
|
*/
|
||||||
public function onBefore() {
|
public function onBefore() {
|
||||||
foreach (ZMBuf::$events[CQBefore::class]["message"] ?? [] as $v) {
|
$obj_list = ZMBuf::$events[CQBefore::class]["message"] ?? [];
|
||||||
$c = $v->class;
|
foreach ($obj_list as $v) {
|
||||||
|
if ($v->level < 200) break;
|
||||||
EventHandler::callWithMiddleware(
|
EventHandler::callWithMiddleware(
|
||||||
$c,
|
$v->class,
|
||||||
$v->method,
|
$v->method,
|
||||||
["data" => context()->getData(), "connection" => $this->connection],
|
["data" => context()->getData(), "connection" => $this->connection],
|
||||||
[],
|
[],
|
||||||
function ($r) {
|
function ($r) {
|
||||||
if(!$r) context()->setCache("block_continue", true);
|
if (!$r) context()->setCache("block_continue", true);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
if(context()->getCache("block_continue") === true) return false;
|
if (context()->getCache("block_continue") === true) return false;
|
||||||
}
|
}
|
||||||
foreach (ZMBuf::get("wait_api", []) as $k => $v) {
|
foreach (ZMBuf::get("wait_api", []) as $k => $v) {
|
||||||
if (context()->getData()["user_id"] == $v["user_id"] &&
|
if (context()->getData()["user_id"] == $v["user_id"] &&
|
||||||
@@ -63,6 +64,21 @@ class MessageEvent
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
foreach (ZMBuf::$events[CQBefore::class]["message"] ?? [] as $v) {
|
||||||
|
if ($v->level >= 200) continue;
|
||||||
|
$c = $v->class;
|
||||||
|
if (ctx()->getCache("level") != 0) continue;
|
||||||
|
EventHandler::callWithMiddleware(
|
||||||
|
$c,
|
||||||
|
$v->method,
|
||||||
|
["data" => context()->getData(), "connection" => $this->connection],
|
||||||
|
[],
|
||||||
|
function ($r) {
|
||||||
|
if (!$r) context()->setCache("block_continue", true);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (context()->getCache("block_continue") === true) return false;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,14 +100,18 @@ class MessageEvent
|
|||||||
$obj = [];
|
$obj = [];
|
||||||
foreach (ZMBuf::$events[CQCommand::class] ?? [] as $v) {
|
foreach (ZMBuf::$events[CQCommand::class] ?? [] as $v) {
|
||||||
/** @var CQCommand $v */
|
/** @var CQCommand $v */
|
||||||
if ($v->match == "" && $v->regexMatch == "") continue;
|
if ($v->match == "" && $v->regexMatch == "" && $v->fullMatch == "") continue;
|
||||||
else {
|
elseif (($v->user_id == 0 || ($v->user_id != 0 && $v->user_id == context()->getData()["user_id"])) &&
|
||||||
|
($v->group_id == 0 || ($v->group_id != 0 && $v->group_id == (context()->getData()["group_id"] ?? 0))) &&
|
||||||
|
($v->discuss_id == 0 || ($v->discuss_id != 0 && $v->discuss_id == (context()->getData()["discuss_id"] ?? 0))) &&
|
||||||
|
($v->message_type == '' || ($v->message_type != '' && $v->message_type == context()->getData()["message_type"]))
|
||||||
|
) {
|
||||||
$c = $v->class;
|
$c = $v->class;
|
||||||
$class_construct = [
|
$class_construct = [
|
||||||
"data" => context()->getData(),
|
"data" => context()->getData(),
|
||||||
"connection" => context()->getConnection()
|
"connection" => context()->getConnection()
|
||||||
];
|
];
|
||||||
if(!isset($obj[$c])) {
|
if (!isset($obj[$c])) {
|
||||||
$obj[$c] = new $c($class_construct);
|
$obj[$c] = new $c($class_construct);
|
||||||
}
|
}
|
||||||
if ($word[0] != "" && $v->match == $word[0]) {
|
if ($word[0] != "" && $v->match == $word[0]) {
|
||||||
@@ -101,8 +121,24 @@ class MessageEvent
|
|||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
|
} elseif (in_array($word[0], $v->alias)) {
|
||||||
|
Console::debug("Calling $c -> {$v->method}");
|
||||||
|
$this->function_call = EventHandler::callWithMiddleware($obj[$c], $v->method, $class_construct, [$word], function ($r) {
|
||||||
|
if (is_string($r)) context()->reply($r);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
return;
|
||||||
} elseif ($v->regexMatch != "" && ($args = matchArgs($v->regexMatch, context()->getMessage())) !== false) {
|
} elseif ($v->regexMatch != "" && ($args = matchArgs($v->regexMatch, context()->getMessage())) !== false) {
|
||||||
$this->function_call = EventHandler::callWithMiddleware($obj[$c], $v->method, $class_construct, [$args], function ($r){
|
Console::debug("Calling $c -> {$v->method}");
|
||||||
|
$this->function_call = EventHandler::callWithMiddleware($obj[$c], $v->method, $class_construct, [$args], function ($r) {
|
||||||
|
if (is_string($r)) context()->reply($r);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
} elseif ($v->fullMatch != "" && (preg_match("/".$v->fullMatch."/u", ctx()->getMessage(), $args)) != 0) {
|
||||||
|
Console::debug("Calling $c -> {$v->method}");
|
||||||
|
array_shift($args);
|
||||||
|
$this->function_call = EventHandler::callWithMiddleware($obj[$c], $v->method, $class_construct, [$args], function ($r) {
|
||||||
if (is_string($r)) context()->reply($r);
|
if (is_string($r)) context()->reply($r);
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
@@ -120,13 +156,14 @@ class MessageEvent
|
|||||||
($v->message_type == '' || ($v->message_type != '' && $v->message_type == context()->getData()["message_type"])) &&
|
($v->message_type == '' || ($v->message_type != '' && $v->message_type == context()->getData()["message_type"])) &&
|
||||||
($v->raw_message == '' || ($v->raw_message != '' && $v->raw_message == context()->getData()["raw_message"]))) {
|
($v->raw_message == '' || ($v->raw_message != '' && $v->raw_message == context()->getData()["raw_message"]))) {
|
||||||
$c = $v->class;
|
$c = $v->class;
|
||||||
|
Console::debug("Calling CQMessage: $c -> {$v->method}");
|
||||||
if (!isset($obj[$c]))
|
if (!isset($obj[$c]))
|
||||||
$obj[$c] = new $c([
|
$obj[$c] = new $c([
|
||||||
"data" => context()->getData(),
|
"data" => context()->getData(),
|
||||||
"connection" => $this->connection
|
"connection" => $this->connection
|
||||||
], ModHandleType::CQ_MESSAGE);
|
], ModHandleType::CQ_MESSAGE);
|
||||||
EventHandler::callWithMiddleware($obj[$c], $v->method, [], [context()->getData()["message"]], function($r) {
|
EventHandler::callWithMiddleware($obj[$c], $v->method, [], [context()->getData()["message"]], function ($r) {
|
||||||
if(is_string($r)) context()->reply($r);
|
if (is_string($r)) context()->reply($r);
|
||||||
});
|
});
|
||||||
if (context()->getCache("block_continue") === true) return;
|
if (context()->getCache("block_continue") === true) return;
|
||||||
}
|
}
|
||||||
@@ -150,10 +187,10 @@ class MessageEvent
|
|||||||
["data" => context()->getData(), "connection" => $this->connection],
|
["data" => context()->getData(), "connection" => $this->connection],
|
||||||
[],
|
[],
|
||||||
function ($r) {
|
function ($r) {
|
||||||
if(!$r) context()->setCache("block_continue", true);
|
if (!$r) context()->setCache("block_continue", true);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
if(context()->getCache("block_continue") === true) return false;
|
if (context()->getCache("block_continue") === true) return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ use Exception;
|
|||||||
use Framework\Console;
|
use Framework\Console;
|
||||||
use Framework\ZMBuf;
|
use Framework\ZMBuf;
|
||||||
use ZM\Event\Swoole\{MessageEvent, RequestEvent, WorkerStartEvent, WSCloseEvent, WSOpenEvent};
|
use ZM\Event\Swoole\{MessageEvent, RequestEvent, WorkerStartEvent, WSCloseEvent, WSOpenEvent};
|
||||||
|
use Swoole\Http\Request;
|
||||||
use Swoole\Server;
|
use Swoole\Server;
|
||||||
use Swoole\WebSocket\Frame;
|
use Swoole\WebSocket\Frame;
|
||||||
use ZM\Annotation\CQ\CQAPIResponse;
|
use ZM\Annotation\CQ\CQAPIResponse;
|
||||||
@@ -38,23 +39,30 @@ class EventHandler
|
|||||||
switch ($event_name) {
|
switch ($event_name) {
|
||||||
case "workerstart":
|
case "workerstart":
|
||||||
try {
|
try {
|
||||||
register_shutdown_function(function () {
|
register_shutdown_function(function () use ($param0) {
|
||||||
$error = error_get_last();
|
$error = error_get_last();
|
||||||
if ($error["type"] != 0) {
|
if ($error["type"] != 0) {
|
||||||
Console::error("Internal fatal error: " . $error["message"] . " at " . $error["file"] . "({$error["line"]})");
|
Console::error("Internal fatal error: " . $error["message"] . " at " . $error["file"] . "({$error["line"]})");
|
||||||
}
|
}
|
||||||
DataProvider::saveBuffer();
|
DataProvider::saveBuffer();
|
||||||
ZMBuf::$server->shutdown();
|
/** @var Server $param0 */
|
||||||
|
if (ZMBuf::$server === null) $param0->shutdown();
|
||||||
|
else ZMBuf::$server->shutdown();
|
||||||
});
|
});
|
||||||
(new WorkerStartEvent($param0, $param1))->onActivate()->onAfter();
|
ZMBuf::$server = $param0;
|
||||||
|
$r = (new WorkerStartEvent($param0, $param1))->onActivate();
|
||||||
Console::log("\n=== Worker #" . $param0->worker_id . " 已启动 ===\n", "gold");
|
Console::log("\n=== Worker #" . $param0->worker_id . " 已启动 ===\n", "gold");
|
||||||
|
$r->onAfter();
|
||||||
|
self::startTick();
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
Console::error("Worker加载出错!停止服务!");
|
Console::error("Worker加载出错!停止服务!");
|
||||||
Console::error($e->getMessage() . "\n" . $e->getTraceAsString());
|
Console::error($e->getMessage() . "\n" . $e->getTraceAsString());
|
||||||
ZMUtil::stop();
|
ZMUtil::stop();
|
||||||
return;
|
return;
|
||||||
} catch (Error $e) {
|
} catch (Error $e) {
|
||||||
var_export($e);
|
Console::error("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');
|
||||||
ZMUtil::stop();
|
ZMUtil::stop();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -63,7 +71,13 @@ class EventHandler
|
|||||||
/** @var Server $param0 */
|
/** @var Server $param0 */
|
||||||
$conn = ConnectionManager::get($param1->fd);
|
$conn = ConnectionManager::get($param1->fd);
|
||||||
set_coroutine_params(["server" => $param0, "frame" => $param1, "connection" => $conn]);
|
set_coroutine_params(["server" => $param0, "frame" => $param1, "connection" => $conn]);
|
||||||
(new MessageEvent($param0, $param1))->onActivate()->onAfter();
|
try {
|
||||||
|
(new MessageEvent($param0, $param1))->onActivate()->onAfter();
|
||||||
|
} catch (Error $e) {
|
||||||
|
$error_msg = $e->getMessage() . " at " . $e->getFile() . "(" . $e->getLine() . ")";
|
||||||
|
Console::error("Fatal error when calling $event_name: " . $error_msg);
|
||||||
|
Console::stackTrace();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case "request":
|
case "request":
|
||||||
try {
|
try {
|
||||||
@@ -76,17 +90,42 @@ class EventHandler
|
|||||||
" [" . $param1->getStatusCode() . "] " . $param0->server["request_uri"]
|
" [" . $param1->getStatusCode() . "] " . $param0->server["request_uri"]
|
||||||
);
|
);
|
||||||
if (!$param1->isEnd()) $param1->end("Internal server error: " . $e->getMessage());
|
if (!$param1->isEnd()) $param1->end("Internal server error: " . $e->getMessage());
|
||||||
Console::error("Internal server error (500), caused by uncaught exception.");
|
Console::error("Internal server exception (500), caused by " . get_class($e));
|
||||||
|
Console::log($e->getTraceAsString(), "gray");
|
||||||
|
} catch (Error $e) {
|
||||||
|
/** @var Response $param1 */
|
||||||
|
$param1->status(500);
|
||||||
|
Console::info($param0->server["remote_addr"] . ":" . $param0->server["remote_port"] .
|
||||||
|
" [" . $param1->getStatusCode() . "] " . $param0->server["request_uri"]
|
||||||
|
);
|
||||||
|
$doc = "Internal server error<br>";
|
||||||
|
$error_msg = $e->getMessage() . " at " . $e->getFile() . "(" . $e->getLine() . ")";
|
||||||
|
if (ZMBuf::$atomics["info_level"]->get() >= 4) $doc .= $error_msg;
|
||||||
|
if (!$param1->isEnd()) $param1->end($doc);
|
||||||
|
Console::error("Internal server error (500): " . $error_msg);
|
||||||
Console::log($e->getTraceAsString(), "gray");
|
Console::log($e->getTraceAsString(), "gray");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "open":
|
case "open":
|
||||||
set_coroutine_params(["server" => $param0, "request" => $param1]);
|
/** @var Request $param1 */
|
||||||
(new WSOpenEvent($param0, $param1))->onActivate()->onAfter();
|
set_coroutine_params(["server" => $param0, "request" => $param1, "fd" => $param1->fd]);
|
||||||
|
try {
|
||||||
|
(new WSOpenEvent($param0, $param1))->onActivate()->onAfter();
|
||||||
|
} catch (Error $e) {
|
||||||
|
$error_msg = $e->getMessage() . " at " . $e->getFile() . "(" . $e->getLine() . ")";
|
||||||
|
Console::error("Fatal error when calling $event_name: " . $error_msg);
|
||||||
|
Console::stackTrace();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case "close":
|
case "close":
|
||||||
set_coroutine_params(["server" => $param0, "fd" => $param1]);
|
set_coroutine_params(["server" => $param0, "fd" => $param1]);
|
||||||
(new WSCloseEvent($param0, $param1))->onActivate()->onAfter();
|
try {
|
||||||
|
(new WSCloseEvent($param0, $param1))->onActivate()->onAfter();
|
||||||
|
} catch (Error $e) {
|
||||||
|
$error_msg = $e->getMessage() . " at " . $e->getFile() . "(" . $e->getLine() . ")";
|
||||||
|
Console::error("Fatal error when calling $event_name: " . $error_msg);
|
||||||
|
Console::stackTrace();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
//Console::info(Console::setColor("Event: " . $event_name . " 运行了 " . round(microtime(true) - $starttime, 5) . " 秒", "gold"));
|
//Console::info(Console::setColor("Event: " . $event_name . " 运行了 " . round(microtime(true) - $starttime, 5) . " 秒", "gold"));
|
||||||
@@ -100,6 +139,7 @@ class EventHandler
|
|||||||
* @throws AnnotationException
|
* @throws AnnotationException
|
||||||
*/
|
*/
|
||||||
public static function callCQEvent($event_data, $conn_or_response, int $level = 0) {
|
public static function callCQEvent($event_data, $conn_or_response, int $level = 0) {
|
||||||
|
ctx()->setCache("level", $level);
|
||||||
if ($level >= 5) {
|
if ($level >= 5) {
|
||||||
Console::warning("Recursive call reached " . $level . " times");
|
Console::warning("Recursive call reached " . $level . " times");
|
||||||
Console::stackTrace();
|
Console::stackTrace();
|
||||||
@@ -132,8 +172,12 @@ class EventHandler
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $req
|
||||||
|
* @throws AnnotationException
|
||||||
|
*/
|
||||||
public static function callCQResponse($req) {
|
public static function callCQResponse($req) {
|
||||||
//Console::info("收到来自API连接的回复:".json_encode($req, 128|256));
|
Console::debug("收到来自API连接的回复:" . json_encode($req, 128 | 256));
|
||||||
$status = $req["status"];
|
$status = $req["status"];
|
||||||
$retcode = $req["retcode"];
|
$retcode = $req["retcode"];
|
||||||
$data = $req["data"];
|
$data = $req["data"];
|
||||||
@@ -144,13 +188,26 @@ class EventHandler
|
|||||||
"status" => $status,
|
"status" => $status,
|
||||||
"retcode" => $retcode,
|
"retcode" => $retcode,
|
||||||
"data" => $data,
|
"data" => $data,
|
||||||
"self_id" => $self_id
|
"self_id" => $self_id,
|
||||||
|
"echo" => $req["echo"]
|
||||||
];
|
];
|
||||||
|
set_coroutine_params(["cq_response" => $response]);
|
||||||
if (isset(ZMBuf::$events[CQAPIResponse::class][$req["retcode"]])) {
|
if (isset(ZMBuf::$events[CQAPIResponse::class][$req["retcode"]])) {
|
||||||
list($c, $method) = ZMBuf::$events[CQAPIResponse::class][$req["retcode"]];
|
list($c, $method) = ZMBuf::$events[CQAPIResponse::class][$req["retcode"]];
|
||||||
$class = new $c(["data" => $origin["data"]]);
|
$class = new $c(["data" => $origin["data"]]);
|
||||||
call_user_func_array([$class, $method], [$origin["data"], $req]);
|
call_user_func_array([$class, $method], [$origin["data"], $req]);
|
||||||
}
|
}
|
||||||
|
$origin_ctx = ctx()->copy();
|
||||||
|
ctx()->setCache("action", $origin["data"]["action"] ?? "unknown");
|
||||||
|
ctx()->setData($origin["data"]);
|
||||||
|
foreach (ZMBuf::$events[CQAPISend::class] ?? [] as $k => $v) {
|
||||||
|
if (($v->action == "" || $v->action == ctx()->getCache("action")) && $v->with_result) {
|
||||||
|
$c = $v->class;
|
||||||
|
self::callWithMiddleware($c, $v->method, context()->copy(), [ctx()->getCache("action"), $origin["data"]["params"] ?? [], ctx()->getRobotId()]);
|
||||||
|
if (context()->getCache("block_continue") === true) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set_coroutine_params($origin_ctx);
|
||||||
if (($origin["func"] ?? null) !== null) {
|
if (($origin["func"] ?? null) !== null) {
|
||||||
call_user_func($origin["func"], $response, $origin["data"]);
|
call_user_func($origin["func"], $response, $origin["data"]);
|
||||||
} elseif (($origin["coroutine"] ?? false) !== false) {
|
} elseif (($origin["coroutine"] ?? false) !== false) {
|
||||||
@@ -169,13 +226,14 @@ class EventHandler
|
|||||||
Console::warning("API 激活事件异常!");
|
Console::warning("API 激活事件异常!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$content = ctx()->copy();
|
if (ctx() === null) $content = [];
|
||||||
|
else $content = ctx()->copy();
|
||||||
go(function () use ($action, $reply, $connection, $content) {
|
go(function () use ($action, $reply, $connection, $content) {
|
||||||
set_coroutine_params($content);
|
set_coroutine_params($content);
|
||||||
context()->setCache("action", $action);
|
context()->setCache("action", $action);
|
||||||
context()->setCache("reply", $reply);
|
context()->setCache("reply", $reply);
|
||||||
foreach (ZMBuf::$events[CQAPISend::class] ?? [] as $k => $v) {
|
foreach (ZMBuf::$events[CQAPISend::class] ?? [] as $k => $v) {
|
||||||
if ($v->action == "" || $v->action == $action) {
|
if (($v->action == "" || $v->action == $action) && !$v->with_result) {
|
||||||
$c = $v->class;
|
$c = $v->class;
|
||||||
self::callWithMiddleware($c, $v->method, context()->copy(), [$reply["action"], $reply["params"] ?? [], $connection->getQQ()]);
|
self::callWithMiddleware($c, $v->method, context()->copy(), [$reply["action"], $reply["params"] ?? [], $connection->getQQ()]);
|
||||||
if (context()->getCache("block_continue") === true) break;
|
if (context()->getCache("block_continue") === true) break;
|
||||||
@@ -216,6 +274,7 @@ class EventHandler
|
|||||||
if ($before_result) {
|
if ($before_result) {
|
||||||
try {
|
try {
|
||||||
if (is_object($c)) $class = $c;
|
if (is_object($c)) $class = $c;
|
||||||
|
elseif ($class_construct == []) $class = ZMUtil::getModInstance($c);
|
||||||
else $class = new $c($class_construct);
|
else $class = new $c($class_construct);
|
||||||
$result = call_user_func_array([$class, $method], $func_args);
|
$result = call_user_func_array([$class, $method], $func_args);
|
||||||
if (is_callable($after_call))
|
if (is_callable($after_call))
|
||||||
@@ -226,7 +285,7 @@ class EventHandler
|
|||||||
if (!isset($middleware_obj["exceptions"])) continue;
|
if (!isset($middleware_obj["exceptions"])) continue;
|
||||||
foreach ($middleware_obj["exceptions"] as $name => $method) {
|
foreach ($middleware_obj["exceptions"] as $name => $method) {
|
||||||
if ($e instanceof $name) {
|
if ($e instanceof $name) {
|
||||||
call_user_func_array([$r[$i], $method], [$e]);
|
$r[$i]->$method($e);
|
||||||
context()->setCache("block_continue", true);
|
context()->setCache("block_continue", true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -242,6 +301,7 @@ class EventHandler
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (is_object($c)) $class = $c;
|
if (is_object($c)) $class = $c;
|
||||||
|
elseif ($class_construct == []) $class = ZMUtil::getModInstance($c);
|
||||||
else $class = new $c($class_construct);
|
else $class = new $c($class_construct);
|
||||||
$result = call_user_func_array([$class, $method], $func_args);
|
$result = call_user_func_array([$class, $method], $func_args);
|
||||||
if (is_callable($after_call))
|
if (is_callable($after_call))
|
||||||
@@ -249,4 +309,12 @@ class EventHandler
|
|||||||
}
|
}
|
||||||
return $return_value;
|
return $return_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static function startTick() {
|
||||||
|
Console::debug("Starting " . count(ZMBuf::get("paused_tick", [])) . " custom tick function");
|
||||||
|
foreach (ZMBuf::get("paused_tick", []) as $cid) {
|
||||||
|
Co::resume($cid);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,10 +45,13 @@ class MessageEvent implements SwooleEvent
|
|||||||
$data = json_decode(context()->getFrame()->data, true);
|
$data = json_decode(context()->getFrame()->data, true);
|
||||||
if (isset($data["post_type"])) {
|
if (isset($data["post_type"])) {
|
||||||
set_coroutine_params(["data" => $data, "connection" => $conn]);
|
set_coroutine_params(["data" => $data, "connection" => $conn]);
|
||||||
|
ctx()->setCache("level", 0);
|
||||||
Console::debug("Calling CQ Event from fd=" . $conn->fd);
|
Console::debug("Calling CQ Event from fd=" . $conn->fd);
|
||||||
EventHandler::callCQEvent($data, ConnectionManager::get(context()->getFrame()->fd), 0);
|
EventHandler::callCQEvent($data, ConnectionManager::get(context()->getFrame()->fd), 0);
|
||||||
} else
|
} else{
|
||||||
|
set_coroutine_params(["connection" => $conn]);
|
||||||
EventHandler::callCQResponse($data);
|
EventHandler::callCQResponse($data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
foreach (ZMBuf::$events[SwooleEventAt::class] ?? [] as $v) {
|
foreach (ZMBuf::$events[SwooleEventAt::class] ?? [] as $v) {
|
||||||
if (strtolower($v->type) == "message" && $this->parseSwooleRule($v)) {
|
if (strtolower($v->type) == "message" && $this->parseSwooleRule($v)) {
|
||||||
@@ -63,7 +66,8 @@ class MessageEvent implements SwooleEvent
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
Console::warning("Websocket message event exception: " . $e->getMessage());
|
Console::warning("Websocket message event exception: " . (($cs = $e->getMessage()) == "" ? get_class($e) : $cs));
|
||||||
|
Console::warning("In ". $e->getFile() . " at line ".$e->getLine());
|
||||||
}
|
}
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,8 @@ class RequestEvent implements SwooleEvent
|
|||||||
*/
|
*/
|
||||||
private $response;
|
private $response;
|
||||||
|
|
||||||
public function __construct(Request $request, Response $response) {
|
public function __construct(Request $request, Response $response)
|
||||||
|
{
|
||||||
$this->request = $request;
|
$this->request = $request;
|
||||||
$this->response = $response;
|
$this->response = $response;
|
||||||
}
|
}
|
||||||
@@ -36,19 +37,24 @@ class RequestEvent implements SwooleEvent
|
|||||||
* @return $this|SwooleEvent
|
* @return $this|SwooleEvent
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public function onActivate() {
|
public function onActivate()
|
||||||
|
{
|
||||||
ZMUtil::checkWait();
|
ZMUtil::checkWait();
|
||||||
foreach (ZMBuf::globals("http_header") as $k => $v) {
|
foreach (ZMBuf::globals("http_header") as $k => $v) {
|
||||||
$this->response->setHeader($k, $v);
|
$this->response->setHeader($k, $v);
|
||||||
}
|
}
|
||||||
$uri = $this->request->server["request_uri"];
|
$uri = $this->request->server["request_uri"];
|
||||||
|
Console::verbose($this->request->server["remote_addr"] . " request " . $uri);
|
||||||
$uri = explode("/", $uri);
|
$uri = explode("/", $uri);
|
||||||
$uri = array_diff($uri, ["..", "", "."]);
|
$uri = array_diff($uri, ["..", "", "."]);
|
||||||
$node = ZMBuf::$req_mapping;
|
$node = ZMBuf::$req_mapping;
|
||||||
$params = [];
|
$params = [];
|
||||||
while (true) {
|
while (true) {
|
||||||
$r = array_shift($uri);
|
$r = array_shift($uri);
|
||||||
if ($r === null) break;
|
if ($r === null) {
|
||||||
|
if ($node == ZMBuf::$req_mapping) goto statics;
|
||||||
|
else break;
|
||||||
|
}
|
||||||
if (($cnt = count($node["son"] ?? [])) == 1) {
|
if (($cnt = count($node["son"] ?? [])) == 1) {
|
||||||
if (isset($node["param_route"])) {
|
if (isset($node["param_route"])) {
|
||||||
foreach ($node["son"] as $k => $v) {
|
foreach ($node["son"] as $k => $v) {
|
||||||
@@ -79,13 +85,18 @@ class RequestEvent implements SwooleEvent
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
statics:
|
||||||
if (ZMBuf::globals("static_file_server")["status"]) {
|
if (ZMBuf::globals("static_file_server")["status"]) {
|
||||||
$base_dir = ZMBuf::globals("static_file_server")["document_root"];
|
$base_dir = ZMBuf::globals("static_file_server")["document_root"];
|
||||||
$base_index = ZMBuf::globals("static_file_server")["document_index"];
|
$base_index = ZMBuf::globals("static_file_server")["document_index"];
|
||||||
$uri = $this->request->server["request_uri"];
|
$uri = $this->request->server["request_uri"];
|
||||||
$path = realpath($base_dir . urldecode($uri));
|
$path = realpath($base_dir . urldecode($uri));
|
||||||
if ($path !== false) {
|
if ($path !== false) {
|
||||||
|
if (is_dir($path) && mb_substr($uri, -1, 1) != "/") {
|
||||||
|
$this->response->redirect($uri . "/", 301);
|
||||||
|
$this->response->end();
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
if (is_dir($path)) $path = $path . '/';
|
if (is_dir($path)) $path = $path . '/';
|
||||||
$work = realpath(DataProvider::getWorkingDir()) . '/';
|
$work = realpath(DataProvider::getWorkingDir()) . '/';
|
||||||
if (strpos($path, $work) !== 0) {
|
if (strpos($path, $work) !== 0) {
|
||||||
@@ -149,7 +160,8 @@ class RequestEvent implements SwooleEvent
|
|||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
*/
|
*/
|
||||||
public function onAfter() {
|
public function onAfter()
|
||||||
|
{
|
||||||
foreach (ZMBuf::$events[SwooleEventAfter::class] ?? [] as $v) {
|
foreach (ZMBuf::$events[SwooleEventAfter::class] ?? [] as $v) {
|
||||||
if (strtolower($v->type) == "request" && $this->parseSwooleRule($v)) {
|
if (strtolower($v->type) == "request" && $this->parseSwooleRule($v)) {
|
||||||
$c = $v->class;
|
$c = $v->class;
|
||||||
@@ -161,12 +173,14 @@ class RequestEvent implements SwooleEvent
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function responseStatus(int $int) {
|
private function responseStatus(int $int)
|
||||||
|
{
|
||||||
$this->response->status($int);
|
$this->response->status($int);
|
||||||
$this->response->end();
|
$this->response->end();
|
||||||
}
|
}
|
||||||
|
|
||||||
private function parseSwooleRule($v) {
|
private function parseSwooleRule($v)
|
||||||
|
{
|
||||||
switch (explode(":", $v->rule)[0]) {
|
switch (explode(":", $v->rule)[0]) {
|
||||||
case "containsGet":
|
case "containsGet":
|
||||||
case "containsPost":
|
case "containsPost":
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
namespace ZM\Event\Swoole;
|
namespace ZM\Event\Swoole;
|
||||||
|
|
||||||
|
|
||||||
|
use Closure;
|
||||||
use Doctrine\Common\Annotations\AnnotationException;
|
use Doctrine\Common\Annotations\AnnotationException;
|
||||||
use Framework\ZMBuf;
|
use Framework\ZMBuf;
|
||||||
use Swoole\Server;
|
use Swoole\Server;
|
||||||
@@ -11,8 +12,6 @@ use ZM\Annotation\Swoole\SwooleEventAfter;
|
|||||||
use ZM\Annotation\Swoole\SwooleEventAt;
|
use ZM\Annotation\Swoole\SwooleEventAt;
|
||||||
use ZM\Connection\ConnectionManager;
|
use ZM\Connection\ConnectionManager;
|
||||||
use ZM\Event\EventHandler;
|
use ZM\Event\EventHandler;
|
||||||
use ZM\ModBase;
|
|
||||||
use ZM\ModHandleType;
|
|
||||||
use ZM\Utils\ZMUtil;
|
use ZM\Utils\ZMUtil;
|
||||||
|
|
||||||
class WSCloseEvent implements SwooleEvent
|
class WSCloseEvent implements SwooleEvent
|
||||||
@@ -32,8 +31,7 @@ class WSCloseEvent implements SwooleEvent
|
|||||||
*/
|
*/
|
||||||
public function onActivate() {
|
public function onActivate() {
|
||||||
ZMUtil::checkWait();
|
ZMUtil::checkWait();
|
||||||
ConnectionManager::close($this->fd);
|
set_coroutine_params(["server" => $this->server, "fd" => $this->fd, "connection" => ConnectionManager::get($this->fd)]);
|
||||||
set_coroutine_params(["server" => $this->server, "fd" => $this->fd]);
|
|
||||||
foreach(ZMBuf::$events[SwooleEventAt::class] ?? [] as $v) {
|
foreach(ZMBuf::$events[SwooleEventAt::class] ?? [] as $v) {
|
||||||
if(strtolower($v->type) == "close" && $this->parseSwooleRule($v)) {
|
if(strtolower($v->type) == "close" && $this->parseSwooleRule($v)) {
|
||||||
$c = $v->class;
|
$c = $v->class;
|
||||||
@@ -41,6 +39,7 @@ class WSCloseEvent implements SwooleEvent
|
|||||||
if(context()->getCache("block_continue") === true) break;
|
if(context()->getCache("block_continue") === true) break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ConnectionManager::close($this->fd);
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,6 +59,11 @@ class WSCloseEvent implements SwooleEvent
|
|||||||
}
|
}
|
||||||
|
|
||||||
private function parseSwooleRule($v) {
|
private function parseSwooleRule($v) {
|
||||||
|
switch (explode(":", $v->rule)[0]) {
|
||||||
|
case "connectType": //websocket连接类型
|
||||||
|
if ($v->callback instanceof Closure) return call_user_func($v->callback, ConnectionManager::get($this->fd));
|
||||||
|
break;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ namespace ZM\Event\Swoole;
|
|||||||
|
|
||||||
use Closure;
|
use Closure;
|
||||||
use Doctrine\Common\Annotations\AnnotationException;
|
use Doctrine\Common\Annotations\AnnotationException;
|
||||||
|
use Framework\Console;
|
||||||
use Framework\ZMBuf;
|
use Framework\ZMBuf;
|
||||||
use Swoole\Http\Request;
|
use Swoole\Http\Request;
|
||||||
use Swoole\WebSocket\Server;
|
use Swoole\WebSocket\Server;
|
||||||
@@ -51,9 +52,16 @@ class WSOpenEvent implements SwooleEvent
|
|||||||
if ($type_conn == CQConnection::class) {
|
if ($type_conn == CQConnection::class) {
|
||||||
$qq = $this->request->get["qq"] ?? $this->request->header["x-self-id"] ?? "";
|
$qq = $this->request->get["qq"] ?? $this->request->header["x-self-id"] ?? "";
|
||||||
$self_token = ZMBuf::globals("access_token") ?? "";
|
$self_token = ZMBuf::globals("access_token") ?? "";
|
||||||
$remote_token = $this->request->get["token"] ?? (isset($header["authorization"]) ? explode(" ", $this->request->header["authorization"])[1] : "");
|
if(isset($this->request->header["authorization"])) {
|
||||||
|
Console::debug($this->request->header["authorization"]);
|
||||||
|
}
|
||||||
|
$remote_token = $this->request->get["token"] ?? (isset($this->request->header["authorization"]) ? explode(" ", $this->request->header["authorization"])[1] : "");
|
||||||
if ($qq != "" && ($self_token == $remote_token)) $this->conn = new CQConnection($this->server, $this->request->fd, $qq);
|
if ($qq != "" && ($self_token == $remote_token)) $this->conn = new CQConnection($this->server, $this->request->fd, $qq);
|
||||||
else $this->conn = new UnknownConnection($this->server, $this->request->fd);
|
else {
|
||||||
|
$this->conn = new UnknownConnection($this->server, $this->request->fd);
|
||||||
|
Console::warning("connection of CQ has invalid QQ or token!");
|
||||||
|
Console::debug("Remote token: ".$remote_token);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
$this->conn = new $type_conn($this->server, $this->request->fd);
|
$this->conn = new $type_conn($this->server, $this->request->fd);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,8 +7,11 @@ namespace ZM\Event\Swoole;
|
|||||||
use Co;
|
use Co;
|
||||||
use Doctrine\Common\Annotations\AnnotationException;
|
use Doctrine\Common\Annotations\AnnotationException;
|
||||||
use Exception;
|
use Exception;
|
||||||
|
use PDO;
|
||||||
use ReflectionException;
|
use ReflectionException;
|
||||||
use Swoole\Coroutine;
|
use Swoole\Coroutine;
|
||||||
|
use Swoole\Database\PDOConfig;
|
||||||
|
use Swoole\Database\PDOPool;
|
||||||
use Swoole\Process;
|
use Swoole\Process;
|
||||||
use Swoole\Timer;
|
use Swoole\Timer;
|
||||||
use ZM\Annotation\AnnotationBase;
|
use ZM\Annotation\AnnotationBase;
|
||||||
@@ -25,7 +28,6 @@ use Swoole\Server;
|
|||||||
use ZM\Event\EventHandler;
|
use ZM\Event\EventHandler;
|
||||||
use ZM\Exception\DbException;
|
use ZM\Exception\DbException;
|
||||||
use Framework\DataProvider;
|
use Framework\DataProvider;
|
||||||
use ZM\Utils\SQLPool;
|
|
||||||
use ZM\Utils\ZMUtil;
|
use ZM\Utils\ZMUtil;
|
||||||
|
|
||||||
class WorkerStartEvent implements SwooleEvent
|
class WorkerStartEvent implements SwooleEvent
|
||||||
@@ -49,6 +51,9 @@ class WorkerStartEvent implements SwooleEvent
|
|||||||
*/
|
*/
|
||||||
public function onActivate(): WorkerStartEvent {
|
public function onActivate(): WorkerStartEvent {
|
||||||
Console::info("Worker启动中");
|
Console::info("Worker启动中");
|
||||||
|
ZMBuf::$server = $this->server;
|
||||||
|
Console::listenConsole(); //这个方法只能在这里调用,且如果worker_num不为1的话,此功能不可用
|
||||||
|
|
||||||
Process::signal(SIGINT, function () {
|
Process::signal(SIGINT, function () {
|
||||||
Console::warning("Server interrupted by keyboard.");
|
Console::warning("Server interrupted by keyboard.");
|
||||||
ZMUtil::stop(true);
|
ZMUtil::stop(true);
|
||||||
@@ -76,14 +81,36 @@ class WorkerStartEvent implements SwooleEvent
|
|||||||
}
|
}
|
||||||
if (ZMBuf::globals("sql_config")["sql_host"] != "") {
|
if (ZMBuf::globals("sql_config")["sql_host"] != "") {
|
||||||
Console::info("新建SQL连接池中");
|
Console::info("新建SQL连接池中");
|
||||||
ZMBuf::$sql_pool = new SQLPool();
|
ob_start();
|
||||||
|
phpinfo();
|
||||||
|
$str = ob_get_clean();
|
||||||
|
$str = explode("\n", $str);
|
||||||
|
foreach($str as $k => $v) {
|
||||||
|
$v = trim($v);
|
||||||
|
if($v == "") continue;
|
||||||
|
if(mb_strpos($v, "API Extensions") === false) continue;
|
||||||
|
if(mb_strpos($v, "pdo_mysql") === false) {
|
||||||
|
throw new DbException("未安装 mysqlnd php-mysql扩展。");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$sql = ZMBuf::globals("sql_config");
|
||||||
|
ZMBuf::$sql_pool = new PDOPool((new PDOConfig())
|
||||||
|
->withHost($sql["sql_host"])
|
||||||
|
->withPort($sql["sql_port"])
|
||||||
|
// ->withUnixSocket('/tmp/mysql.sock')
|
||||||
|
->withDbName($sql["sql_database"])
|
||||||
|
->withCharset('utf8mb4')
|
||||||
|
->withUsername($sql["sql_username"])
|
||||||
|
->withPassword($sql["sql_password"])
|
||||||
|
->withOptions($sql["sql_options"] ?? [PDO::ATTR_STRINGIFY_FETCHES => false])
|
||||||
|
);
|
||||||
DB::initTableList();
|
DB::initTableList();
|
||||||
}
|
}
|
||||||
ZMBuf::$server = $this->server;
|
|
||||||
ZMBuf::$atomics['reload_time']->add(1);
|
ZMBuf::$atomics['reload_time']->add(1);
|
||||||
|
|
||||||
Console::info("监听console输入");
|
Console::info("监听console输入");
|
||||||
Console::listenConsole(); //这个方法只能在这里调用,且如果worker_num不为1的话,此功能不可用
|
|
||||||
$this->setAutosaveTimer(ZMBuf::globals("auto_save_interval"));
|
$this->setAutosaveTimer(ZMBuf::globals("auto_save_interval"));
|
||||||
$this->loadAllClass(); //加载composer资源、phar外置包、注解解析注册等
|
$this->loadAllClass(); //加载composer资源、phar外置包、注解解析注册等
|
||||||
return $this;
|
return $this;
|
||||||
@@ -99,18 +126,22 @@ class WorkerStartEvent implements SwooleEvent
|
|||||||
}
|
}
|
||||||
ZMBuf::unsetCache("wait_start");
|
ZMBuf::unsetCache("wait_start");
|
||||||
set_coroutine_params(["server" => $this->server, "worker_id" => $this->worker_id]);
|
set_coroutine_params(["server" => $this->server, "worker_id" => $this->worker_id]);
|
||||||
|
|
||||||
foreach (ZMBuf::$events[OnStart::class] ?? [] as $v) {
|
foreach (ZMBuf::$events[OnStart::class] ?? [] as $v) {
|
||||||
$class_name = $v->class;
|
$class_name = $v->class;
|
||||||
|
Console::debug("正在调用启动时函数: " . $class_name . " -> " . $v->method);
|
||||||
EventHandler::callWithMiddleware($class_name, $v->method, ["server" => $this->server, "worker_id" => $this->worker_id], []);
|
EventHandler::callWithMiddleware($class_name, $v->method, ["server" => $this->server, "worker_id" => $this->worker_id], []);
|
||||||
}
|
}
|
||||||
foreach (ZMBuf::$events[SwooleEventAfter::class] ?? [] as $v) {
|
foreach (ZMBuf::$events[SwooleEventAfter::class] ?? [] as $v) {
|
||||||
/** @var AnnotationBase $v */
|
/** @var AnnotationBase $v */
|
||||||
if (strtolower($v->type) == "workerstart") {
|
if (strtolower($v->type) == "workerstart") {
|
||||||
$class_name = $v->class;
|
$class_name = $v->class;
|
||||||
|
Console::debug("正在调用启动时函数after: " . $class_name . " -> " . $v->method);
|
||||||
EventHandler::callWithMiddleware($class_name, $v->method, ["server" => $this->server, "worker_id" => $this->worker_id], []);
|
EventHandler::callWithMiddleware($class_name, $v->method, ["server" => $this->server, "worker_id" => $this->worker_id], []);
|
||||||
if (context()->getCache("block_continue") === true) break;
|
if (context()->getCache("block_continue") === true) break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Console::debug("调用完毕!");
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,8 +149,8 @@ class WorkerStartEvent implements SwooleEvent
|
|||||||
foreach ($this->server->connections as $v) {
|
foreach ($this->server->connections as $v) {
|
||||||
$this->server->close($v);
|
$this->server->close($v);
|
||||||
}
|
}
|
||||||
if (ZMBuf::$sql_pool instanceof SqlPool) {
|
if (ZMBuf::$sql_pool !== null) {
|
||||||
ZMBuf::$sql_pool->destruct();
|
ZMBuf::$sql_pool->close();
|
||||||
ZMBuf::$sql_pool = null;
|
ZMBuf::$sql_pool = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -133,22 +164,20 @@ class WorkerStartEvent implements SwooleEvent
|
|||||||
//加载phar包
|
//加载phar包
|
||||||
Console::info("加载外部phar包中");
|
Console::info("加载外部phar包中");
|
||||||
$dir = DataProvider::getWorkingDir() . "/resources/package/";
|
$dir = DataProvider::getWorkingDir() . "/resources/package/";
|
||||||
|
if (version_compare(SWOOLE_VERSION, "4.4.0", ">=")) Timer::clearAll();
|
||||||
if (is_dir($dir)) {
|
if (is_dir($dir)) {
|
||||||
$list = scandir($dir);
|
$list = scandir($dir);
|
||||||
unset($list[0], $list[1]);
|
unset($list[0], $list[1]);
|
||||||
foreach ($list as $v) {
|
foreach ($list as $v) {
|
||||||
if (is_dir($dir . $v)) continue;
|
if (is_dir($dir . $v)) continue;
|
||||||
if (pathinfo($dir . $v, 4) == "phar") require_once($dir . $v);
|
if (pathinfo($dir . $v, 4) == "phar") {
|
||||||
|
Console::verbose("加载Phar: " . $dir . $v . " 中");
|
||||||
|
require_once($dir . $v);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//加载composer类
|
//加载composer类
|
||||||
if (file_exists(DataProvider::getWorkingDir() . "/vendor/autoload.php")) {
|
//remove stupid duplicate code
|
||||||
Console::info("加载composer资源中");
|
|
||||||
require_once DataProvider::getWorkingDir() . "/vendor/autoload.php";
|
|
||||||
} else {
|
|
||||||
if(isPharMode()) require_once WORKING_DIR . "/vendor/autoload.php";
|
|
||||||
}
|
|
||||||
|
|
||||||
//加载各个模块的注解类,以及反射
|
//加载各个模块的注解类,以及反射
|
||||||
Console::info("检索Module中");
|
Console::info("检索Module中");
|
||||||
@@ -158,6 +187,7 @@ class WorkerStartEvent implements SwooleEvent
|
|||||||
ConnectionManager::registerCustomClass();
|
ConnectionManager::registerCustomClass();
|
||||||
|
|
||||||
//加载自定义的全局函数
|
//加载自定义的全局函数
|
||||||
|
Console::debug("加载自定义的全局函数中");
|
||||||
if (file_exists(DataProvider::getWorkingDir() . "/src/Custom/global_function.php"))
|
if (file_exists(DataProvider::getWorkingDir() . "/src/Custom/global_function.php"))
|
||||||
require_once DataProvider::getWorkingDir() . "/src/Custom/global_function.php";
|
require_once DataProvider::getWorkingDir() . "/src/Custom/global_function.php";
|
||||||
$this->afterCheck();
|
$this->afterCheck();
|
||||||
@@ -165,7 +195,7 @@ class WorkerStartEvent implements SwooleEvent
|
|||||||
|
|
||||||
private function setAutosaveTimer($globals) {
|
private function setAutosaveTimer($globals) {
|
||||||
DataProvider::$buffer_list = [];
|
DataProvider::$buffer_list = [];
|
||||||
Timer::tick($globals * 1000, function () {
|
zm_timer_tick($globals * 1000, function () {
|
||||||
DataProvider::saveBuffer();
|
DataProvider::saveBuffer();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -184,7 +184,7 @@ class Response
|
|||||||
* @return mixed
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
public function redirect($location, $http_code = null) {
|
public function redirect($location, $http_code = null) {
|
||||||
return $this->redirect($location, $http_code);
|
return $this->response->redirect($location, $http_code);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
35
src/ZM/Http/StaticFileHandler.php
Normal file
35
src/ZM/Http/StaticFileHandler.php
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace ZM\Http;
|
||||||
|
|
||||||
|
|
||||||
|
use Framework\Console;
|
||||||
|
use Framework\ZMBuf;
|
||||||
|
use ZM\Utils\ZMUtil;
|
||||||
|
|
||||||
|
class StaticFileHandler
|
||||||
|
{
|
||||||
|
public function __construct($filename, $path) {
|
||||||
|
$full_path = realpath($path . "/" . $filename);
|
||||||
|
$response = ctx()->getResponse();
|
||||||
|
Console::debug("Full path: ".$full_path);
|
||||||
|
if ($full_path !== false) {
|
||||||
|
if (strpos($full_path, $path) !== 0) {
|
||||||
|
$response->status(403);
|
||||||
|
$response->end("403 Forbidden");
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
if(is_file($full_path)) {
|
||||||
|
$exp = strtolower(pathinfo($full_path)['extension'] ?? "unknown");
|
||||||
|
$response->setHeader("Content-Type", ZMBuf::config("file_header")[$exp] ?? "application/octet-stream");
|
||||||
|
$response->end(file_get_contents($full_path));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$response->status(404);
|
||||||
|
$response->end(ZMUtil::getHttpCodePage(404));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,6 +15,11 @@ use ZM\Http\Response;
|
|||||||
use Swoole\WebSocket\Frame;
|
use Swoole\WebSocket\Frame;
|
||||||
use Swoole\WebSocket\Server;
|
use Swoole\WebSocket\Server;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class ModBase
|
||||||
|
* @package ZM
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
abstract class ModBase
|
abstract class ModBase
|
||||||
{
|
{
|
||||||
/** @var Server */
|
/** @var Server */
|
||||||
|
|||||||
@@ -10,9 +10,10 @@ namespace ZM\Utils;
|
|||||||
|
|
||||||
use framework\Console;
|
use framework\Console;
|
||||||
use framework\ZMBuf;
|
use framework\ZMBuf;
|
||||||
|
use PDO;
|
||||||
|
use PDOException;
|
||||||
use SplQueue;
|
use SplQueue;
|
||||||
use Swoole\Coroutine;
|
use Swoole\Coroutine;
|
||||||
use Swoole\Coroutine\Mysql;
|
|
||||||
|
|
||||||
class SQLPool
|
class SQLPool
|
||||||
{
|
{
|
||||||
@@ -32,6 +33,26 @@ class SQLPool
|
|||||||
"password" => ZMBuf::globals("sql_config")["sql_password"],
|
"password" => ZMBuf::globals("sql_config")["sql_password"],
|
||||||
"database" => ZMBuf::globals("sql_config")["sql_database"]
|
"database" => ZMBuf::globals("sql_config")["sql_database"]
|
||||||
];
|
];
|
||||||
|
Console::debug("新建检测 MySQL 连接的计时器");
|
||||||
|
zm_timer_tick(10000, function () {
|
||||||
|
//Console::debug("正在检测是否有坏死的MySQL连接,当前连接池有 ".count($this->pool) . " 个连接");
|
||||||
|
if (count($this->pool) > 0) {
|
||||||
|
/** @var PDO $cnn */
|
||||||
|
$cnn = $this->pool->pop();
|
||||||
|
$this->connect_cnt -= 1;
|
||||||
|
try {
|
||||||
|
$cnn->getAttribute(PDO::ATTR_SERVER_INFO);
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
if (strpos($e->getMessage(), 'MySQL server has gone away') !== false) {
|
||||||
|
Console::info("MySQL 长连接丢失,取消连接");
|
||||||
|
unset($cnn);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->pool->push($cnn);
|
||||||
|
$this->connect_cnt += 1;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -48,11 +69,11 @@ class SQLPool
|
|||||||
/**
|
/**
|
||||||
* 获取队中的连接,如果不存在则创建新的
|
* 获取队中的连接,如果不存在则创建新的
|
||||||
* @param bool $no_new_conn
|
* @param bool $no_new_conn
|
||||||
* @return bool|mixed|Mysql
|
* @return bool|mixed|PDO
|
||||||
*/
|
*/
|
||||||
public function get($no_new_conn = false) {
|
public function get($no_new_conn = false) {
|
||||||
if (count($this->pool) == 0 && $this->connect_cnt <= 70) {
|
if (count($this->pool) == 0 && $this->connect_cnt <= 70) {
|
||||||
if($no_new_conn) return false;
|
if ($no_new_conn) return false;
|
||||||
$this->connect_cnt += 1;
|
$this->connect_cnt += 1;
|
||||||
$r = $this->newConnect();
|
$r = $this->newConnect();
|
||||||
if ($r !== false) {
|
if ($r !== false) {
|
||||||
@@ -62,11 +83,12 @@ class SQLPool
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} elseif (count($this->pool) > 0) {
|
} elseif (count($this->pool) > 0) {
|
||||||
|
/** @var PDO $con */
|
||||||
$con = $this->pool->pop();
|
$con = $this->pool->pop();
|
||||||
if ($con->connected !== false) return $con;
|
return $con;
|
||||||
} elseif ($this->connect_cnt > 70) {
|
} elseif ($this->connect_cnt > 70) {
|
||||||
$this->co_list[]=Coroutine::getuid();
|
$this->co_list[] = Coroutine::getuid();
|
||||||
Console::warning("数据库连接过多,协程等待重复利用中...当前协程数 ".Coroutine::stats()["coroutine_num"]);
|
Console::warning("数据库连接过多,协程等待重复利用中...当前协程数 " . Coroutine::stats()["coroutine_num"]);
|
||||||
Coroutine::suspend();
|
Coroutine::suspend();
|
||||||
return $this->get($no_new_conn);
|
return $this->get($no_new_conn);
|
||||||
}
|
}
|
||||||
@@ -87,15 +109,14 @@ class SQLPool
|
|||||||
|
|
||||||
private function newConnect() {
|
private function newConnect() {
|
||||||
//无空闲连接,创建新连接
|
//无空闲连接,创建新连接
|
||||||
$mysql = new Mysql();
|
$dsn = "mysql:host=" . $this->info["host"] . ";dbname=" . $this->info["database"] . ";charset=utf8";
|
||||||
|
try {
|
||||||
Console::info("创建SQL连接中,当前有" . $this->connect_cnt . "个连接");
|
$mysql = new PDO($dsn, $this->info["user"], $this->info["password"], array(PDO::ATTR_PERSISTENT => true));
|
||||||
$res = $mysql->connect($this->info);
|
} catch (PDOException $e) {
|
||||||
if ($res == false) {
|
Console::error("PDO Error: " . $e->getMessage());
|
||||||
echo $mysql->error . PHP_EOL;
|
|
||||||
return false;
|
return false;
|
||||||
} else {
|
|
||||||
return $mysql;
|
|
||||||
}
|
}
|
||||||
|
Console::info("创建SQL连接中,当前有" . $this->connect_cnt . "个连接");
|
||||||
|
return $mysql;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
namespace ZM\Utils;
|
namespace ZM\Utils;
|
||||||
|
|
||||||
|
|
||||||
|
use Framework\Console;
|
||||||
use Swlib\Saber;
|
use Swlib\Saber;
|
||||||
use Swoole\Coroutine\Http\Client;
|
use Swoole\Coroutine\Http\Client;
|
||||||
|
|
||||||
@@ -11,20 +12,21 @@ class ZMRequest
|
|||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* 使用Swoole协程客户端发起HTTP GET请求
|
* 使用Swoole协程客户端发起HTTP GET请求
|
||||||
* @version 1.1
|
|
||||||
* 返回请求后的body
|
|
||||||
* 如果请求失败或返回状态不是200,则返回 false
|
|
||||||
* @param $url
|
* @param $url
|
||||||
* @param array $headers
|
* @param array $headers
|
||||||
* @param array $set
|
* @param array $set
|
||||||
* @param bool $return_body
|
* @param bool $return_body
|
||||||
* @return bool|string|Client
|
* @return bool|string|Client
|
||||||
|
* @version 1.1
|
||||||
|
* 返回请求后的body
|
||||||
|
* 如果请求失败或返回状态不是200,则返回 false
|
||||||
*/
|
*/
|
||||||
public static function get($url, $headers = [], $set = [], $return_body = true) {
|
public static function get($url, $headers = [], $set = [], $return_body = true) {
|
||||||
$parse = parse_url($url);
|
/** @var Client $cli */
|
||||||
$cli = new Client($parse["host"], ($parse["scheme"] == "https" ? 443 : (isset($parse["port"]) ? $parse["port"] : 80)), ($parse["scheme"] == "https" ? true : false));
|
list($cli, $parse) = self::getNewClient($url);
|
||||||
$cli->setHeaders($headers);
|
if($cli === null) return false;
|
||||||
$cli->set($set == [] ? ['timeout' => 15.0] : $set);
|
$cli->set($set == [] ? ['timeout' => 15.0] : $set);
|
||||||
|
$cli->setHeaders($headers);
|
||||||
$cli->get($parse["path"] . (isset($parse["query"]) ? "?" . $parse["query"] : ""));
|
$cli->get($parse["path"] . (isset($parse["query"]) ? "?" . $parse["query"] : ""));
|
||||||
if ($return_body) {
|
if ($return_body) {
|
||||||
if ($cli->errCode != 0 || $cli->statusCode != 200) return false;
|
if ($cli->errCode != 0 || $cli->statusCode != 200) return false;
|
||||||
@@ -49,8 +51,9 @@ class ZMRequest
|
|||||||
* @return bool|string|Client
|
* @return bool|string|Client
|
||||||
*/
|
*/
|
||||||
public static function post($url, array $header, $data, $set = [], $return_body = true) {
|
public static function post($url, array $header, $data, $set = [], $return_body = true) {
|
||||||
$parse = parse_url($url);
|
/** @var Client $cli */
|
||||||
$cli = new Client($parse["host"], ($parse["scheme"] == "https" ? 443 : (isset($parse["port"]) ? $parse["port"] : 80)), ($parse["scheme"] == "https" ? true : false));
|
list($cli, $parse) = self::getNewClient($url);
|
||||||
|
if($cli === null) return false;
|
||||||
$cli->set($set == [] ? ['timeout' => 15.0] : $set);
|
$cli->set($set == [] ? ['timeout' => 15.0] : $set);
|
||||||
$cli->setHeaders($header);
|
$cli->setHeaders($header);
|
||||||
$cli->post($parse["path"] . (isset($parse["query"]) ? ("?" . $parse["query"]) : ""), $data);
|
$cli->post($parse["path"] . (isset($parse["query"]) ? ("?" . $parse["query"]) : ""), $data);
|
||||||
@@ -65,6 +68,17 @@ class ZMRequest
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $url
|
||||||
|
* @param array $set
|
||||||
|
* @param array $header
|
||||||
|
* @return ZMWebSocket
|
||||||
|
* @since 1.5
|
||||||
|
*/
|
||||||
|
public static function websocket($url, $set = ['websocket_mask' => true], $header = []) {
|
||||||
|
return new ZMWebSocket($url, $set, $header);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param $option
|
* @param $option
|
||||||
* @return Saber
|
* @return Saber
|
||||||
@@ -72,4 +86,68 @@ class ZMRequest
|
|||||||
public static function session($option) {
|
public static function session($option) {
|
||||||
return Saber::session($option);
|
return Saber::session($option);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
|
* @param $url
|
||||||
|
* @param array $attribute
|
||||||
|
* @param bool $return_body
|
||||||
|
* @return bool|string|Client
|
||||||
|
*/
|
||||||
|
public static function request($url, $attribute = [], $return_body = true) {
|
||||||
|
/** @var Client $cli */
|
||||||
|
list($cli, $parse) = self::getNewClient($url);
|
||||||
|
if($cli === null) return false;
|
||||||
|
$cli->set($attribute["set"] ?? ["timeout" => 15.0]);
|
||||||
|
$cli->setMethod($attribute["method"] ?? "GET");
|
||||||
|
$cli->setHeaders($attribute["headers"] ?? []);
|
||||||
|
if(isset($attribute["data"])) $cli->setData($attribute["data"]);
|
||||||
|
if(isset($attribute["file"])) {
|
||||||
|
foreach($attribute["file"] as $k => $v) {
|
||||||
|
$cli->addFile($v["path"], $v["name"], $v["mime_type"] ?? null, $v["filename"] ?? null, $v["offset"] ?? 0, $v["length"] ?? 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$cli->execute($parse["path"] . (isset($parse["query"]) ? "?" . $parse["query"] : ""));
|
||||||
|
if ($return_body) {
|
||||||
|
if ($cli->errCode != 0 || $cli->statusCode != 200) return false;
|
||||||
|
$a = $cli->body;
|
||||||
|
$cli->close();
|
||||||
|
return $a;
|
||||||
|
} else {
|
||||||
|
$cli->close();
|
||||||
|
return $cli;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $url
|
||||||
|
* @param null|bool $dst
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function downloadFile($url, $dst = null) {
|
||||||
|
/** @var Client $cli */
|
||||||
|
list($cli, $parse) = self::getNewClient($url);
|
||||||
|
if($cli === null) return false;
|
||||||
|
$cli->set(["timeout" => 60.0]);
|
||||||
|
$save_path = $dst === null ? "/tmp/_zm_".mt_rand(1000000, 9999999) : $dst;
|
||||||
|
$result = $cli->download($parse["path"] . (isset($parse["query"]) ? "?" . $parse["query"] : ""), $save_path);
|
||||||
|
if($result === false) return false;
|
||||||
|
elseif ($dst === null) return $save_path;
|
||||||
|
else return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $url
|
||||||
|
* @return bool|array
|
||||||
|
*/
|
||||||
|
private static function getNewClient($url) {
|
||||||
|
$parse = parse_url($url);
|
||||||
|
if (!isset($parse["host"])) {
|
||||||
|
Console::warning("ZMRequest: url must contains scheme such as \"http(s)://\"");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(!isset($parse["path"])) $parse["path"] = "/";
|
||||||
|
$port = $parse["port"] ?? (($parse["scheme"] ?? "http") == "https" ? 443 : 80);
|
||||||
|
$cli = new Client($parse["host"], $port, ($parse["scheme"] ?? "http") == "https");
|
||||||
|
return [$cli, $parse];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ class ZMUtil
|
|||||||
|
|
||||||
public static function stop($without_shutdown = false) {
|
public static function stop($without_shutdown = false) {
|
||||||
Console::info(Console::setColor("Stopping server...", "red"));
|
Console::info(Console::setColor("Stopping server...", "red"));
|
||||||
foreach (ZMBuf::$server->connections as $v) {
|
foreach ((ZMBuf::$server->connections ?? []) as $v) {
|
||||||
ZMBuf::$server->close($v);
|
ZMBuf::$server->close($v);
|
||||||
}
|
}
|
||||||
DataProvider::saveBuffer();
|
DataProvider::saveBuffer();
|
||||||
|
|||||||
106
src/ZM/Utils/ZMWebSocket.php
Normal file
106
src/ZM/Utils/ZMWebSocket.php
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace ZM\Utils;
|
||||||
|
|
||||||
|
|
||||||
|
use Framework\Console;
|
||||||
|
use Swoole\Coroutine\Http\Client;
|
||||||
|
use Swoole\WebSocket\Frame;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class ZMWebSocket
|
||||||
|
* @package ZM\Utils
|
||||||
|
* @since 1.5
|
||||||
|
*/
|
||||||
|
class ZMWebSocket
|
||||||
|
{
|
||||||
|
private $parse;
|
||||||
|
private $client;
|
||||||
|
|
||||||
|
public $is_available = false;
|
||||||
|
|
||||||
|
private $close_func;
|
||||||
|
private $message_func;
|
||||||
|
|
||||||
|
public function __construct($url, $set = ['websocket_mask' => true], $header = []) {
|
||||||
|
$this->parse = parse_url($url);
|
||||||
|
if (!isset($this->parse["host"])) {
|
||||||
|
Console::warning("ZMRequest: url must contains scheme such as \"ws(s)://\"");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!isset($this->parse["path"])) $this->parse["path"] = "/";
|
||||||
|
$port = $this->parse["port"] ?? (($this->parse["scheme"] ?? "ws") == "wss" ? 443 : 80);
|
||||||
|
$this->client = new Client($this->parse["host"], $port, (($this->parse["scheme"] ?? "ws") == "wss" ? true : false));
|
||||||
|
$this->client->set($set);
|
||||||
|
if ($header != []) $this->client->setHeaders($header);
|
||||||
|
$this->is_available = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function upgrade() {
|
||||||
|
if (!$this->is_available) return false;
|
||||||
|
$r = $this->client->upgrade($this->parse["path"] . (isset($this->parse["query"]) ? ("?" . $this->parse["query"]) : ""));
|
||||||
|
if ($r) {
|
||||||
|
go(function () {
|
||||||
|
while (true) {
|
||||||
|
$result = $this->client->recv(60);
|
||||||
|
if ($result === false) {
|
||||||
|
if ($this->client->connected === false) {
|
||||||
|
go(function () {
|
||||||
|
call_user_func($this->close_func, $this->client);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} elseif ($result instanceof Frame) {
|
||||||
|
go(function () use ($result) {
|
||||||
|
$this->is_available = false;
|
||||||
|
call_user_func($this->message_func, $result, $this->client);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param callable $callable
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function onMessage(callable $callable) {
|
||||||
|
$this->message_func = $callable;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param callable $callable
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function onClose(callable $callable) {
|
||||||
|
$this->close_func = $callable;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!debug_backtrace()) {
|
||||||
|
go(function () {
|
||||||
|
require_once __DIR__ . "/../../Framework/Console.php";
|
||||||
|
$cli = new ZMWebSocket("ws://127.0.0.1:20001/");
|
||||||
|
if (!$cli->is_available) die("Error!\n");
|
||||||
|
$cli->onMessage(function (Frame $frame) {
|
||||||
|
var_dump($frame);
|
||||||
|
});
|
||||||
|
$cli->onClose(function () {
|
||||||
|
echo "Connection closed.\n";
|
||||||
|
});
|
||||||
|
if ($cli->upgrade()) {
|
||||||
|
echo "成功连接!\n";
|
||||||
|
} else {
|
||||||
|
echo "连接失败!\n";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user