mirror of
https://github.com/zhamao-robot/zhamao-framework.git
synced 2026-07-02 22:35:38 +08:00
Compare commits
54 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
ec88c56137 | ||
|
|
acb4bdf9b4 | ||
|
|
a1b013ee53 | ||
|
|
086c65af26 | ||
|
|
9b3a2e5296 | ||
|
|
181f6430a4 | ||
|
|
d5c192108e | ||
|
|
0c28dda808 | ||
|
|
b61eeccdb8 | ||
|
|
19d618f167 | ||
|
|
17ce0c20ae | ||
|
|
82a1f86bbd | ||
|
|
97e579b8a1 |
@@ -2,4 +2,4 @@
|
||||
if [ ! -d "/app/zhamao-framework/bin" ]; then
|
||||
cp -r /app/zhamao-framework-bak/* /app/zhamao-framework/
|
||||
fi
|
||||
php /app/zhamao-framework/bin/start framework --disable-console-input
|
||||
php /app/zhamao-framework/bin/start
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -4,4 +4,5 @@
|
||||
/vendor/
|
||||
zm.json
|
||||
/zm_data/
|
||||
composer.lock
|
||||
composer.lock
|
||||
/resources/server.phar
|
||||
|
||||
22
Dockerfile
22
Dockerfile
@@ -1,15 +1,29 @@
|
||||
FROM phpswoole/swoole:4.4.15-php7.3
|
||||
FROM ubuntu:18.04
|
||||
WORKDIR /app/
|
||||
RUN echo "Asia/Shanghai" > /etc/timezone
|
||||
#RUN dpkg-configure -f noninteractive tzdata
|
||||
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/"]
|
||||
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-bak
|
||||
#RUN cd /app/zhamao-framework && composer update && composer clearcache
|
||||
#RUN mv zhamao-framework-master zhamao-framework
|
||||
WORKDIR /app/zhamao-framework
|
||||
|
||||
EXPOSE 20001
|
||||
CMD ["bash", "/app/zhamao-framework-bak/.entry.sh"]
|
||||
CMD ["/bin/bash", "-i", "/app/zhamao-framework-bak/.entry.sh"]
|
||||
|
||||
30
README.md
30
README.md
@@ -2,12 +2,12 @@
|
||||
|
||||
[]()
|
||||
[](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=TODO)
|
||||
|
||||
一个异步、多平台兼容的 **聊天机器人** 框架。
|
||||
协程高性能的 **QQ 机器人 + Web 服务器** 开发框架(炸毛框架)。
|
||||
|
||||
<img src="https://avatars0.githubusercontent.com/u/48620312" height = "200" alt="炸毛框架" align=center/>
|
||||
|
||||
@@ -19,23 +19,27 @@ zhamao-framework 是一个基于 酷Q 的 PHP Swoole 的机器人框架,它会
|
||||
除了起到解析消息的作用,炸毛框架 还提供了完整的 WebSocket + HTTP 服务器,你还能用此框架构建出高性能的 API 接口服务器。
|
||||
|
||||
## 开始
|
||||
你可以使用 GitHub 的 `Use This Template` 功能快速将本项目克隆到你的公开或私有仓库,也可以从 `release` 中下载本项目的最新版本到本地私有开发。
|
||||
先安装环境,环境安装见下方文档。
|
||||
1. `composer create-project zhamao/framework-starter` 从模板新建基础文档结构进行使用
|
||||
2. 你也可以直接到 **Release** 中下载最新的 phar 包,放入文件夹后 `php server.phar` 快速启动框架
|
||||
3. 还可以使用 Dockerfile 构建 Docker 容器
|
||||
|
||||
## 文档
|
||||
本项目文档正在努力编写中。
|
||||
Pages托管:[https://framework.zhamao.xin/](https://framework.zhamao.xin/)
|
||||
|
||||
Pages:[https://framework.zhamao.xin/](https://framework.zhamao.xin/)
|
||||
|
||||
如果上面的访问较慢,可以访问国内服务器:[https://framework2.zhamao.xin/](https://framework2.zhamao.xin/)
|
||||
国内服务器:[https://framework2.zhamao.xin/](https://framework2.zhamao.xin/)
|
||||
|
||||
## 特点
|
||||
- 支持多账号
|
||||
- 灵活的注解事件绑定机制
|
||||
- 支持下断点调试(Psysh)
|
||||
- 易用的上下文,模块内随处可用
|
||||
- 采用模块化编写,功能之间高内聚低耦合
|
||||
- 常驻内存,全局缓存变量随处使用
|
||||
- 自带 MySQL 查询器、数据库连接池等数据库连接方案
|
||||
- 自带 HTTP 服务器、WebSocket 服务器可复用,可以构建属于自己的 HTTP API 接口
|
||||
- 静态文件服务器
|
||||
- 支持 phar 一键打包
|
||||
|
||||
## 炸毛特色模块
|
||||
|
||||
@@ -45,20 +49,18 @@ Pages:[https://framework.zhamao.xin/](https://framework.zhamao.xin/)
|
||||
| 通用模块 | 图片上传和下载模块 | [zhamao-general-tools](https://github.com/zhamao-robot/zhamao-general-tools) |
|
||||
|
||||
## 计划开发内容
|
||||
- [ ] WebSocket测试脚本(客户端)
|
||||
- [X] WebSocket测试脚本(客户端)
|
||||
- [X] Session 和中间层管理模块
|
||||
- [ ] 支持本地和远程两种方式的定时器(计划任务)
|
||||
- [X] 常驻服务脚本
|
||||
- [ ] 一些常用的通用 API 例如经济(用户积分、亲密度等)的模块
|
||||
- [X] 一些常用的通用 API 例如经济(用户积分、亲密度等)的模块
|
||||
- [ ] 图灵机器人/腾讯AI 聊天模块
|
||||
- [ ] 分词模块(可能会放弃计划,因为目前好用的分词都是其他语言的)
|
||||
- [ ] HTTP 过滤器、Auth 模块、完整的 MVC 兼容(可能会放弃计划,因为框架主打机器人开发)
|
||||
- [ ] Redis 连接池或开箱即用的相应功能内置
|
||||
- [ ] 2.0 版本抛弃 `ModBase` 继承结构,完全使用上下文代替
|
||||
- [X] 1.3 版本使用上下文代替
|
||||
- [X] 更好的 Logger,稳定和漂亮的控制台输出
|
||||
- [ ] 日志服务
|
||||
- [ ] 模块支持 Phar 打包(可能会比较靠后支持)
|
||||
- [ ] 兼容面向过程的模块编写方案(可能会比较靠后支持)
|
||||
- [X] 框架支持 Phar 打包(可能会比较靠后支持)
|
||||
- [ ] 完整的单元测试(如果有需求则尽快开发)
|
||||
- [X] 静态文件服务器
|
||||
|
||||
@@ -82,6 +84,6 @@ Pages:[https://framework.zhamao.xin/](https://framework.zhamao.xin/)
|
||||
|
||||
欢迎随时在 HTTP-API 插件群里提问,当然更好的话可以加作者 QQ(627577391)或提交 Issue 进行疑难解答。
|
||||
|
||||
本项目在更行内容时,请及时关注 GitHub 动态,更新前请将自己的模块代码做好备份。
|
||||
本项目在更新内容时,请及时关注 GitHub 动态,更新前请将自己的模块代码做好备份。
|
||||
|
||||
项目框架采用 Apache-2.0 协议开源,在分发或重写修改等操作时需遵守协议。项目模块部分(`Module` 文件夹) 在非借鉴框架内代码时可不遵守 Apache-2.0 协议进行分发和修改(声明版权)。
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
| Version | Supported |
|
||||
| ------- | ------------------ |
|
||||
| 1.2 | :white_check_mark: |
|
||||
| 1.2.x | :white_check_mark: |
|
||||
| 1.1.x | :x: |
|
||||
| 1.0.x | :x: |
|
||||
|
||||
|
||||
60
bin/phar-build
Executable file
60
bin/phar-build
Executable file
@@ -0,0 +1,60 @@
|
||||
#!/usr/bin/env php
|
||||
<?php /** @since 1.2.1 */
|
||||
global $version;
|
||||
echo "version: " . ($version = json_decode(file_get_contents(__DIR__ . "/../composer.json"), true)["version"]) . PHP_EOL;
|
||||
|
||||
switch ($argv[1] ?? '') {
|
||||
case '--normal':
|
||||
case '':
|
||||
build();
|
||||
break;
|
||||
case '--help':
|
||||
case '-h':
|
||||
default:
|
||||
echo "\nzhamao-framework Phar builder.\n";
|
||||
echo "\nUsage: " . $argv[0] . " [OPTION]";
|
||||
echo "\n\n -h, --help\t\tShow this help menu";
|
||||
echo "\n --with-wechat-patch\tReplace ModBase with wechat patch version and build your own phar package";
|
||||
echo "\n --normal\t\tBuild your own phar package as normal options\n\n";
|
||||
break;
|
||||
}
|
||||
|
||||
function build($with_wechat_patch = false) {
|
||||
if (ini_get('phar.readonly') == 1) {
|
||||
die("You need to set \"phar.readonly\" to \"Off\"!\nSee: https://stackoverflow.com/questions/34667606/cant-enable-phar-writing\n");
|
||||
}
|
||||
$filename = "server.phar";
|
||||
@unlink(__DIR__ . '/../resources/' . $filename);
|
||||
$phar = new Phar(__DIR__ . '/../resources/' . $filename);
|
||||
$phar->startBuffering();
|
||||
$src = realpath(__DIR__ . '/../');
|
||||
$hello = file_get_contents($src . '/src/Module/Example/Hello.php');
|
||||
$middleware = file_get_contents($src . '/src/Module/Middleware/TimerMiddleware.php');
|
||||
unlink($src . '/src/Module/Example/Hello.php');
|
||||
unlink($src . '/src/Module/Middleware/TimerMiddleware.php');
|
||||
if ($with_wechat_patch) {
|
||||
global $wechat_patch;
|
||||
$wechat = base64_decode($wechat_patch);
|
||||
} else {
|
||||
$wechat = false;
|
||||
}
|
||||
if ($wechat !== false) {
|
||||
echo "Using wechat patch.\n";
|
||||
$modbase = file_get_contents($src . '/src/ZM/ModBase.php');
|
||||
unlink($src . '/src/ZM/ModBase.php');
|
||||
}
|
||||
$phar->buildFromDirectory($src);
|
||||
$phar->addFromString('tmp/Hello.php.bak', $hello);
|
||||
$phar->addFromString('tmp/TimerMiddleware.php.bak', $middleware);
|
||||
if ($wechat !== false) {
|
||||
$phar->addFromString('src/ZM/ModBase.php', $wechat);
|
||||
file_put_contents($src . '/src/ZM/ModBase.php', $modbase);
|
||||
}
|
||||
//$phar->compressFiles(Phar::GZ);
|
||||
$phar->setStub($phar->createDefaultStub('phar-starter.php'));
|
||||
$phar->stopBuffering();
|
||||
file_put_contents($src . '/src/Module/Example/Hello.php', $hello);
|
||||
file_put_contents($src . '/src/Module/Middleware/TimerMiddleware.php', $middleware);
|
||||
echo "Successfully built. Location: " . $src . "/resources/$filename\n";
|
||||
}
|
||||
|
||||
92
bin/start
92
bin/start
@@ -10,6 +10,14 @@ require __DIR__ . '/../src/Scheduler/Scheduler.php';
|
||||
Swoole\Coroutine::set([
|
||||
'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");
|
||||
|
||||
@@ -24,13 +32,86 @@ switch ($argv[1] ?? '') {
|
||||
}
|
||||
});
|
||||
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 'framework':
|
||||
case 'server':
|
||||
if(!is_dir(__DIR__.'/../vendor/')){
|
||||
if (!is_dir(__DIR__ . '/../vendor/') && LOAD_MODE == 0) {
|
||||
echo "Warning: you have not update composer!\n";
|
||||
exec("composer update", $out, $var);
|
||||
if($var != 0) {
|
||||
if ($var != 0) {
|
||||
echo "You need to run \"composer update\" at root of zhamao-framework!\n";
|
||||
die;
|
||||
}
|
||||
@@ -39,10 +120,13 @@ switch ($argv[1] ?? '') {
|
||||
break;
|
||||
case '--help':
|
||||
case '-h':
|
||||
echo "\nUsage: ".$argv[0]." [OPTION]\n";
|
||||
echo "\nUsage: " . $argv[0] . " [OPTION]\n";
|
||||
echo "\nzhamao-framework start script, provides several startup arguments.";
|
||||
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;
|
||||
default:
|
||||
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 */
|
||||
switch ($argv[1] ?? '') {
|
||||
case '--generate':
|
||||
generate();
|
||||
case '':
|
||||
generate($argv);
|
||||
break;
|
||||
case '--help':
|
||||
case '-h':
|
||||
@@ -14,13 +15,17 @@ switch ($argv[1] ?? '') {
|
||||
break;
|
||||
}
|
||||
|
||||
function generate() {
|
||||
function generate($argv) {
|
||||
$s = "[Unit]\nDescription=zhamao-framework Daemon\nAfter=rc-local.service\n\n[Service]\nType=simple";
|
||||
$s .= "\nUser=" . exec("whoami");
|
||||
$s .= "\nGroup=" . exec("groups | awk '{print $1}'");
|
||||
$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";
|
||||
@mkdir(getcwd() . "/resources/");
|
||||
file_put_contents(getcwd() . "/resources/zhamao.service", $s);
|
||||
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",
|
||||
"description": "high-performance intelligent assistant",
|
||||
"description": "High performance QQ robot and web server development framework",
|
||||
"minimum-stability": "stable",
|
||||
"license": "proprietary",
|
||||
"version": "1.2.0",
|
||||
"license": "Apache-2.0",
|
||||
"version": "1.5.7",
|
||||
"authors": [
|
||||
{
|
||||
"name": "whale",
|
||||
@@ -14,20 +14,28 @@
|
||||
"email": "hugo_swift@yahoo.com"
|
||||
}
|
||||
],
|
||||
"prefer-stable": true,
|
||||
"bin": [
|
||||
"bin/start"
|
||||
],
|
||||
"require": {
|
||||
"php": ">=7.2",
|
||||
"swoole/ide-helper": "@dev",
|
||||
"ext-mbstring": "*",
|
||||
"swlib/saber": "^1.0",
|
||||
"doctrine/annotations": "<1.10.2",
|
||||
"doctrine/annotations": "~1.10",
|
||||
"ext-json": "*",
|
||||
"ext-posix": "*",
|
||||
"ext-ctype": "*",
|
||||
"ext-pcntl": "*"
|
||||
"ext-pdo": "*",
|
||||
"psy/psysh": "@stable"
|
||||
},
|
||||
"repositories": {
|
||||
"packagist": {
|
||||
"type": "composer",
|
||||
"url": "https://mirrors.aliyun.com/composer/"
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Custom\\": "src/Custom",
|
||||
"Framework\\": "src/Framework",
|
||||
"ZM\\": "src/ZM",
|
||||
"Module\\": "src/Module"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,8 +10,11 @@ $config['port'] = 20001;
|
||||
/** 框架开到公网或外部的HTTP访问链接,通过 DataProvider::getFrameworkLink() 获取 */
|
||||
$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/';
|
||||
@@ -35,7 +38,13 @@ $config['sql_config'] = [
|
||||
'sql_database' => 'db_name',
|
||||
'sql_password' => '',
|
||||
'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 */
|
||||
@@ -70,7 +79,7 @@ $config['context_class'] = \ZM\Context\Context::class;
|
||||
/** 静态文件访问 */
|
||||
$config['static_file_server'] = [
|
||||
'status' => false,
|
||||
'document_root' => WORKING_DIR . '/resources/html',
|
||||
'document_root' => realpath(__DIR__ . "/../") . '/resources/html',
|
||||
'document_index' => [
|
||||
'index.html'
|
||||
]
|
||||
|
||||
6
config/motd.txt
Normal file
6
config/motd.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
______
|
||||
|__ / |__ __ _ _ __ ___ __ _ ___
|
||||
/ /| '_ \ / _` | '_ ` _ \ / _` |/ _ \
|
||||
/ /_| | | | (_| | | | | | | (_| | (_) |
|
||||
/____|_| |_|\__,_|_| |_| |_|\__,_|\___/
|
||||
|
||||
18
docker_mixed/Dockerfile
Normal file
18
docker_mixed/Dockerfile
Normal file
@@ -0,0 +1,18 @@
|
||||
FROM richardchien/cqhttp:latest
|
||||
RUN apt-get update && apt-get install -y software-properties-common tzdata
|
||||
RUN dpkg-reconfigure -f noninteractive tzdata
|
||||
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 start.sh /home/user/start.sh
|
||||
RUN chown user:user /home/user/start.sh && chmod +x /home/user/start.sh
|
||||
ADD https://github.com/zhamao-robot/zhamao-framework/archive/master.zip /home/user/master.zip
|
||||
RUN chown user:user /home/user/master.zip && chmod 777 /home/user/master.zip
|
||||
VOLUME ["/home/user/coolq","/home/user/zhamao-framework"]
|
||||
6
docker_mixed/start.sh
Normal file
6
docker_mixed/start.sh
Normal file
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
unzip master.zip
|
||||
mv zhamao-framework-master/* zhamao-framework/
|
||||
cd zhamao-framework
|
||||
php bin/start
|
||||
85
phar-starter.php
Normal file
85
phar-starter.php
Normal file
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
global $is_phar;
|
||||
|
||||
use Framework\FrameworkLoader;
|
||||
|
||||
$is_phar = true;
|
||||
|
||||
if (substr(__DIR__, 0, 7) != 'phar://') {
|
||||
die("You can not run this script directly!\n");
|
||||
}
|
||||
|
||||
testEnvironment();
|
||||
|
||||
spl_autoload_register(function ($class) {
|
||||
//echo $class."\n";
|
||||
$exp = str_replace("\\", '/', $class);
|
||||
$exp = __DIR__ . '/src/' . $exp . '.php';
|
||||
if (is_file($exp)) {
|
||||
require_once $exp;
|
||||
}
|
||||
});
|
||||
|
||||
loadPhp(__DIR__ . '/src');
|
||||
|
||||
Swoole\Coroutine::set([
|
||||
'max_coroutine' => 30000,
|
||||
]);
|
||||
|
||||
date_default_timezone_set("Asia/Shanghai");
|
||||
|
||||
define('WORKING_DIR', __DIR__);
|
||||
define('FRAMEWORK_DIR', __DIR__);
|
||||
define('LOAD_MODE', 2);
|
||||
|
||||
$s = new FrameworkLoader($argv);
|
||||
|
||||
function loadPhp($dir) {
|
||||
$dirs = scandir($dir);
|
||||
foreach ($dirs as $v) {
|
||||
$path = $dir . '/' . $v;
|
||||
if (is_dir($path)) {
|
||||
loadPhp($path);
|
||||
} else {
|
||||
if (pathinfo($dir . '/' . $v)['extension'] == 'php') {
|
||||
if(pathinfo($dir . '/' . $v)['basename'] == 'terminal_listener.php') continue;
|
||||
//echo 'loading '.$path.PHP_EOL;
|
||||
require_once $path;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function testEnvironment() {
|
||||
$current_dir = realpath('.');
|
||||
@mkdir($current_dir . '/config/');
|
||||
if (!is_file($current_dir . '/config/global.php')) {
|
||||
echo "Exporting default global config...\n";
|
||||
$global = file_get_contents(__DIR__ . '/config/global.php');
|
||||
$global = str_replace("WORKING_DIR", 'realpath(__DIR__ . "/../")', $global);
|
||||
file_put_contents($current_dir . '/config/global.php', $global);
|
||||
}
|
||||
if (!is_file($current_dir . '/config/file_header.json')) {
|
||||
echo "Exporting default file_header config...\n";
|
||||
$global = file_get_contents(__DIR__ . '/config/file_header.json');
|
||||
file_put_contents($current_dir . '/config/file_header.json', $global);
|
||||
}
|
||||
if (!is_dir($current_dir . '/resources')) mkdir($current_dir . '/resources');
|
||||
if (!is_dir($current_dir . '/src')) mkdir($current_dir . '/src');
|
||||
if (!is_dir($current_dir . '/src')) mkdir($current_dir . '/src');
|
||||
if (!is_dir($current_dir . '/src/Module')) {
|
||||
mkdir($current_dir . '/src/Module');
|
||||
mkdir($current_dir . '/src/Module/Example');
|
||||
file_put_contents($current_dir . '/src/Module/Example/Hello.php', file_get_contents(__DIR__ . '/tmp/Hello.php.bak'));
|
||||
mkdir($current_dir . '/src/Module/Middleware');
|
||||
file_put_contents($current_dir . '/src/Module/Middleware/TimerMiddleware.php', file_get_contents(__DIR__ . '/tmp/TimerMiddleware.php.bak'));
|
||||
}
|
||||
if (!is_dir($current_dir . '/src/Custom')) {
|
||||
mkdir($current_dir . '/src/Custom');
|
||||
mkdir($current_dir . '/src/Custom/Annotation');
|
||||
mkdir($current_dir . '/src/Custom/Connection');
|
||||
file_put_contents($current_dir . '/src/Custom/global_function.php', "<?php\n\n//这里写你的全局方法");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
<?php
|
||||
|
||||
//这里写你的全局函数
|
||||
|
||||
function phptest(){
|
||||
echo "Nothing.\n";
|
||||
}
|
||||
@@ -8,12 +8,19 @@
|
||||
|
||||
namespace Framework;
|
||||
|
||||
use co;
|
||||
use ZM\Annotation\Swoole\SwooleEventAt;
|
||||
use ZM\Connection\WSConnection;
|
||||
use ZM\Utils\ZMUtil;
|
||||
use Exception;
|
||||
|
||||
class Console
|
||||
{
|
||||
/**
|
||||
* @var false|resource
|
||||
*/
|
||||
public static $console_proc = null;
|
||||
public static $pipes = [];
|
||||
|
||||
static function setColor($string, $color = "") {
|
||||
switch ($color) {
|
||||
case "red":
|
||||
@@ -61,14 +68,14 @@ class Console
|
||||
$trace = debug_backtrace()[1] ?? ['file' => '', 'function' => ''];
|
||||
$trace = "[" . basename($trace["file"], ".php") . ":" . $trace["function"] . "] ";
|
||||
}
|
||||
if(ZMBuf::$atomics["info_level"]->get() >= 1) {
|
||||
if (ZMBuf::$atomics["info_level"]->get() >= 1) {
|
||||
if (!is_string($obj)) {
|
||||
if (isset($trace)) {
|
||||
var_dump($obj);
|
||||
return;
|
||||
} else $obj = "{Object}";
|
||||
}
|
||||
echo(self::setColor($head . ($trace ?? "") . $obj, "yellow") . "\n");
|
||||
echo(self::setColor($head . ($trace ?? "") . $obj, in_array("--white-term", FrameworkLoader::$argv) ? "blue" : "yellow") . "\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,14 +85,14 @@ class Console
|
||||
$trace = debug_backtrace()[1] ?? ['file' => '', 'function' => ''];
|
||||
$trace = "[" . basename($trace["file"], ".php") . ":" . $trace["function"] . "] ";
|
||||
}
|
||||
if(ZMBuf::$atomics["info_level"]->get() >= 2) {
|
||||
if (ZMBuf::$atomics["info_level"]->get() >= 2) {
|
||||
if (!is_string($obj)) {
|
||||
if (isset($trace)) {
|
||||
var_dump($obj);
|
||||
return;
|
||||
} else $obj = "{Object}";
|
||||
}
|
||||
echo(self::setColor($head . ($trace ?? "") . $obj, "lightblue") . "\n");
|
||||
echo(self::setColor($head . ($trace ?? "") . $obj, in_array("--white-term", FrameworkLoader::$argv) ? "black" : "lightblue") . "\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,7 +102,7 @@ class Console
|
||||
$trace = debug_backtrace()[1] ?? ['file' => '', 'function' => ''];
|
||||
$trace = "[" . basename($trace["file"], ".php") . ":" . $trace["function"] . "] ";
|
||||
}
|
||||
if(ZMBuf::$atomics["info_level"]->get() >= 2) {
|
||||
if (ZMBuf::$atomics["info_level"]->get() >= 2) {
|
||||
if (!is_string($obj)) {
|
||||
if (isset($trace)) {
|
||||
var_dump($obj);
|
||||
@@ -107,8 +114,8 @@ class Console
|
||||
}
|
||||
|
||||
static function verbose($obj, $head = null) {
|
||||
if($head === null) $head = date("[H:i:s] ") . "[V] ";
|
||||
if(ZMBuf::$atomics["info_level"]->get() >= 3) {
|
||||
if ($head === null) $head = date("[H:i:s] ") . "[V] ";
|
||||
if (ZMBuf::$atomics["info_level"]->get() >= 3) {
|
||||
if (!is_string($obj)) {
|
||||
if (isset($trace)) {
|
||||
var_dump($obj);
|
||||
@@ -119,8 +126,8 @@ class Console
|
||||
}
|
||||
}
|
||||
|
||||
static function debug($obj) {
|
||||
debug($obj);
|
||||
static function debug($msg) {
|
||||
if (ZMBuf::$atomics["info_level"]->get() >= 4) Console::log(date("[H:i:s] ") . "[D] " . $msg, 'gray');
|
||||
}
|
||||
|
||||
static function log($obj, $color = "") {
|
||||
@@ -153,15 +160,57 @@ class Console
|
||||
}
|
||||
|
||||
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.");
|
||||
return;
|
||||
}
|
||||
go(function () {
|
||||
while (true) {
|
||||
$cmd = trim(co::fread(STDIN));
|
||||
if (self::executeCommand($cmd) === false) break;
|
||||
global $terminal_id;
|
||||
global $port;
|
||||
$port = ZMBuf::globals("port");
|
||||
$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);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -173,7 +222,7 @@ class Console
|
||||
$it = explodeMsg($cmd);
|
||||
switch ($it[0] ?? '') {
|
||||
case 'logtest':
|
||||
Console::log(date("[H:i:s]"). " [L] This is normal msg. (0)");
|
||||
Console::log(date("[H:i:s]") . " [L] This is normal msg. (0)");
|
||||
Console::error("This is error msg. (0)");
|
||||
Console::warning("This is warning msg. (1)");
|
||||
Console::info("This is info msg. (2)");
|
||||
@@ -207,10 +256,16 @@ class Console
|
||||
case 'r':
|
||||
ZMUtil::reload();
|
||||
return false;
|
||||
case 'save':
|
||||
$origin = ZMBuf::$atomics["info_level"]->get();
|
||||
//ZMBuf::$atomics["info_level"]->set(3);
|
||||
DataProvider::saveBuffer();
|
||||
//ZMBuf::$atomics["info_level"]->set($origin);
|
||||
return true;
|
||||
case '':
|
||||
return true;
|
||||
default:
|
||||
Console::info("Command not found: " . $it[0]);
|
||||
Console::info("Command not found: " . $cmd);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
78
src/Framework/DataProvider.php
Normal file
78
src/Framework/DataProvider.php
Normal file
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Framework;
|
||||
|
||||
|
||||
use ZM\Annotation\Swoole\OnSave;
|
||||
|
||||
class DataProvider
|
||||
{
|
||||
public static $buffer_list = [];
|
||||
|
||||
public static function getResourceFolder() {
|
||||
return self::getWorkingDir() . '/resources/';
|
||||
}
|
||||
|
||||
public static function getWorkingDir() {
|
||||
if(LOAD_MODE == 0) return WORKING_DIR;
|
||||
elseif (LOAD_MODE == 1) return LOAD_MODE_COMPOSER_PATH;
|
||||
elseif (LOAD_MODE == 2) return realpath('.');
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function getDataConfig() {
|
||||
return CONFIG_DIR;
|
||||
}
|
||||
|
||||
public static function addSaveBuffer($buf_name, $sub_folder = null) {
|
||||
$name = ($sub_folder ?? "") . "/" . $buf_name . ".json";
|
||||
self::$buffer_list[$buf_name] = $name;
|
||||
Console::debug("Added " . $buf_name . " at $sub_folder");
|
||||
ZMBuf::set($buf_name, self::getJsonData($name));
|
||||
}
|
||||
|
||||
public static function saveBuffer() {
|
||||
$head = Console::setColor(date("[H:i:s] ") . "[V] Saving buffer......", "blue");
|
||||
if (ZMBuf::$atomics["info_level"]->get() >= 3)
|
||||
echo $head;
|
||||
foreach (self::$buffer_list as $k => $v) {
|
||||
Console::debug("Saving " . $k . " to " . $v);
|
||||
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)
|
||||
echo Console::setColor("saved", "blue") . PHP_EOL;
|
||||
}
|
||||
|
||||
public static function getFrameworkLink() {
|
||||
return ZMBuf::globals("http_reverse_link");
|
||||
}
|
||||
|
||||
public static function getJsonData(string $string) {
|
||||
if (!file_exists(self::getDataConfig() . $string)) return [];
|
||||
return json_decode(file_get_contents(self::getDataConfig() . $string), true);
|
||||
}
|
||||
|
||||
public static function setJsonData($filename, array $args) {
|
||||
$pathinfo = pathinfo($filename);
|
||||
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));
|
||||
if ($r === false) {
|
||||
Console::warning("无法保存文件: " . $filename);
|
||||
}
|
||||
}
|
||||
|
||||
public static function getDataFolder() {
|
||||
return ZM_DATA;
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
namespace Framework;
|
||||
|
||||
use Co;
|
||||
use Doctrine\Common\Annotations\AnnotationException;
|
||||
use Swoole\Http\Request;
|
||||
use Swoole\Runtime;
|
||||
use Swoole\WebSocket\Frame;
|
||||
@@ -35,17 +36,35 @@ class FrameworkLoader
|
||||
/** @var 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;
|
||||
|
||||
chdir(__DIR__ . '/../..');
|
||||
define('WORKING_DIR', getcwd());
|
||||
Runtime::enableCoroutine();
|
||||
|
||||
$this->requireGlobalFunctions();
|
||||
$this->registerAutoloader('classLoader');
|
||||
if (LOAD_MODE == 0) define("WORKING_DIR", getcwd());
|
||||
elseif (LOAD_MODE == 1) define("WORKING_DIR", realpath(__DIR__ . "/../../"));
|
||||
elseif (LOAD_MODE == 2) echo "Phar mode: " . WORKING_DIR . PHP_EOL;
|
||||
//$this->registerAutoloader('classLoader');
|
||||
require_once "DataProvider.php";
|
||||
if (file_exists(DataProvider::getWorkingDir() . "/vendor/autoload.php")) {
|
||||
require_once DataProvider::getWorkingDir() . "/vendor/autoload.php";
|
||||
}
|
||||
if (LOAD_MODE == 2) {
|
||||
require_once FRAMEWORK_DIR . "/vendor/autoload.php";
|
||||
spl_autoload_register('phar_classloader');
|
||||
}
|
||||
|
||||
|
||||
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();
|
||||
ZMBuf::$globals = self::$settings;
|
||||
if (!self::$settings->success) die("Failed to load global config. Please check config/global.php file");
|
||||
@@ -55,7 +74,6 @@ class FrameworkLoader
|
||||
$this->selfCheck();
|
||||
try {
|
||||
$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");
|
||||
if (in_array("--daemon", $args)) {
|
||||
$settings["daemonize"] = 1;
|
||||
@@ -65,7 +83,7 @@ class FrameworkLoader
|
||||
$this->server->set($settings);
|
||||
$this->server->on("WorkerStart", [$this, "onWorkerStart"]);
|
||||
$this->server->on("message", function ($server, Frame $frame) {
|
||||
Console::debug("Calling Swoole \"message\" event from fd=" . $frame->fd);
|
||||
Console::debug("Calling Swoole \"message\" from fd=" . $frame->fd);
|
||||
EventHandler::callSwooleEvent("message", $server, $frame);
|
||||
});
|
||||
$this->server->on("request", function ($request, $response) {
|
||||
@@ -82,6 +100,7 @@ class FrameworkLoader
|
||||
EventHandler::callSwooleEvent("close", $server, $fd);
|
||||
});
|
||||
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-warning", $args)) ZMBuf::$atomics["info_level"]->set(1);
|
||||
if (in_array("--log-info", $args)) ZMBuf::$atomics["info_level"]->set(2);
|
||||
@@ -91,10 +110,17 @@ class FrameworkLoader
|
||||
"host: " . self::$settings->get("host") .
|
||||
", port: " . self::$settings->get("port") .
|
||||
", log_level: " . ZMBuf::$atomics["info_level"]->get() .
|
||||
", version: " . json_decode(file_get_contents(WORKING_DIR . "/composer.json"), true)["version"]
|
||||
", version: " . ZM_VERSION .
|
||||
"\nworking_dir: " . DataProvider::getWorkingDir()
|
||||
);
|
||||
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();
|
||||
} catch (Exception $e) {
|
||||
Console::error("Framework初始化出现错误,请检查!");
|
||||
@@ -103,20 +129,18 @@ class FrameworkLoader
|
||||
}
|
||||
}
|
||||
|
||||
private function requireGlobalFunctions()
|
||||
{
|
||||
require __DIR__ . '/global_functions.php';
|
||||
private function requireGlobalFunctions() {
|
||||
require_once __DIR__ . '/global_functions.php';
|
||||
}
|
||||
|
||||
private function registerAutoloader(string $string)
|
||||
{
|
||||
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_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("CRASH_DIR", self::$settings->get("crash_dir"));
|
||||
@mkdir(ZM_DATA);
|
||||
@@ -126,13 +150,17 @@ class FrameworkLoader
|
||||
define("ZM_MATCH_FIRST", 1);
|
||||
define("ZM_MATCH_NUMBER", 2);
|
||||
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 (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("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 (substr(PHP_VERSION, 0, 1) != "7") die("PHP >=7 required.\n");
|
||||
//if (!function_exists("curl_exec")) die("Can not find curl extension.\n");
|
||||
@@ -141,8 +169,12 @@ class FrameworkLoader
|
||||
return true;
|
||||
}
|
||||
|
||||
public function onWorkerStart(\Swoole\Server $server, $worker_id)
|
||||
{
|
||||
/**
|
||||
* @param \Swoole\Server $server
|
||||
* @param $worker_id
|
||||
* @throws AnnotationException
|
||||
*/
|
||||
public function onWorkerStart(\Swoole\Server $server, $worker_id) {
|
||||
self::$instance = $this;
|
||||
self::$run_time = microtime(true);
|
||||
EventHandler::callSwooleEvent("WorkerStart", $server, $worker_id);
|
||||
@@ -157,5 +189,6 @@ $motd = <<<EOL
|
||||
/ /_| | | | (_| | | | | | | (_| | (_) |
|
||||
/____|_| |_|\__,_|_| |_| |_|\__,_|\___/
|
||||
|
||||
|
||||
EOL;
|
||||
|
||||
|
||||
@@ -18,7 +18,8 @@ class GlobalConfig
|
||||
public $success = false;
|
||||
|
||||
public function __construct() {
|
||||
include_once WORKING_DIR . '/config/global.php';
|
||||
/** @noinspection PhpIncludeInspection */
|
||||
include_once DataProvider::getWorkingDir() . '/config/global.php';
|
||||
global $config;
|
||||
$this->success = true;
|
||||
$this->config = $config;
|
||||
|
||||
@@ -9,9 +9,9 @@
|
||||
namespace Framework;
|
||||
|
||||
use Swoole\Atomic;
|
||||
use Swoole\Database\PDOPool;
|
||||
use swoole_atomic;
|
||||
use ZM\connection\WSConnection;
|
||||
use ZM\Utils\SQLPool;
|
||||
|
||||
class ZMBuf
|
||||
{
|
||||
@@ -24,7 +24,7 @@ class ZMBuf
|
||||
static $scheduler = null; //This is stupid warning...
|
||||
|
||||
//Swoole SQL连接池,多进程下每个进程一个连接池
|
||||
/** @var SQLPool */
|
||||
/** @var PDOPool */
|
||||
static $sql_pool = null;//保存sql连接池的类
|
||||
|
||||
//只读的数据,可以在多worker_num下使用
|
||||
@@ -51,6 +51,7 @@ class ZMBuf
|
||||
public static $config = [];
|
||||
public static $context = [];
|
||||
public static $instance = [];
|
||||
public static $context_class = [];
|
||||
|
||||
static function get($name, $default = null) {
|
||||
return self::$cache[$name] ?? $default;
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
<?php
|
||||
|
||||
use Framework\Console;
|
||||
use Framework\DataProvider;
|
||||
use Framework\ZMBuf;
|
||||
use Swoole\Coroutine\System;
|
||||
use ZM\Context\ContextInterface;
|
||||
use ZM\Utils\ZMUtil;
|
||||
|
||||
function classLoader($p) {
|
||||
|
||||
function phar_classloader($p){
|
||||
$filepath = getClassPath($p);
|
||||
if ($filepath === null)
|
||||
echo "F:Warning: get class path wrongs.$p\n";
|
||||
//else echo "F:DBG: Found " . $p . "\n";
|
||||
if($filepath === null) {
|
||||
Console::debug("F:Warning: get class path wrongs.$p");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
require_once $filepath;
|
||||
} catch (Exception $e) {
|
||||
@@ -19,11 +24,16 @@ function classLoader($p) {
|
||||
|
||||
function getClassPath($class_name) {
|
||||
$dir = str_replace("\\", "/", $class_name);
|
||||
$dir = WORKING_DIR . "/src/" . $dir . ".php";
|
||||
//echo "@@@".$dir.PHP_EOL;
|
||||
$dir = str_replace("\\", "/", $dir);
|
||||
if (file_exists($dir)) return $dir;
|
||||
else return null;
|
||||
$dir2 = WORKING_DIR . "/src/" . $dir . ".php";
|
||||
//echo "@@@".$dir2.PHP_EOL;
|
||||
$dir2 = str_replace("\\", "/", $dir2);
|
||||
if (file_exists($dir2)) return $dir2;
|
||||
else {
|
||||
$dir = DataProvider::getWorkingDir() . "/src/" . $dir . ".php";
|
||||
//echo "###".$dir.PHP_EOL;
|
||||
if (file_exists($dir)) return $dir;
|
||||
else return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -60,6 +70,7 @@ function unicode_decode($str) {
|
||||
* @return array
|
||||
*/
|
||||
function getAllClasses($dir, $indoor_name) {
|
||||
if(!is_dir($dir)) return [];
|
||||
$list = scandir($dir);
|
||||
$classes = [];
|
||||
unset($list[0], $list[1]);
|
||||
@@ -152,9 +163,10 @@ function matchArgs($pattern, $context) {
|
||||
function set_coroutine_params($array) {
|
||||
$cid = Co::getCid();
|
||||
if ($cid == -1) die("Cannot set coroutine params at none coroutine mode.");
|
||||
ZMBuf::$context[$cid] = $array;
|
||||
if (isset(ZMBuf::$context[$cid])) ZMBuf::$context[$cid] = array_merge(ZMBuf::$context[$cid], $array);
|
||||
else ZMBuf::$context[$cid] = $array;
|
||||
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]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,20 +174,54 @@ function set_coroutine_params($array) {
|
||||
* @return ContextInterface|null
|
||||
*/
|
||||
function context() {
|
||||
return ctx();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ContextInterface|null
|
||||
*/
|
||||
function ctx() {
|
||||
$cid = Co::getCid();
|
||||
$c_class = ZMBuf::globals("context_class");
|
||||
if (isset(ZMBuf::$context[$cid])) {
|
||||
return new $c_class(ZMBuf::$context[$cid], $cid);
|
||||
return ZMBuf::$context_class[$cid] ?? (ZMBuf::$context_class[$cid] = new $c_class($cid));
|
||||
} else {
|
||||
Console::debug("未找到当前协程的上下文($cid),正在找父进程的上下文");
|
||||
while (($pcid = Co::getPcid($cid)) !== -1) {
|
||||
if (isset(ZMBuf::$context[$cid])) return new $c_class(ZMBuf::$context[$cid], $cid);
|
||||
else $cid = $pcid;
|
||||
$cid = $pcid;
|
||||
if (isset(ZMBuf::$context[$cid])) return ZMBuf::$context_class[$cid] ?? (ZMBuf::$context_class[$cid] = new $c_class($cid));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function debug($msg) {
|
||||
if (ZMBuf::$atomics["info_level"]->get() >= 4)
|
||||
Console::log(date("[H:i:s] ") . "[D] " . $msg, 'gray');
|
||||
function debug($msg) { Console::debug($msg); }
|
||||
|
||||
function zm_sleep($s = 1) { Co::sleep($s); }
|
||||
|
||||
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,46 +10,90 @@ use ZM\Annotation\Http\Middleware;
|
||||
use ZM\Annotation\Http\RequestMapping;
|
||||
use ZM\Annotation\Swoole\SwooleEventAt;
|
||||
use ZM\Connection\CQConnection;
|
||||
use ZM\ModBase;
|
||||
use ZM\Utils\ZMUtil;
|
||||
|
||||
/**
|
||||
* Class Hello
|
||||
* @package Module\Example
|
||||
* @since 1.0
|
||||
*/
|
||||
class Hello extends ModBase
|
||||
class Hello
|
||||
{
|
||||
/**
|
||||
* 在机器人连接后向终端输出信息
|
||||
* @SwooleEventAt("open",rule="connectType:qq")
|
||||
* @param $conn
|
||||
*/
|
||||
public function onConnect(CQConnection $conn){
|
||||
Console::info("机器人 ".$conn->getQQ()." 已连接!");
|
||||
public function onConnect(CQConnection $conn) {
|
||||
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 "你好啊,我是由炸毛框架构建的机器人!";
|
||||
}
|
||||
|
||||
/**
|
||||
* @CQCommand(".reload")
|
||||
*/
|
||||
public function reload() {
|
||||
context()->reply("reloading...");
|
||||
ZMUtil::reload();
|
||||
}
|
||||
|
||||
/**
|
||||
* @CQCommand("随机数")
|
||||
* @CQCommand(regexMatch="*从*到*的随机数")
|
||||
* @param $arg
|
||||
*/
|
||||
public function randNum($arg) {
|
||||
// 获取第一个数字类型的参数
|
||||
$num1 = context()->getArgs($arg, ZM_MATCH_NUMBER, "请输入第一个数字");
|
||||
// 获取第二个数字类型的参数
|
||||
$num2 = context()->getArgs($arg, ZM_MATCH_NUMBER, "请输入第二个数字");
|
||||
$a = min(intval($num1), intval($num2));
|
||||
$b = max(intval($num1), intval($num2));
|
||||
// 回复用户结果
|
||||
context()->reply("随机数是:".mt_rand($a, $b));
|
||||
}
|
||||
|
||||
/**
|
||||
* 中间件测试的一个示例函数
|
||||
* @RequestMapping("/httpTimer")
|
||||
* @Middleware("timer")
|
||||
*/
|
||||
public function timer(){
|
||||
public function timer() {
|
||||
return "This page is used as testing TimerMiddleware! Do not use it in production.";
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认示例页面
|
||||
* @RequestMapping("/index")
|
||||
* @RequestMapping("/")
|
||||
*/
|
||||
public function index() {
|
||||
return "Hello Zhamao!";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 框架会默认关闭未知的WebSocket链接,因为这个绑定的事件,你可以根据你自己的需求进行修改
|
||||
* @SwooleEventAt(type="open",rule="connectType:unknown")
|
||||
*/
|
||||
public function closeUnknownConn(){
|
||||
public function closeUnknownConn() {
|
||||
Console::info("Unknown connection , I will close it.");
|
||||
$this->connection->close();
|
||||
context()->getConnection()->close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -211,10 +211,31 @@ class CQ
|
||||
return $msg;
|
||||
}
|
||||
|
||||
public static function encode($str) {
|
||||
return self::escape($str);
|
||||
}
|
||||
|
||||
public static function removeCQ($msg) {
|
||||
while (($cq = ZMUtil::getCQ($msg)) !== null) {
|
||||
while (($cq = self::getCQ($msg)) !== null) {
|
||||
$msg = str_replace(mb_substr($msg, $cq["start"], $cq["end"] - $cq["start"] + 1), "", $msg);
|
||||
}
|
||||
return $msg;
|
||||
}
|
||||
|
||||
public static function getCQ($msg) {
|
||||
if (($start = mb_strpos($msg, '[')) === false) return null;
|
||||
if (($end = mb_strpos($msg, ']')) === false) return null;
|
||||
$msg = mb_substr($msg, $start + 1, $end - $start - 1);
|
||||
if (mb_substr($msg, 0, 3) != "CQ:") return null;
|
||||
$msg = mb_substr($msg, 3);
|
||||
$msg2 = explode(",", $msg);
|
||||
$type = array_shift($msg2);
|
||||
$array = [];
|
||||
foreach ($msg2 as $k => $v) {
|
||||
$ss = explode("=", $v);
|
||||
$sk = array_shift($ss);
|
||||
$array[$sk] = implode("=", $ss);
|
||||
}
|
||||
return ["type" => $type, "params" => $array, "start" => $start, "end" => $end];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ use Framework\Console;
|
||||
use Framework\ZMBuf;
|
||||
use ZM\Connection\ConnectionManager;
|
||||
use ZM\Connection\CQConnection;
|
||||
use ZM\Connection\WSConnection;
|
||||
use ZM\Event\EventHandler;
|
||||
use ZM\Utils\ZMRobot;
|
||||
|
||||
/**
|
||||
@@ -198,16 +198,16 @@ class CQAPI
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WSConnection $connection
|
||||
* @param CQConnection $connection
|
||||
* @param $reply
|
||||
* @param |null $function
|
||||
* @return bool
|
||||
* @return bool|array
|
||||
*/
|
||||
public static function processAPI($connection, $reply, $function = null) {
|
||||
$api_id = ZMBuf::$atomics["wait_msg_id"]->get();
|
||||
$reply["echo"] = $api_id;
|
||||
ZMBuf::$atomics["wait_msg_id"]->add(1);
|
||||
|
||||
EventHandler::callCQAPISend($reply, $connection);
|
||||
if (is_callable($function)) {
|
||||
ZMBuf::appendKey("sent_api", $api_id, [
|
||||
"data" => $reply,
|
||||
@@ -243,7 +243,7 @@ class CQAPI
|
||||
Console::warning("CQAPI send failed, websocket push error.");
|
||||
$response = [
|
||||
"status" => "failed",
|
||||
"retcode" => 999,
|
||||
"retcode" => -1000,
|
||||
"data" => null,
|
||||
"self_id" => $connection->getQQ()
|
||||
];
|
||||
@@ -251,7 +251,7 @@ class CQAPI
|
||||
if (($s["func"] ?? null) !== null)
|
||||
call_user_func($s["func"], $response, $reply);
|
||||
ZMBuf::unsetByValue("sent_api", $reply["echo"]);
|
||||
if ($function === true) return null;
|
||||
if ($function === true) return $response;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,21 +4,34 @@
|
||||
namespace ZM\Annotation;
|
||||
|
||||
use Doctrine\Common\Annotations\{AnnotationException, AnnotationReader};
|
||||
use Co;
|
||||
use Framework\{Console, ZMBuf};
|
||||
use Error;
|
||||
use Exception;
|
||||
use ReflectionClass;
|
||||
use ReflectionException;
|
||||
use ReflectionMethod;
|
||||
use ZM\Annotation\CQ\{CQAfter, CQBefore, CQCommand, CQMessage, CQMetaEvent, CQNotice, CQRequest};
|
||||
use ZM\Annotation\CQ\{CQAfter,
|
||||
CQAPIResponse,
|
||||
CQAPISend,
|
||||
CQBefore,
|
||||
CQCommand,
|
||||
CQMessage,
|
||||
CQMetaEvent,
|
||||
CQNotice,
|
||||
CQRequest
|
||||
};
|
||||
use ZM\Annotation\Http\{After, Before, Controller, HandleException, Middleware, MiddlewareClass, RequestMapping};
|
||||
use Swoole\Timer;
|
||||
use ZM\Annotation\Interfaces\CustomAnnotation;
|
||||
use ZM\Annotation\Interfaces\Level;
|
||||
use ZM\Annotation\Module\{Closed, InitBuffer, SaveBuffer};
|
||||
use ZM\Annotation\Swoole\{OnStart, OnTick, SwooleEventAfter, SwooleEventAt};
|
||||
use ZM\Annotation\Module\{Closed, InitBuffer, LoadBuffer, SaveBuffer};
|
||||
use ZM\Annotation\Swoole\{OnSave, OnStart, OnTick, SwooleEventAfter, SwooleEventAt};
|
||||
use ZM\Annotation\Interfaces\Rule;
|
||||
use ZM\Connection\WSConnection;
|
||||
use ZM\Event\EventHandler;
|
||||
use ZM\Http\MiddlewareInterface;
|
||||
use ZM\Utils\DataProvider;
|
||||
use Framework\DataProvider;
|
||||
use ZM\Utils\ZMUtil;
|
||||
|
||||
class AnnotationParser
|
||||
@@ -30,7 +43,7 @@ class AnnotationParser
|
||||
*/
|
||||
public static function registerMods() {
|
||||
self::loadAnnotationClasses();
|
||||
$all_class = getAllClasses(WORKING_DIR . "/src/Module/", "Module");
|
||||
$all_class = getAllClasses(DataProvider::getWorkingDir() . "/src/Module/", "Module");
|
||||
ZMBuf::$req_mapping[0] = [
|
||||
'id' => 0,
|
||||
'pid' => -1,
|
||||
@@ -38,6 +51,7 @@ class AnnotationParser
|
||||
];
|
||||
$reader = new AnnotationReader();
|
||||
foreach ($all_class as $v) {
|
||||
Console::debug("正在检索 " . $v);
|
||||
$reflection_class = new ReflectionClass($v);
|
||||
$class_prefix = '';
|
||||
$methods = $reflection_class->getMethods(ReflectionMethod::IS_PUBLIC);
|
||||
@@ -47,15 +61,18 @@ class AnnotationParser
|
||||
if ($vs instanceof Closed) {
|
||||
continue 2;
|
||||
} elseif ($vs instanceof Controller) {
|
||||
Console::debug("找到 Controller 中间件: ".$vs->class);
|
||||
Console::debug("找到 Controller 中间件: " . $vs->class);
|
||||
$class_prefix = $vs->prefix;
|
||||
} 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);
|
||||
} 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) {
|
||||
ZMBuf::set($vs->buf_name, []);
|
||||
} elseif ($vs instanceof MiddlewareClass) {
|
||||
Console::verbose("正在注册中间件 " . $vs->class);
|
||||
Console::verbose("正在注册中间件 " . $reflection_class->getName());
|
||||
$result = [
|
||||
"class" => "\\" . $reflection_class->getName()
|
||||
];
|
||||
@@ -85,10 +102,15 @@ class AnnotationParser
|
||||
}
|
||||
}
|
||||
foreach ($methods as $vs) {
|
||||
if ($middleware_addon !== null) {
|
||||
Console::debug("Added middleware " . $middleware_addon->middleware . " to $v -> " . $vs->getName());
|
||||
ZMBuf::$events[MiddlewareInterface::class][$v][$vs->getName()][] = $middleware_addon->middleware;
|
||||
}
|
||||
$method_annotations = $reader->getMethodAnnotations($vs);
|
||||
foreach ($method_annotations as $vss) {
|
||||
if ($vss instanceof Rule) $vss = self::registerRuleEvent($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;
|
||||
elseif ($vss instanceof SwooleEventAfter) ZMBuf::$events[SwooleEventAfter::class][] = $vss;
|
||||
@@ -99,14 +121,15 @@ class AnnotationParser
|
||||
elseif ($vss instanceof CQCommand) ZMBuf::$events[CQCommand::class][] = $vss;
|
||||
elseif ($vss instanceof RequestMapping) {
|
||||
self::registerRequestMapping($vss, $vs, $reflection_class, $class_prefix);
|
||||
if ($middleware_addon !== null)
|
||||
ZMBuf::$events[MiddlewareInterface::class][$vss->class][$vss->method] = $middleware_addon->middleware;
|
||||
} elseif ($vss instanceof CustomAnnotation) ZMBuf::$events[get_class($vss)][] = $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 OnStart) ZMBuf::$events[OnStart::class][] = $vss;
|
||||
elseif ($vss instanceof Middleware) ZMBuf::$events[MiddlewareInterface::class][$vss->class][$vss->method] = $vss->middleware;
|
||||
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 OnTick) self::addTimerTick($vss);
|
||||
elseif ($vss instanceof CQAPISend) ZMBuf::$events[CQAPISend::class][] = $vss;
|
||||
elseif ($vss instanceof CQAPIResponse) ZMBuf::$events[CQAPIResponse::class][$vss->retcode] = [$vss->class, $vss->method];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -128,13 +151,14 @@ class AnnotationParser
|
||||
}
|
||||
}
|
||||
}
|
||||
Console::debug("解析注解完毕!");
|
||||
if (ZMBuf::isset("timer_count")) {
|
||||
Console::info("Added " . ZMBuf::get("timer_count") . " timer(s)!");
|
||||
ZMBuf::unsetCache("timer_count");
|
||||
}
|
||||
}
|
||||
|
||||
private static function getRuleCallback($rule_str) {
|
||||
public static function getRuleCallback($rule_str) {
|
||||
$func = null;
|
||||
$rule = $rule_str;
|
||||
if ($rule != "") {
|
||||
@@ -144,7 +168,8 @@ class AnnotationParser
|
||||
//Swoole 事件时走此switch
|
||||
switch ($asp_name) {
|
||||
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;
|
||||
};
|
||||
break;
|
||||
@@ -212,14 +237,14 @@ class AnnotationParser
|
||||
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->method = $method->getName();
|
||||
$vss->class = $class->getName();
|
||||
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->class = $class->getName();
|
||||
return $vss;
|
||||
@@ -299,9 +324,10 @@ class AnnotationParser
|
||||
$s = WORKING_DIR . '/src/' . str_replace("\\", "/", $v) . ".php";
|
||||
require_once $s;
|
||||
}
|
||||
$class = getAllClasses(WORKING_DIR . "/src/Custom/Annotation/", "Custom\\Annotation");
|
||||
$class = getAllClasses(DataProvider::getWorkingDir() . "/src/Custom/Annotation/", "Custom\\Annotation");
|
||||
foreach ($class as $v) {
|
||||
$s = WORKING_DIR . '/src/' . str_replace("\\", "/", $v) . ".php";
|
||||
$s = DataProvider::getWorkingDir() . '/src/' . str_replace("\\", "/", $v) . ".php";
|
||||
Console::debug("Requiring custom annotation ".$s);
|
||||
require_once $s;
|
||||
}
|
||||
}
|
||||
@@ -318,6 +344,42 @@ class AnnotationParser
|
||||
|
||||
private static function addTimerTick(?OnTick $vss) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
24
src/ZM/Annotation/CQ/CQAPIResponse.php
Normal file
24
src/ZM/Annotation/CQ/CQAPIResponse.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Annotation\CQ;
|
||||
|
||||
|
||||
use Doctrine\Common\Annotations\Annotation\Required;
|
||||
use Doctrine\Common\Annotations\Annotation\Target;
|
||||
use ZM\Annotation\AnnotationBase;
|
||||
|
||||
/**
|
||||
* Class CQAPIResponse
|
||||
* @package ZM\Annotation\CQ
|
||||
* @Annotation
|
||||
* @Target("METHOD")
|
||||
*/
|
||||
class CQAPIResponse extends AnnotationBase
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
* @Required()
|
||||
*/
|
||||
public $retcode;
|
||||
}
|
||||
43
src/ZM/Annotation/CQ/CQAPISend.php
Normal file
43
src/ZM/Annotation/CQ/CQAPISend.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Annotation\CQ;
|
||||
|
||||
use Doctrine\Common\Annotations\Annotation\Target;
|
||||
use ZM\Annotation\AnnotationBase;
|
||||
use ZM\Annotation\Interfaces\Level;
|
||||
|
||||
/**
|
||||
* Class CQAPISend
|
||||
* @package ZM\Annotation\CQ
|
||||
* @Annotation
|
||||
* @Target("METHOD")
|
||||
*/
|
||||
class CQAPISend extends AnnotationBase implements Level
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $action = "";
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $with_result = false;
|
||||
|
||||
public $level = 20;
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getLevel() {
|
||||
return $this->level;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $level
|
||||
*/
|
||||
public function setLevel($level) {
|
||||
$this->level = $level;
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ namespace ZM\Annotation\CQ;
|
||||
|
||||
use Doctrine\Common\Annotations\Annotation\Target;
|
||||
use ZM\Annotation\AnnotationBase;
|
||||
use ZM\Annotation\Interfaces\Level;
|
||||
|
||||
/**
|
||||
* Class CQAfter
|
||||
@@ -12,11 +13,27 @@ use ZM\Annotation\AnnotationBase;
|
||||
* @Target("METHOD")
|
||||
* @package ZM\Annotation\CQ
|
||||
*/
|
||||
class CQAfter extends AnnotationBase
|
||||
class CQAfter extends AnnotationBase implements Level
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
* @Required()
|
||||
*/
|
||||
public $cq_event;
|
||||
}
|
||||
|
||||
public $level = 20;
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getLevel() {
|
||||
return $this->level;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $level
|
||||
*/
|
||||
public function setLevel($level) {
|
||||
$this->level = $level;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,16 @@ class CQCommand extends AnnotationBase implements Level
|
||||
public $match = "";
|
||||
/** @var string */
|
||||
public $regexMatch = "";
|
||||
/** @var string[] */
|
||||
public $alias = [];
|
||||
/** @var string */
|
||||
public $message_type = "";
|
||||
/** @var int */
|
||||
public $user_id = 0;
|
||||
/** @var int */
|
||||
public $group_id = 0;
|
||||
/** @var int */
|
||||
public $discuss_id = 0;
|
||||
/** @var int */
|
||||
public $level = 20;
|
||||
|
||||
@@ -32,4 +42,4 @@ class CQCommand extends AnnotationBase implements 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
|
||||
*@Required()
|
||||
* @Required()
|
||||
*/
|
||||
public $buf_name;
|
||||
|
||||
/** @var string $sub_folder */
|
||||
public $sub_folder = null;
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
}
|
||||
@@ -20,4 +20,4 @@ class CQConnection extends WSConnection
|
||||
public function getType() {
|
||||
return "qq";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace ZM\Connection;
|
||||
|
||||
|
||||
use Framework\ZMBuf;
|
||||
use Framework\DataProvider;
|
||||
|
||||
class ConnectionManager
|
||||
{
|
||||
@@ -46,6 +47,8 @@ class ConnectionManager
|
||||
return WCConnection::class;
|
||||
case "proxy":
|
||||
return ProxyConnection::class;
|
||||
case "terminal":
|
||||
return TerminalConnection::class;
|
||||
default:
|
||||
foreach (ZMBuf::$custom_connection_class as $v) {
|
||||
/** @var WSConnection $r */
|
||||
@@ -67,7 +70,7 @@ class ConnectionManager
|
||||
}
|
||||
|
||||
public static function registerCustomClass() {
|
||||
$classes = getAllClasses(WORKING_DIR . "/src/Custom/Connection/", "Custom\\Connection");
|
||||
$classes = getAllClasses(DataProvider::getWorkingDir(). "/src/Custom/Connection/", "Custom\\Connection");
|
||||
ZMBuf::$custom_connection_class = $classes;
|
||||
}
|
||||
}
|
||||
|
||||
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";
|
||||
}
|
||||
}
|
||||
@@ -4,73 +4,62 @@
|
||||
namespace ZM\Context;
|
||||
|
||||
|
||||
use Co;
|
||||
use Framework\ZMBuf;
|
||||
use Swoole\Http\Request;
|
||||
use Swoole\WebSocket\Frame;
|
||||
use swoole_server;
|
||||
use ZM\API\CQAPI;
|
||||
use ZM\Connection\ConnectionManager;
|
||||
use ZM\Connection\CQConnection;
|
||||
use ZM\Connection\WSConnection;
|
||||
use ZM\Exception\InvalidArgumentException;
|
||||
use ZM\Exception\WaitTimeoutException;
|
||||
use ZM\Http\Response;
|
||||
use ZM\Utils\ZMRobot;
|
||||
|
||||
class Context implements ContextInterface
|
||||
{
|
||||
private $server = null;
|
||||
private $frame = null;
|
||||
private $data = null;
|
||||
private $request = null;
|
||||
private $response = null;
|
||||
private $cid;
|
||||
|
||||
public function __construct($param, $cid) {
|
||||
if (isset($param["server"])) $this->server = $param["server"];
|
||||
if (isset($param["frame"])) $this->frame = $param["frame"];
|
||||
if (isset($param["data"])) $this->data = $param["data"];
|
||||
if (isset($param["request"])) $this->request = $param["request"];
|
||||
if (isset($param["response"])) $this->response = $param["response"];
|
||||
$this->cid = $cid;
|
||||
}
|
||||
public function __construct($cid) { $this->cid = $cid; }
|
||||
|
||||
/**
|
||||
* @return swoole_server|null
|
||||
*/
|
||||
public function getServer() {
|
||||
return $this->server;
|
||||
}
|
||||
public function getServer() { return ZMBuf::$context[$this->cid]["server"] ?? null; }
|
||||
|
||||
/**
|
||||
* @return Frame|null
|
||||
*/
|
||||
public function getFrame() {
|
||||
return $this->frame;
|
||||
}
|
||||
public function getFrame() { return ZMBuf::$context[$this->cid]["frame"] ?? null; }
|
||||
|
||||
public function getFd() { return ZMBuf::$context[$this->cid]["fd"] ?? $this->getFrame()->fd ?? null; }
|
||||
|
||||
/**
|
||||
* @return array|null
|
||||
*/
|
||||
public function getData() {
|
||||
return $this->data;
|
||||
}
|
||||
public function getData() { return ZMBuf::$context[$this->cid]["data"] ?? null; }
|
||||
|
||||
public function setData($data) { ZMBuf::$context[$this->cid]["data"] = $data; }
|
||||
|
||||
/**
|
||||
* @return Request|null
|
||||
*/
|
||||
public function getRequest() {
|
||||
return $this->request;
|
||||
}
|
||||
public function getRequest() { return ZMBuf::$context[$this->cid]["request"] ?? null; }
|
||||
|
||||
/**
|
||||
* @return Response|null
|
||||
*/
|
||||
public function getResponse() {
|
||||
return $this->response;
|
||||
}
|
||||
public function getResponse() { return ZMBuf::$context[$this->cid]["response"] ?? null; }
|
||||
|
||||
/** @return WSConnection */
|
||||
public function getConnection() { return ConnectionManager::get($this->getFd()); }
|
||||
|
||||
/**
|
||||
* @return int|null
|
||||
*/
|
||||
public function getCid() {
|
||||
return $this->cid;
|
||||
}
|
||||
public function getCid() { return $this->cid; }
|
||||
|
||||
/**
|
||||
* @return ZMRobot|null
|
||||
@@ -79,4 +68,139 @@ class Context implements ContextInterface
|
||||
$conn = ConnectionManager::get($this->getFrame()->fd);
|
||||
return $conn instanceof CQConnection ? new ZMRobot($conn) : null;
|
||||
}
|
||||
|
||||
public function getMessage() { return ZMBuf::$context[$this->cid]["data"]["message"] ?? null; }
|
||||
|
||||
public function setMessage($msg) { ZMBuf::$context[$this->cid]["data"]["message"] = $msg; }
|
||||
|
||||
public function getUserId() { return $this->getData()["user_id"] ?? null; }
|
||||
|
||||
public function setUserId($id) { ZMBuf::$context[$this->cid]["data"]["user_id"] = $id; }
|
||||
|
||||
public function getGroupId() { return $this->getData()["group_id"] ?? null; }
|
||||
|
||||
public function setGroupId($id) { ZMBuf::$context[$this->cid]["data"]["group_id"] = $id; }
|
||||
|
||||
public function getDiscussId() { return $this->getData()["discuss_id"] ?? null; }
|
||||
|
||||
public function setDiscussId($id) { ZMBuf::$context[$this->cid]["data"]["discuss_id"] = $id; }
|
||||
|
||||
public function getMessageType() { return $this->getData()["message_type"] ?? null; }
|
||||
|
||||
public function setMessageType($type) { ZMBuf::$context[$this->cid]["data"]["message_type"] = $type; }
|
||||
|
||||
public function getRobotId() { return $this->getData()["self_id"] ?? null; }
|
||||
|
||||
public function getCache($key) { return ZMBuf::$context[$this->cid]["cache"][$key] ?? null; }
|
||||
|
||||
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
|
||||
* @param $msg
|
||||
* @param bool $yield
|
||||
* @return mixed
|
||||
*/
|
||||
public function reply($msg, $yield = false) {
|
||||
switch ($this->getData()["message_type"]) {
|
||||
case "group":
|
||||
case "private":
|
||||
case "discuss":
|
||||
$this->setCache("has_reply", true);
|
||||
return CQAPI::quick_reply(ConnectionManager::get($this->getFrame()->fd), $this->getData(), $msg, $yield);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function finalReply($msg, $yield = false) {
|
||||
ZMBuf::$context[$this->cid]["cache"]["block_continue"] = true;
|
||||
if ($msg == "") return true;
|
||||
return $this->reply($msg, $yield);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $prompt
|
||||
* @param int $timeout
|
||||
* @param string $timeout_prompt
|
||||
* @return string
|
||||
* @throws InvalidArgumentException
|
||||
* @throws WaitTimeoutException
|
||||
*/
|
||||
public function waitMessage($prompt = "", $timeout = 600, $timeout_prompt = "") {
|
||||
if ($prompt != "") $this->reply($prompt);
|
||||
if (!isset($this->getData()["user_id"], $this->getData()["message"], $this->getData()["self_id"]))
|
||||
throw new InvalidArgumentException("协程等待参数缺失");
|
||||
$cid = Co::getuid();
|
||||
$api_id = ZMBuf::$atomics["wait_msg_id"]->get();
|
||||
ZMBuf::$atomics["wait_msg_id"]->add(1);
|
||||
$hang = [
|
||||
"coroutine" => $cid,
|
||||
"user_id" => $this->getData()["user_id"],
|
||||
"message" => $this->getData()["message"],
|
||||
"self_id" => $this->getData()["self_id"],
|
||||
"message_type" => $this->getData()["message_type"],
|
||||
"result" => null
|
||||
];
|
||||
if ($hang["message_type"] == "group" || $hang["message_type"] == "discuss") {
|
||||
$hang[$hang["message_type"] . "_id"] = $this->getData()[$this->getData()["message_type"] . "_id"];
|
||||
}
|
||||
ZMBuf::appendKey("wait_api", $api_id, $hang);
|
||||
$id = swoole_timer_after($timeout * 1000, function () use ($api_id, $timeout_prompt) {
|
||||
$r = ZMBuf::get("wait_api")[$api_id] ?? null;
|
||||
if ($r !== null) {
|
||||
Co::resume($r["coroutine"]);
|
||||
}
|
||||
});
|
||||
|
||||
Co::suspend();
|
||||
$sess = ZMBuf::get("wait_api")[$api_id];
|
||||
ZMBuf::unsetByValue("wait_api", $api_id);
|
||||
$result = $sess["result"];
|
||||
if (isset($id)) swoole_timer_clear($id);
|
||||
if ($result === null) throw new WaitTimeoutException($this, $timeout_prompt);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $arg
|
||||
* @param $mode
|
||||
* @param $prompt_msg
|
||||
* @return mixed|string
|
||||
* @throws InvalidArgumentException
|
||||
* @throws WaitTimeoutException
|
||||
*/
|
||||
public function getArgs(&$arg, $mode, $prompt_msg) {
|
||||
switch ($mode) {
|
||||
case ZM_MATCH_ALL:
|
||||
$p = $arg;
|
||||
array_shift($p);
|
||||
return trim(implode(" ", $p)) == "" ? $this->waitMessage($prompt_msg) : trim(implode(" ", $p));
|
||||
case ZM_MATCH_NUMBER:
|
||||
foreach ($arg as $k => $v) {
|
||||
if (is_numeric($v)) {
|
||||
array_splice($arg, $k, 1);
|
||||
return $v;
|
||||
}
|
||||
}
|
||||
return $this->waitMessage($prompt_msg);
|
||||
case ZM_MATCH_FIRST:
|
||||
if (isset($arg[1])) {
|
||||
$a = $arg[1];
|
||||
array_splice($arg, 1, 1);
|
||||
return $a;
|
||||
} else {
|
||||
return $this->waitMessage($prompt_msg);
|
||||
}
|
||||
}
|
||||
throw new InvalidArgumentException();
|
||||
}
|
||||
|
||||
public function cloneFromParent() {
|
||||
set_coroutine_params(ZMBuf::$context[Co::getPcid()] ?? ZMBuf::$context[$this->cid]);
|
||||
return context();
|
||||
}
|
||||
|
||||
public function copy() { return ZMBuf::$context[$this->cid]; }
|
||||
}
|
||||
|
||||
@@ -7,12 +7,13 @@ namespace ZM\Context;
|
||||
use Swoole\Http\Request;
|
||||
use Swoole\WebSocket\Frame;
|
||||
use Swoole\WebSocket\Server;
|
||||
use ZM\Connection\WSConnection;
|
||||
use ZM\Http\Response;
|
||||
use ZM\Utils\ZMRobot;
|
||||
|
||||
interface ContextInterface
|
||||
{
|
||||
public function __construct($param, $cid);
|
||||
public function __construct($cid);
|
||||
|
||||
/** @return Server */
|
||||
public function getServer();
|
||||
@@ -23,6 +24,14 @@ interface ContextInterface
|
||||
/** @return mixed */
|
||||
public function getData();
|
||||
|
||||
public function setData($data);
|
||||
|
||||
/** @return WSConnection */
|
||||
public function getConnection();
|
||||
|
||||
/** @return int|null */
|
||||
public function getFd();
|
||||
|
||||
/** @return int */
|
||||
public function getCid();
|
||||
|
||||
@@ -34,4 +43,76 @@ interface ContextInterface
|
||||
|
||||
/** @return ZMRobot */
|
||||
public function getRobot();
|
||||
|
||||
/** @return mixed */
|
||||
public function getUserId();
|
||||
|
||||
/** @return mixed */
|
||||
public function getGroupId();
|
||||
|
||||
/** @return mixed */
|
||||
public function getDiscussId();
|
||||
|
||||
/** @return string */
|
||||
public function getMessageType();
|
||||
|
||||
/** @return mixed */
|
||||
public function getRobotId();
|
||||
|
||||
/** @return mixed */
|
||||
public function getMessage();
|
||||
|
||||
public function setMessage($msg);
|
||||
|
||||
public function setUserId($id);
|
||||
|
||||
public function setGroupId($id);
|
||||
|
||||
public function setDiscussId($id);
|
||||
|
||||
public function setMessageType($type);
|
||||
|
||||
public function getCQResponse();
|
||||
|
||||
/**
|
||||
* @param $msg
|
||||
* @param bool $yield
|
||||
* @return mixed
|
||||
*/
|
||||
public function reply($msg, $yield = false);
|
||||
|
||||
/**
|
||||
* @param $msg
|
||||
* @param bool $yield
|
||||
* @return mixed
|
||||
*/
|
||||
public function finalReply($msg, $yield = false);
|
||||
|
||||
/**
|
||||
* @param string $prompt
|
||||
* @param int $timeout
|
||||
* @param string $timeout_prompt
|
||||
* @return mixed
|
||||
*/
|
||||
public function waitMessage($prompt = "", $timeout = 600, $timeout_prompt = "");
|
||||
|
||||
/**
|
||||
* @param $arg
|
||||
* @param $mode
|
||||
* @param $prompt_msg
|
||||
* @return mixed
|
||||
*/
|
||||
public function getArgs(&$arg, $mode, $prompt_msg);
|
||||
|
||||
public function setCache($key, $value);
|
||||
|
||||
/**
|
||||
* @param $key
|
||||
* @return mixed
|
||||
*/
|
||||
public function getCache($key);
|
||||
|
||||
public function cloneFromParent();
|
||||
|
||||
public function copy();
|
||||
}
|
||||
|
||||
@@ -4,10 +4,13 @@
|
||||
namespace ZM\DB;
|
||||
|
||||
|
||||
use Exception;
|
||||
use framework\Console;
|
||||
use framework\ZMBuf;
|
||||
use PDOException;
|
||||
use PDOStatement;
|
||||
use Swoole\Coroutine;
|
||||
use Swoole\Coroutine\MySQL\Statement;
|
||||
use Swoole\Database\PDOStatementProxy;
|
||||
use ZM\Exception\DbException;
|
||||
|
||||
class DB
|
||||
@@ -16,8 +19,10 @@ class DB
|
||||
|
||||
/**
|
||||
* @throws DbException
|
||||
* @throws Exception
|
||||
*/
|
||||
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"] . "';", []);
|
||||
foreach ($result as $v) {
|
||||
self::$table_list[] = $v['TABLE_NAME'];
|
||||
@@ -63,9 +68,10 @@ class DB
|
||||
try {
|
||||
$conn = ZMBuf::$sql_pool->get();
|
||||
if ($conn === false) {
|
||||
ZMBuf::$sql_pool->put(null);
|
||||
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);
|
||||
return $result;
|
||||
} catch (DBException $e) {
|
||||
@@ -84,36 +90,41 @@ class DB
|
||||
/**
|
||||
* @param string $line
|
||||
* @param array $params
|
||||
* @param int $fetch_mode
|
||||
* @return mixed
|
||||
* @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) {
|
||||
$starttime = microtime(true);
|
||||
}
|
||||
Console::debug("MySQL: ".$line);
|
||||
try {
|
||||
$conn = ZMBuf::$sql_pool->get();
|
||||
if ($conn === false) {
|
||||
ZMBuf::$sql_pool->put(null);
|
||||
throw new DbException("无法连接SQL!" . $line);
|
||||
}
|
||||
$ps = $conn->prepare($line);
|
||||
if ($ps === false) {
|
||||
$conn->close();
|
||||
ZMBuf::$sql_pool->connect_cnt -= 1;
|
||||
ZMBuf::$sql_pool->put(null);
|
||||
throw new DbException("SQL语句查询错误," . $line . ",错误信息:" . $conn->error);
|
||||
} else {
|
||||
if (!($ps instanceof Statement)) {
|
||||
throw new DbException("语句查询错误!" . $line);
|
||||
if (!($ps instanceof PDOStatement) && !($ps instanceof PDOStatementProxy)) {
|
||||
var_dump($ps);
|
||||
ZMBuf::$sql_pool->put(null);
|
||||
throw new DbException("语句查询错误!返回的不是 PDOStatement" . $line);
|
||||
}
|
||||
if ($params == []) $result = $ps->execute();
|
||||
elseif (!is_array($params)) {
|
||||
$result = $ps->execute([$params]);
|
||||
} else $result = $ps->execute($params);
|
||||
ZMBuf::$sql_pool->put($conn);
|
||||
if ($ps->errno != 0) {
|
||||
throw new DBException("语句[$line]错误!" . $ps->error);
|
||||
if ($result !== true) {
|
||||
ZMBuf::$sql_pool->put(null);
|
||||
throw new DBException("语句[$line]错误!" . $ps->errorInfo()[2]);
|
||||
//echo json_encode(debug_backtrace(), 128 | 256);
|
||||
}
|
||||
ZMBuf::$sql_pool->put($conn);
|
||||
if (ZMBuf::get("sql_log") === true) {
|
||||
$log =
|
||||
"[" . date("Y-m-d H:i:s") .
|
||||
@@ -121,9 +132,9 @@ class DB
|
||||
"] " . $line . " " . json_encode($params, JSON_UNESCAPED_UNICODE) . "\n";
|
||||
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) {
|
||||
$log =
|
||||
"[" . date("Y-m-d H:i:s") .
|
||||
@@ -131,8 +142,28 @@ class DB
|
||||
"] " . $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 $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(); }
|
||||
|
||||
/**
|
||||
* @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
|
||||
* @throws DbException
|
||||
*/
|
||||
public function fetchAll() {
|
||||
public function fetchAll($fetch_mode = ZM_DEFAULT_FETCH_MODE) {
|
||||
if ($this->table->isCacheEnabled()) {
|
||||
$rr = md5(implode(",", $this->select_thing) . serialize($this->where_thing));
|
||||
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();
|
||||
}
|
||||
}
|
||||
$this->execute();
|
||||
$this->execute($fetch_mode);
|
||||
if ($this->table->isCacheEnabled() && !in_array($rr, $this->table->cache)) {
|
||||
$this->table->cache[$rr] = $this;
|
||||
}
|
||||
@@ -71,11 +82,12 @@ class SelectBody
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $fetch_mode
|
||||
* @throws DbException
|
||||
*/
|
||||
public function execute() {
|
||||
public function execute($fetch_mode = ZM_DEFAULT_FETCH_MODE) {
|
||||
$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; }
|
||||
|
||||
@@ -9,7 +9,7 @@ trait WhereBody
|
||||
protected $where_thing = [];
|
||||
|
||||
public function where($column, $operation_or_value, $value = null) {
|
||||
if (!in_array($operation_or_value, ['=', '!='])) $this->where_thing['='][$column] = $operation_or_value;
|
||||
if (!in_array($operation_or_value, ['=', '!=', '>', '<', '>=', '<=', 'IN', 'in'])) $this->where_thing['='][$column] = $operation_or_value;
|
||||
elseif ($value !== null) $this->where_thing[$operation_or_value][$column] = $value;
|
||||
else $this->where_thing['='][$column] = $operation_or_value;
|
||||
return $this;
|
||||
@@ -28,6 +28,7 @@ trait WhereBody
|
||||
$param []=$vs;
|
||||
}
|
||||
}
|
||||
if ($msg == '') $msg = 1;
|
||||
return [$msg, $param];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace ZM\Event\CQ;
|
||||
|
||||
|
||||
use Co;
|
||||
use Doctrine\Common\Annotations\AnnotationException;
|
||||
use Framework\Console;
|
||||
use Framework\ZMBuf;
|
||||
use ZM\Annotation\CQ\CQAfter;
|
||||
@@ -12,6 +13,7 @@ use ZM\Annotation\CQ\CQBefore;
|
||||
use ZM\Annotation\CQ\CQCommand;
|
||||
use ZM\Annotation\CQ\CQMessage;
|
||||
use ZM\Connection\WSConnection;
|
||||
use ZM\Event\EventHandler;
|
||||
use ZM\Exception\WaitTimeoutException;
|
||||
use ZM\Http\Response;
|
||||
use ZM\ModBase;
|
||||
@@ -31,37 +33,63 @@ class MessageEvent
|
||||
$this->circle = $circle;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @throws AnnotationException
|
||||
*/
|
||||
public function onBefore() {
|
||||
foreach (ZMBuf::$events[CQBefore::class]["message"] ?? [] as $v) {
|
||||
$c = $v->class;
|
||||
$class = new $c([
|
||||
"data" => $this->data,
|
||||
"connection" => $this->connection
|
||||
], ModHandleType::CQ_MESSAGE);
|
||||
$r = call_user_func_array([$class, $v->method], []);
|
||||
if (!$r || $class->block_continue) return false;
|
||||
$obj_list = ZMBuf::$events[CQBefore::class]["message"] ?? [];
|
||||
foreach ($obj_list as $v) {
|
||||
if ($v->level < 200) break;
|
||||
EventHandler::callWithMiddleware(
|
||||
$v->class,
|
||||
$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;
|
||||
}
|
||||
foreach (ZMBuf::get("wait_api", []) as $k => $v) {
|
||||
if($this->data["user_id"] == $v["user_id"] &&
|
||||
$this->data["self_id"] == $v["self_id"] &&
|
||||
$this->data["message_type"] == $v["message_type"] &&
|
||||
($this->data[$this->data["message_type"]."_id"] ?? $this->data["user_id"]) ==
|
||||
($v[$v["message_type"]."_id"] ?? $v["user_id"])){
|
||||
$v["result"] = $this->data["message"];
|
||||
if (context()->getData()["user_id"] == $v["user_id"] &&
|
||||
context()->getData()["self_id"] == $v["self_id"] &&
|
||||
context()->getData()["message_type"] == $v["message_type"] &&
|
||||
(context()->getData()[context()->getData()["message_type"] . "_id"] ?? context()->getData()["user_id"]) ==
|
||||
($v[$v["message_type"] . "_id"] ?? $v["user_id"])) {
|
||||
$v["result"] = context()->getData()["message"];
|
||||
ZMBuf::appendKey("wait_api", $k, $v);
|
||||
Co::resume($v["coroutine"]);
|
||||
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;
|
||||
}
|
||||
|
||||
/** @noinspection PhpRedundantCatchClauseInspection */
|
||||
/**
|
||||
* @throws AnnotationException
|
||||
*/
|
||||
public function onActivate() {
|
||||
try {
|
||||
$word = split_explode(" ", str_replace("\r", "", $this->data["message"]));
|
||||
$word = split_explode(" ", str_replace("\r", "", context()->getMessage()));
|
||||
if (count(explode("\n", $word[0])) >= 2) {
|
||||
$enter = explode("\n", $this->data["message"]);
|
||||
$enter = explode("\n", context()->getMessage());
|
||||
$first = split_explode(" ", array_shift($enter));
|
||||
$word = array_merge($first, $enter);
|
||||
foreach ($word as $k => $v) {
|
||||
@@ -73,22 +101,39 @@ class MessageEvent
|
||||
foreach (ZMBuf::$events[CQCommand::class] ?? [] as $v) {
|
||||
/** @var CQCommand $v */
|
||||
if ($v->match == "" && $v->regexMatch == "") 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;
|
||||
if (!isset($obj[$c]))
|
||||
$obj[$c] = new $c([
|
||||
"data" => $this->data,
|
||||
"connection" => $this->connection
|
||||
], ModHandleType::CQ_MESSAGE);
|
||||
$class_construct = [
|
||||
"data" => context()->getData(),
|
||||
"connection" => context()->getConnection()
|
||||
];
|
||||
if (!isset($obj[$c])) {
|
||||
$obj[$c] = new $c($class_construct);
|
||||
}
|
||||
if ($word[0] != "" && $v->match == $word[0]) {
|
||||
$r = call_user_func([$obj[$c], $v->method], $word);
|
||||
if (is_string($r)) $obj[$c]->reply($r);
|
||||
$this->function_call = true;
|
||||
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 (($args = matchArgs($v->regexMatch, $this->data["message"])) !== false) {
|
||||
$r = call_user_func([$obj[$c], $v->method], $args);
|
||||
if (is_string($r)) $obj[$c]->reply($r);
|
||||
$this->function_call = true;
|
||||
} 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) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -96,41 +141,48 @@ class MessageEvent
|
||||
foreach (ZMBuf::$events[CQMessage::class] ?? [] as $v) {
|
||||
/** @var CQMessage $v */
|
||||
if (
|
||||
($v->message == '' || ($v->message != '' && $v->message == $this->data["message"])) &&
|
||||
($v->user_id == 0 || ($v->user_id != 0 && $v->user_id == $this->data["user_id"])) &&
|
||||
($v->group_id == 0 || ($v->group_id != 0 && $v->group_id == ($this->data["group_id"] ?? 0))) &&
|
||||
($v->discuss_id == 0 || ($v->discuss_id != 0 && $v->discuss_id == ($this->data["discuss_id"] ?? 0))) &&
|
||||
($v->message_type == '' || ($v->message_type != '' && $v->message_type == $this->data["message_type"])) &&
|
||||
($v->raw_message == '' || ($v->raw_message != '' && $v->raw_message == $this->data["raw_message"]))) {
|
||||
($v->message == '' || ($v->message != '' && $v->message == context()->getData()["message"])) &&
|
||||
($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"])) &&
|
||||
($v->raw_message == '' || ($v->raw_message != '' && $v->raw_message == context()->getData()["raw_message"]))) {
|
||||
$c = $v->class;
|
||||
Console::debug("Calling CQMessage: $c -> {$v->method}");
|
||||
if (!isset($obj[$c]))
|
||||
$obj[$c] = new $c([
|
||||
"data" => $this->data,
|
||||
"data" => context()->getData(),
|
||||
"connection" => $this->connection
|
||||
], ModHandleType::CQ_MESSAGE);
|
||||
$r = call_user_func([$obj[$c], $v->method], $this->data["message"]);
|
||||
if (is_string($r)) $obj[$c]->reply($r);
|
||||
if ($obj[$c]->block_continue) return;
|
||||
EventHandler::callWithMiddleware($obj[$c], $v->method, [], [context()->getData()["message"]], function ($r) {
|
||||
if (is_string($r)) context()->reply($r);
|
||||
});
|
||||
if (context()->getCache("block_continue") === true) return;
|
||||
}
|
||||
}
|
||||
} catch (WaitTimeoutException $e) {
|
||||
|
||||
$e->module->finalReply($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 在调用完事件后执行的
|
||||
* @throws AnnotationException
|
||||
*/
|
||||
public function onAfter() {
|
||||
context()->setCache("block_continue", null);
|
||||
foreach (ZMBuf::$events[CQAfter::class]["message"] ?? [] as $v) {
|
||||
$c = $v->class;
|
||||
$class = new $c([
|
||||
"data" => $this->data,
|
||||
"connection" => $this->connection
|
||||
], ModHandleType::CQ_MESSAGE);
|
||||
$r = call_user_func_array([$class, $v->method], []);
|
||||
if (!$r || $class->block_continue) return false;
|
||||
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;
|
||||
}
|
||||
@@ -138,4 +190,4 @@ class MessageEvent
|
||||
public function hasReply() {
|
||||
return $this->function_call;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,11 +4,12 @@
|
||||
namespace ZM\Event\CQ;
|
||||
|
||||
|
||||
use Doctrine\Common\Annotations\AnnotationException;
|
||||
use Framework\ZMBuf;
|
||||
use ZM\Annotation\CQ\CQBefore;
|
||||
use ZM\Annotation\CQ\CQMetaEvent;
|
||||
use ZM\Connection\ConnectionManager;
|
||||
use ZM\Connection\CQConnection;
|
||||
use ZM\Event\EventHandler;
|
||||
use ZM\Exception\WaitTimeoutException;
|
||||
use ZM\ModBase;
|
||||
use ZM\ModHandleType;
|
||||
@@ -26,21 +27,30 @@ class MetaEvent
|
||||
$this->circle = $circle;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @throws AnnotationException
|
||||
*/
|
||||
public function onBefore() {
|
||||
foreach (ZMBuf::$events[CQBefore::class]["meta_event"] ?? [] as $v) {
|
||||
$c = $v->class;
|
||||
/** @var CQMetaEvent $v */
|
||||
$class = new $c([
|
||||
"data" => $this->data,
|
||||
"connection" => $this->connection
|
||||
], ModHandleType::CQ_META_EVENT);
|
||||
$r = call_user_func_array([$class, $v->method], []);
|
||||
if (!$r || $class->block_continue) return false;
|
||||
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;
|
||||
}
|
||||
|
||||
/** @noinspection PhpRedundantCatchClauseInspection */
|
||||
/**
|
||||
* @throws AnnotationException
|
||||
*/
|
||||
public function onActivate() {
|
||||
try {
|
||||
/** @var ModBase[] $obj */
|
||||
@@ -56,13 +66,14 @@ class MetaEvent
|
||||
"data" => $this->data,
|
||||
"connection" => $this->connection
|
||||
], ModHandleType::CQ_META_EVENT);
|
||||
$r = call_user_func([$obj[$c], $v->method]);
|
||||
if (is_string($r)) $obj[$c]->reply($r);
|
||||
if ($obj[$c]->block_continue) return;
|
||||
EventHandler::callWithMiddleware($obj[$c],$v->method, [], [], function($r) {
|
||||
if (is_string($r)) context()->reply($r);
|
||||
});
|
||||
if (context()->getCache("block_continue") === true) return;
|
||||
}
|
||||
}
|
||||
} catch (WaitTimeoutException $e) {
|
||||
$e->module->finalReply($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,11 +4,13 @@
|
||||
namespace ZM\Event\CQ;
|
||||
|
||||
|
||||
use Doctrine\Common\Annotations\AnnotationException;
|
||||
use Framework\ZMBuf;
|
||||
use ZM\Annotation\CQ\CQAfter;
|
||||
use ZM\Annotation\CQ\CQBefore;
|
||||
use ZM\Annotation\CQ\CQNotice;
|
||||
use ZM\Connection\CQConnection;
|
||||
use ZM\Event\EventHandler;
|
||||
use ZM\Exception\WaitTimeoutException;
|
||||
use ZM\ModBase;
|
||||
use ZM\ModHandleType;
|
||||
@@ -26,20 +28,30 @@ class NoticeEvent
|
||||
$this->circle = $circle;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @throws AnnotationException
|
||||
*/
|
||||
public function onBefore() {
|
||||
foreach (ZMBuf::$events[CQBefore::class]["notice"] ?? [] as $v) {
|
||||
$c = $v->class;
|
||||
/** @var CQNotice $v */
|
||||
$class = new $c([
|
||||
"data" => $this->data,
|
||||
"connection" => $this->connection
|
||||
], ModHandleType::CQ_NOTICE);
|
||||
$r = call_user_func_array([$class, $v->method], []);
|
||||
if (!$r || $class->block_continue) return false;
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws AnnotationException
|
||||
*/
|
||||
public function onActivate() {
|
||||
try {
|
||||
/** @var ModBase[] $obj */
|
||||
@@ -57,9 +69,10 @@ class NoticeEvent
|
||||
"data" => $this->data,
|
||||
"connection" => $this->connection
|
||||
], ModHandleType::CQ_NOTICE);
|
||||
$r = call_user_func([$obj[$c], $v->method]);
|
||||
if (is_string($r)) $obj[$c]->reply($r);
|
||||
if ($obj[$c]->block_continue) return;
|
||||
EventHandler::callWithMiddleware($obj[$c],$v->method, [], [], function($r) {
|
||||
if (is_string($r)) context()->reply($r);
|
||||
});
|
||||
if (context()->getCache("block_continue") === true) return;
|
||||
}
|
||||
}
|
||||
} /** @noinspection PhpRedundantCatchClauseInspection */ catch (WaitTimeoutException $e) {
|
||||
@@ -67,16 +80,24 @@ class NoticeEvent
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @throws AnnotationException
|
||||
*/
|
||||
public function onAfter() {
|
||||
foreach (ZMBuf::$events[CQAfter::class]["notice"] ?? [] as $v) {
|
||||
$c = $v->class;
|
||||
$class = new $c([
|
||||
"data" => $this->data,
|
||||
"connection" => $this->connection
|
||||
], ModHandleType::CQ_NOTICE);
|
||||
$r = call_user_func_array([$class, $v->method], []);
|
||||
if (!$r || $class->block_continue) return false;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,11 +4,13 @@
|
||||
namespace ZM\Event\CQ;
|
||||
|
||||
|
||||
use Doctrine\Common\Annotations\AnnotationException;
|
||||
use Framework\ZMBuf;
|
||||
use ZM\Annotation\CQ\CQAfter;
|
||||
use ZM\Annotation\CQ\CQBefore;
|
||||
use ZM\Annotation\CQ\CQRequest;
|
||||
use ZM\Connection\CQConnection;
|
||||
use ZM\Event\EventHandler;
|
||||
use ZM\Exception\WaitTimeoutException;
|
||||
use ZM\ModBase;
|
||||
use ZM\ModHandleType;
|
||||
@@ -26,21 +28,30 @@ class RequestEvent
|
||||
$this->circle = $circle;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @throws AnnotationException
|
||||
*/
|
||||
public function onBefore() {
|
||||
foreach (ZMBuf::$events[CQBefore::class]["request"] ?? [] as $v) {
|
||||
$c = $v->class;
|
||||
/** @var CQRequest $v */
|
||||
$class = new $c([
|
||||
"data" => $this->data,
|
||||
"connection" => $this->connection
|
||||
], ModHandleType::CQ_REQUEST);
|
||||
$r = call_user_func_array([$class, $v->method], []);
|
||||
if (!$r || $class->block_continue) return false;
|
||||
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;
|
||||
}
|
||||
|
||||
/** @noinspection PhpRedundantCatchClauseInspection */
|
||||
/**
|
||||
* @throws AnnotationException
|
||||
*/
|
||||
public function onActivate() {
|
||||
try {
|
||||
/** @var ModBase[] $obj */
|
||||
@@ -58,9 +69,10 @@ class RequestEvent
|
||||
"data" => $this->data,
|
||||
"connection" => $this->connection
|
||||
], ModHandleType::CQ_REQUEST);
|
||||
$r = call_user_func([$obj[$c], $v->method]);
|
||||
if (is_string($r)) $obj[$c]->reply($r);
|
||||
if ($obj[$c]->block_continue) return;
|
||||
EventHandler::callWithMiddleware($obj[$c],$v->method, [], [], function($r) {
|
||||
if (is_string($r)) context()->reply($r);
|
||||
});
|
||||
if (context()->getCache("block_continue") === true) return;
|
||||
}
|
||||
}
|
||||
} catch (WaitTimeoutException $e) {
|
||||
@@ -68,16 +80,24 @@ class RequestEvent
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @throws AnnotationException
|
||||
*/
|
||||
public function onAfter() {
|
||||
foreach (ZMBuf::$events[CQAfter::class]["request"] ?? [] as $v) {
|
||||
$c = $v->class;
|
||||
$class = new $c([
|
||||
"data" => $this->data,
|
||||
"connection" => $this->connection
|
||||
], ModHandleType::CQ_REQUEST);
|
||||
$r = call_user_func_array([$class, $v->method], []);
|
||||
if (!$r || $class->block_continue) return false;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,48 +5,83 @@ namespace ZM\Event;
|
||||
|
||||
|
||||
use Co;
|
||||
use Doctrine\Common\Annotations\AnnotationException;
|
||||
use Error;
|
||||
use Exception;
|
||||
use Framework\Console;
|
||||
use Framework\ZMBuf;
|
||||
use ZM\Event\Swoole\{MessageEvent, RequestEvent, WorkerStartEvent, WSCloseEvent, WSOpenEvent};
|
||||
use Swoole\Http\Request;
|
||||
use Swoole\Server;
|
||||
use Swoole\WebSocket\Frame;
|
||||
use ZM\Annotation\CQ\CQAPIResponse;
|
||||
use ZM\Annotation\CQ\CQAPISend;
|
||||
use ZM\Annotation\Http\MiddlewareClass;
|
||||
use ZM\Connection\ConnectionManager;
|
||||
use ZM\Connection\CQConnection;
|
||||
use ZM\Http\MiddlewareInterface;
|
||||
use ZM\Http\Response;
|
||||
use ZM\Utils\DataProvider;
|
||||
use Framework\DataProvider;
|
||||
use ZM\Utils\ZMUtil;
|
||||
|
||||
class EventHandler
|
||||
{
|
||||
/**
|
||||
* @param $event_name
|
||||
* @param $param0
|
||||
* @param null $param1
|
||||
* @throws AnnotationException
|
||||
*/
|
||||
public static function callSwooleEvent($event_name, $param0, $param1 = null) {
|
||||
//$starttime = microtime(true);
|
||||
unset(ZMBuf::$context[Co::getCid()]);
|
||||
$event_name = strtolower($event_name);
|
||||
switch ($event_name) {
|
||||
case "workerstart":
|
||||
try {
|
||||
register_shutdown_function(function () {
|
||||
register_shutdown_function(function () use ($param0) {
|
||||
$error = error_get_last();
|
||||
if ($error["type"] != 0) {
|
||||
Console::error("Internal fatal error: " . $error["message"] . " at " . $error["file"] . "({$error["line"]})");
|
||||
}
|
||||
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");
|
||||
$r->onAfter();
|
||||
self::startTick();
|
||||
} catch (Exception $e) {
|
||||
Console::error("Worker加载出错!停止服务!");
|
||||
Console::error($e->getMessage() . "\n" . $e->getTraceAsString());
|
||||
ZMUtil::stop();
|
||||
return;
|
||||
} 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();
|
||||
}
|
||||
break;
|
||||
case "message":
|
||||
(new MessageEvent($param0, $param1))->onActivate()->onAfter();
|
||||
/** @var Frame $param1 */
|
||||
/** @var Server $param0 */
|
||||
$conn = ConnectionManager::get($param1->fd);
|
||||
set_coroutine_params(["server" => $param0, "frame" => $param1, "connection" => $conn]);
|
||||
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;
|
||||
case "request":
|
||||
try {
|
||||
set_coroutine_params(["request" => $param0, "response" => $param1]);
|
||||
(new RequestEvent($param0, $param1))->onActivate()->onAfter();
|
||||
} catch (Exception $e) {
|
||||
/** @var Response $param1 */
|
||||
@@ -55,21 +90,56 @@ class EventHandler
|
||||
" [" . $param1->getStatusCode() . "] " . $param0->server["request_uri"]
|
||||
);
|
||||
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");
|
||||
}
|
||||
break;
|
||||
case "open":
|
||||
(new WSOpenEvent($param0, $param1))->onActivate()->onAfter();
|
||||
/** @var Request $param1 */
|
||||
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;
|
||||
case "close":
|
||||
(new WSCloseEvent($param0, $param1))->onActivate()->onAfter();
|
||||
set_coroutine_params(["server" => $param0, "fd" => $param1]);
|
||||
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;
|
||||
}
|
||||
//Console::info(Console::setColor("Event: " . $event_name . " 运行了 " . round(microtime(true) - $starttime, 5) . " 秒", "gold"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $event_data
|
||||
* @param $conn_or_response
|
||||
* @param int $level
|
||||
* @return bool
|
||||
* @throws AnnotationException
|
||||
*/
|
||||
public static function callCQEvent($event_data, $conn_or_response, int $level = 0) {
|
||||
ctx()->setCache("level", $level);
|
||||
if ($level >= 5) {
|
||||
Console::warning("Recursive call reached " . $level . " times");
|
||||
Console::stackTrace();
|
||||
@@ -102,20 +172,42 @@ class EventHandler
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $req
|
||||
* @throws AnnotationException
|
||||
*/
|
||||
public static function callCQResponse($req) {
|
||||
//Console::info("收到来自API连接的回复:".json_encode($req, 128|256));
|
||||
Console::debug("收到来自API连接的回复:" . json_encode($req, 128 | 256));
|
||||
$status = $req["status"];
|
||||
$retcode = $req["retcode"];
|
||||
$data = $req["data"];
|
||||
if (isset($req["echo"]) && ZMBuf::array_key_exists("sent_api", $req["echo"])) {
|
||||
$status = $req["status"];
|
||||
$retcode = $req["retcode"];
|
||||
$data = $req["data"];
|
||||
$origin = ZMBuf::get("sent_api")[$req["echo"]];
|
||||
$self_id = $origin["self_id"];
|
||||
$response = [
|
||||
"status" => $status,
|
||||
"retcode" => $retcode,
|
||||
"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"]])) {
|
||||
list($c, $method) = ZMBuf::$events[CQAPIResponse::class][$req["retcode"]];
|
||||
$class = new $c(["data" => $origin["data"]]);
|
||||
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) {
|
||||
call_user_func($origin["func"], $response, $origin["data"]);
|
||||
} elseif (($origin["coroutine"] ?? false) !== false) {
|
||||
@@ -127,4 +219,102 @@ class EventHandler
|
||||
ZMBuf::unsetByValue("sent_api", $req["echo"]);
|
||||
}
|
||||
}
|
||||
|
||||
public static function callCQAPISend($reply, ?CQConnection $connection) {
|
||||
$action = $reply["action"] ?? null;
|
||||
if ($action === null) {
|
||||
Console::warning("API 激活事件异常!");
|
||||
return;
|
||||
}
|
||||
if (ctx() === null) $content = [];
|
||||
else $content = ctx()->copy();
|
||||
go(function () use ($action, $reply, $connection, $content) {
|
||||
set_coroutine_params($content);
|
||||
context()->setCache("action", $action);
|
||||
context()->setCache("reply", $reply);
|
||||
foreach (ZMBuf::$events[CQAPISend::class] ?? [] as $k => $v) {
|
||||
if (($v->action == "" || $v->action == $action) && !$v->with_result) {
|
||||
$c = $v->class;
|
||||
self::callWithMiddleware($c, $v->method, context()->copy(), [$reply["action"], $reply["params"] ?? [], $connection->getQQ()]);
|
||||
if (context()->getCache("block_continue") === true) break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $c
|
||||
* @param $method
|
||||
* @param array $class_construct
|
||||
* @param array $func_args
|
||||
* @param null $after_call
|
||||
* @return mixed|null
|
||||
* @throws AnnotationException
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function callWithMiddleware($c, $method, array $class_construct, array $func_args, $after_call = null) {
|
||||
$return_value = null;
|
||||
$plain_class = is_object($c) ? get_class($c) : $c;
|
||||
if (isset(ZMBuf::$events[MiddlewareInterface::class][$plain_class][$method])) {
|
||||
$middlewares = ZMBuf::$events[MiddlewareInterface::class][$plain_class][$method];
|
||||
$before_result = true;
|
||||
$r = [];
|
||||
foreach ($middlewares as $k => $middleware) {
|
||||
if (!isset(ZMBuf::$events[MiddlewareClass::class][$middleware])) throw new AnnotationException("Annotation parse error: Unknown MiddlewareClass named \"{$middleware}\"!");
|
||||
$middleware_obj = ZMBuf::$events[MiddlewareClass::class][$middleware];
|
||||
$before = $middleware_obj["class"];
|
||||
$r[$k] = new $before();
|
||||
$r[$k]->class = is_object($c) ? get_class($c) : $c;
|
||||
$r[$k]->method = $method;
|
||||
if (isset($middleware_obj["before"])) {
|
||||
$before_result = call_user_func_array([$r[$k], $middleware_obj["before"]], $func_args);
|
||||
if ($before_result === false) break;
|
||||
}
|
||||
}
|
||||
if ($before_result) {
|
||||
try {
|
||||
if (is_object($c)) $class = $c;
|
||||
elseif ($class_construct == []) $class = ZMUtil::getModInstance($c);
|
||||
else $class = new $c($class_construct);
|
||||
$result = call_user_func_array([$class, $method], $func_args);
|
||||
if (is_callable($after_call))
|
||||
$return_value = call_user_func_array($after_call, [$result]);
|
||||
} catch (Exception $e) {
|
||||
for ($i = count($middlewares) - 1; $i >= 0; --$i) {
|
||||
$middleware_obj = ZMBuf::$events[MiddlewareClass::class][$middlewares[$i]];
|
||||
if (!isset($middleware_obj["exceptions"])) continue;
|
||||
foreach ($middleware_obj["exceptions"] as $name => $method) {
|
||||
if ($e instanceof $name) {
|
||||
$r[$i]->$method($e);
|
||||
context()->setCache("block_continue", true);
|
||||
}
|
||||
}
|
||||
if (context()->getCache("block_continue") === true) return $return_value;
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
for ($i = count($middlewares) - 1; $i >= 0; --$i) {
|
||||
$middleware_obj = ZMBuf::$events[MiddlewareClass::class][$middlewares[$i]];
|
||||
if (isset($middleware_obj["after"], $r[$i]))
|
||||
call_user_func_array([$r[$i], $middleware_obj["after"]], $func_args);
|
||||
}
|
||||
} else {
|
||||
if (is_object($c)) $class = $c;
|
||||
elseif ($class_construct == []) $class = ZMUtil::getModInstance($c);
|
||||
else $class = new $c($class_construct);
|
||||
$result = call_user_func_array([$class, $method], $func_args);
|
||||
if (is_callable($after_call))
|
||||
$return_value = call_user_func_array($after_call, [$result]);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,28 +39,35 @@ class MessageEvent implements SwooleEvent
|
||||
*/
|
||||
public function onActivate() {
|
||||
ZMUtil::checkWait();
|
||||
$conn = ConnectionManager::get($this->frame->fd);
|
||||
set_coroutine_params(["server" => $this->server, "frame" => $this->frame, "connection" => $conn]);
|
||||
$conn = ConnectionManager::get(context()->getFrame()->fd);
|
||||
try {
|
||||
if ($conn->getType() == "qq") {
|
||||
$data = json_decode($this->frame->data, true);
|
||||
$data = json_decode(context()->getFrame()->data, true);
|
||||
if (isset($data["post_type"])) {
|
||||
set_coroutine_params(["data" => $data, "connection" => $conn]);
|
||||
EventHandler::callCQEvent($data, ConnectionManager::get($this->frame->fd), 0);
|
||||
} else
|
||||
ctx()->setCache("level", 0);
|
||||
Console::debug("Calling CQ Event from fd=" . $conn->fd);
|
||||
EventHandler::callCQEvent($data, ConnectionManager::get(context()->getFrame()->fd), 0);
|
||||
} else{
|
||||
set_coroutine_params(["connection" => $conn]);
|
||||
EventHandler::callCQResponse($data);
|
||||
}
|
||||
}
|
||||
foreach (ZMBuf::$events[SwooleEventAt::class] ?? [] as $v) {
|
||||
if (strtolower($v->type) == "message" && $this->parseSwooleRule($v)) {
|
||||
$c = $v->class;
|
||||
/** @var ModBase $class */
|
||||
$class = new $c(["server" => $this->server, "frame" => $this->frame, "connection" => $conn], ModHandleType::SWOOLE_MESSAGE);
|
||||
call_user_func_array([$class, $v->method], [$conn]);
|
||||
if ($class->block_continue) break;
|
||||
EventHandler::callWithMiddleware(
|
||||
$c,
|
||||
$v->method,
|
||||
["server" => $this->server, "frame" => $this->frame, "connection" => $conn],
|
||||
[$conn]
|
||||
);
|
||||
if (context()->getCache("block_continue") === true) break;
|
||||
}
|
||||
}
|
||||
} 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;
|
||||
}
|
||||
@@ -76,7 +83,7 @@ class MessageEvent implements SwooleEvent
|
||||
/** @var ModBase $class */
|
||||
$class = new $c(["server" => $this->server, "frame" => $this->frame, "connection" => $conn], ModHandleType::SWOOLE_MESSAGE);
|
||||
call_user_func_array([$class, $v->method], []);
|
||||
if ($class->block_continue) break;
|
||||
if (context()->getCache("block_continue") === true) break;
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
|
||||
@@ -5,18 +5,15 @@ namespace ZM\Event\Swoole;
|
||||
|
||||
|
||||
use Closure;
|
||||
use Doctrine\Common\Annotations\AnnotationException;
|
||||
use Exception;
|
||||
use Framework\Console;
|
||||
use Framework\ZMBuf;
|
||||
use Swoole\Http\Request;
|
||||
use ZM\Annotation\Http\MiddlewareClass;
|
||||
use ZM\Annotation\Swoole\SwooleEventAfter;
|
||||
use ZM\Annotation\Swoole\SwooleEventAt;
|
||||
use ZM\Http\MiddlewareInterface;
|
||||
use ZM\Event\EventHandler;
|
||||
use ZM\Http\Response;
|
||||
use ZM\ModBase;
|
||||
use ZM\ModHandleType;
|
||||
use Framework\DataProvider;
|
||||
use ZM\Utils\ZMUtil;
|
||||
|
||||
class RequestEvent implements SwooleEvent
|
||||
@@ -45,6 +42,7 @@ class RequestEvent implements SwooleEvent
|
||||
$this->response->setHeader($k, $v);
|
||||
}
|
||||
$uri = $this->request->server["request_uri"];
|
||||
Console::verbose($this->request->server["remote_addr"] . " request " . $uri);
|
||||
$uri = explode("/", $uri);
|
||||
$uri = array_diff($uri, ["..", "", "."]);
|
||||
$node = ZMBuf::$req_mapping;
|
||||
@@ -90,7 +88,7 @@ class RequestEvent implements SwooleEvent
|
||||
$path = realpath($base_dir . urldecode($uri));
|
||||
if ($path !== false) {
|
||||
if (is_dir($path)) $path = $path . '/';
|
||||
$work = realpath(WORKING_DIR) . '/';
|
||||
$work = realpath(DataProvider::getWorkingDir()) . '/';
|
||||
if (strpos($path, $work) !== 0) {
|
||||
$this->responseStatus(403);
|
||||
return $this;
|
||||
@@ -119,58 +117,26 @@ class RequestEvent implements SwooleEvent
|
||||
return $this;
|
||||
}
|
||||
|
||||
set_coroutine_params(["request" => $this->request, "response" => $this->response, "params" => $params]);
|
||||
context()->setCache("params", $params);
|
||||
|
||||
if (in_array(strtoupper($this->request->server["request_method"]), $node["request_method"] ?? [])) { //判断目标方法在不在里面
|
||||
$c_name = $node["class"];
|
||||
if (isset(ZMBuf::$events[MiddlewareInterface::class][$c_name][$node["method"]])) {
|
||||
$middleware = ZMBuf::$events[MiddlewareInterface::class][$c_name][$node["method"]];
|
||||
if (!isset(ZMBuf::$events[MiddlewareClass::class][$middleware])) throw new AnnotationException("Annotation parse error: Unknown MiddlewareClass named \"{$middleware}\"!");
|
||||
$middleware = ZMBuf::$events[MiddlewareClass::class][$middleware];
|
||||
$before = $middleware["class"];
|
||||
$r = new $before();
|
||||
$before_result = true;
|
||||
if (isset($middleware["before"])) {
|
||||
$before_result = call_user_func([$r, $middleware["before"]], $this->request, $this->response, $params);
|
||||
if ($before_result !== false) $before_result = true;
|
||||
EventHandler::callWithMiddleware(
|
||||
$c_name,
|
||||
$node["method"],
|
||||
["request" => $this->request, "response" => &$this->response, "params" => $params],
|
||||
[$params],
|
||||
function ($result) {
|
||||
if (is_string($result) && !$this->response->isEnd()) $this->response->end($result);
|
||||
if ($this->response->isEnd()) context()->setCache("block_continue", true);
|
||||
}
|
||||
if ($before_result) {
|
||||
try {
|
||||
/** @var ModBase $class */
|
||||
$class = new $c_name(["request" => $this->request, "response" => $this->response, "params" => $params], ModHandleType::SWOOLE_REQUEST);
|
||||
$result = call_user_func_array([$class, $node["method"]], [$params]);
|
||||
if (is_string($result) && !$this->response->isEnd()) $this->response->end($result);
|
||||
if (!$this->response->isEnd()) goto eventCall;
|
||||
} catch (Exception $e) {
|
||||
if (!isset($middleware["exceptions"])) throw $e;
|
||||
foreach ($middleware["exceptions"] as $name => $method) {
|
||||
if ($e instanceof $name) {
|
||||
call_user_func([$r, $method], $e, $this->request, $this->response, $params);
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
if (isset($middleware["after"])) {
|
||||
call_user_func([$r, $middleware["after"]], $this->request, $this->response, $params);
|
||||
return $this;
|
||||
}
|
||||
} else {
|
||||
/** @var ModBase $class */
|
||||
$class = new $c_name(["request" => $this->request, "response" => $this->response, "params" => $params], ModHandleType::SWOOLE_REQUEST);
|
||||
$r = call_user_func_array([$class, $node["method"]], [$params]);
|
||||
if (is_string($r) && !$this->response->isEnd()) $this->response->end($r);
|
||||
if (!$this->response->isEnd()) goto eventCall;
|
||||
}
|
||||
);
|
||||
}
|
||||
eventCall:
|
||||
foreach (ZMBuf::$events[SwooleEventAt::class] ?? [] as $v) {
|
||||
if (strtolower($v->type) == "request" && $this->parseSwooleRule($v)) {
|
||||
$c = $v->class;
|
||||
$class = new $c(["request" => $this->request, "response" => $this->response]);
|
||||
$r = call_user_func_array([$class, $v->method], []);
|
||||
if ($class->block_continue) break;
|
||||
EventHandler::callWithMiddleware($c, $v->method, ["request" => $this->request, "response" => $this->response], []);
|
||||
if (context()->getCache("block_continue") === true) break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,13 +4,14 @@
|
||||
namespace ZM\Event\Swoole;
|
||||
|
||||
|
||||
use Closure;
|
||||
use Doctrine\Common\Annotations\AnnotationException;
|
||||
use Framework\ZMBuf;
|
||||
use Swoole\Server;
|
||||
use ZM\Annotation\Swoole\SwooleEventAfter;
|
||||
use ZM\Annotation\Swoole\SwooleEventAt;
|
||||
use ZM\Connection\ConnectionManager;
|
||||
use ZM\ModBase;
|
||||
use ZM\ModHandleType;
|
||||
use ZM\Event\EventHandler;
|
||||
use ZM\Utils\ZMUtil;
|
||||
|
||||
class WSCloseEvent implements SwooleEvent
|
||||
@@ -26,39 +27,43 @@ class WSCloseEvent implements SwooleEvent
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @throws AnnotationException
|
||||
*/
|
||||
public function onActivate() {
|
||||
ZMUtil::checkWait();
|
||||
ConnectionManager::close($this->fd);
|
||||
set_coroutine_params(["server" => $this->server, "fd" => $this->fd]);
|
||||
set_coroutine_params(["server" => $this->server, "fd" => $this->fd, "connection" => ConnectionManager::get($this->fd)]);
|
||||
foreach(ZMBuf::$events[SwooleEventAt::class] ?? [] as $v) {
|
||||
if(strtolower($v->type) == "close" && $this->parseSwooleRule($v)) {
|
||||
$c = $v->class;
|
||||
$class = new $c(["server" => $this->server, "fd" => $this->fd], ModHandleType::SWOOLE_CLOSE);
|
||||
call_user_func_array([$class, $v->method], []);
|
||||
if($class->block_continue) break;
|
||||
EventHandler::callWithMiddleware($c, $v->method, ["server" => $this->server, "fd" => $this->fd], []);
|
||||
if(context()->getCache("block_continue") === true) break;
|
||||
}
|
||||
}
|
||||
ConnectionManager::close($this->fd);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @throws AnnotationException
|
||||
*/
|
||||
public function onAfter() {
|
||||
foreach (ZMBuf::$events[SwooleEventAfter::class] ?? [] as $v) {
|
||||
if (strtolower($v->type) == "close" && $this->parseSwooleRule($v) === true) {
|
||||
$c = $v->class;
|
||||
/** @var ModBase $class */
|
||||
$class = new $c(["server" => $this->server, "fd" => $this->fd], ModHandleType::SWOOLE_CLOSE);
|
||||
call_user_func_array([$class, $v->method], []);
|
||||
if($class->block_continue) break;
|
||||
EventHandler::callWithMiddleware($c, $v->method, ["server" => $this->server, "fd" => $this->fd], []);
|
||||
if(context()->getCache("block_continue") === true) break;
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ namespace ZM\Event\Swoole;
|
||||
|
||||
|
||||
use Closure;
|
||||
use Doctrine\Common\Annotations\AnnotationException;
|
||||
use Framework\Console;
|
||||
use Framework\ZMBuf;
|
||||
use Swoole\Http\Request;
|
||||
use Swoole\WebSocket\Server;
|
||||
@@ -14,6 +16,7 @@ use ZM\Connection\ConnectionManager;
|
||||
use ZM\Connection\CQConnection;
|
||||
use ZM\Connection\UnknownConnection;
|
||||
use ZM\Connection\WSConnection;
|
||||
use ZM\Event\EventHandler;
|
||||
use ZM\ModBase;
|
||||
use ZM\ModHandleType;
|
||||
use ZM\Utils\ZMUtil;
|
||||
@@ -40,6 +43,7 @@ class WSOpenEvent implements SwooleEvent
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @throws AnnotationException
|
||||
*/
|
||||
public function onActivate() {
|
||||
ZMUtil::checkWait();
|
||||
@@ -48,9 +52,16 @@ class WSOpenEvent implements SwooleEvent
|
||||
if ($type_conn == CQConnection::class) {
|
||||
$qq = $this->request->get["qq"] ?? $this->request->header["x-self-id"] ?? "";
|
||||
$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);
|
||||
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 {
|
||||
$this->conn = new $type_conn($this->server, $this->request->fd);
|
||||
}
|
||||
@@ -59,9 +70,13 @@ class WSOpenEvent implements SwooleEvent
|
||||
foreach (ZMBuf::$events[SwooleEventAt::class] ?? [] as $v) {
|
||||
if (strtolower($v->type) == "open" && $this->parseSwooleRule($v) === true) {
|
||||
$c = $v->class;
|
||||
$class = new $c(["server" => $this->server, "request" => $this->request, "connection" => $this->conn], ModHandleType::SWOOLE_OPEN);
|
||||
call_user_func_array([$class, $v->method], [$this->conn]);
|
||||
if ($class->block_continue) break;
|
||||
EventHandler::callWithMiddleware(
|
||||
$c,
|
||||
$v->method,
|
||||
["server" => $this->server, "request" => $this->request, "connection" => $this->conn],
|
||||
[$this->conn]
|
||||
);
|
||||
if (context()->getCache("block_continue") === true) break;
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
@@ -77,7 +92,7 @@ class WSOpenEvent implements SwooleEvent
|
||||
/** @var ModBase $class */
|
||||
$class = new $v["class"](["server" => $this->server, "request" => $this->request, "connection" => $this->conn], ModHandleType::SWOOLE_OPEN);
|
||||
call_user_func_array([$class, $v["method"]], [$this->conn]);
|
||||
if ($class->block_continue) break;
|
||||
if (context()->getCache("block_continue") === true) break;
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
|
||||
@@ -7,8 +7,11 @@ namespace ZM\Event\Swoole;
|
||||
use Co;
|
||||
use Doctrine\Common\Annotations\AnnotationException;
|
||||
use Exception;
|
||||
use PDO;
|
||||
use ReflectionException;
|
||||
use Swoole\Coroutine;
|
||||
use Swoole\Database\PDOConfig;
|
||||
use Swoole\Database\PDOPool;
|
||||
use Swoole\Process;
|
||||
use Swoole\Timer;
|
||||
use ZM\Annotation\AnnotationBase;
|
||||
@@ -22,11 +25,9 @@ use Framework\Console;
|
||||
use Framework\GlobalConfig;
|
||||
use Framework\ZMBuf;
|
||||
use Swoole\Server;
|
||||
use ZM\Event\EventHandler;
|
||||
use ZM\Exception\DbException;
|
||||
use ZM\ModBase;
|
||||
use ZM\ModHandleType;
|
||||
use ZM\Utils\DataProvider;
|
||||
use ZM\Utils\SQLPool;
|
||||
use Framework\DataProvider;
|
||||
use ZM\Utils\ZMUtil;
|
||||
|
||||
class WorkerStartEvent implements SwooleEvent
|
||||
@@ -50,7 +51,10 @@ class WorkerStartEvent implements SwooleEvent
|
||||
*/
|
||||
public function onActivate(): WorkerStartEvent {
|
||||
Console::info("Worker启动中");
|
||||
Process::signal(SIGINT, function (){
|
||||
ZMBuf::$server = $this->server;
|
||||
Console::listenConsole(); //这个方法只能在这里调用,且如果worker_num不为1的话,此功能不可用
|
||||
|
||||
Process::signal(SIGINT, function () {
|
||||
Console::warning("Server interrupted by keyboard.");
|
||||
ZMUtil::stop(true);
|
||||
});
|
||||
@@ -61,60 +65,83 @@ class WorkerStartEvent implements SwooleEvent
|
||||
//设置炸毛buf中储存的对象
|
||||
ZMBuf::$globals = new GlobalConfig();
|
||||
ZMBuf::$config = [];
|
||||
$file = scandir(WORKING_DIR . '/config/');
|
||||
$file = scandir(DataProvider::getWorkingDir() . '/config/');
|
||||
unset($file[0], $file[1]);
|
||||
foreach ($file as $k => $v) {
|
||||
if ($v == "global.php") continue;
|
||||
$name = explode(".", $v);
|
||||
if (($prefix = end($name)) == "json") {
|
||||
ZMBuf::$config[$name[0]] = json_decode(Co::readFile(WORKING_DIR . '/config/' . $v), true);
|
||||
ZMBuf::$config[$name[0]] = json_decode(Co::readFile(DataProvider::getWorkingDir() . '/config/' . $v), true);
|
||||
Console::info("已读取配置文件:" . $v);
|
||||
} elseif ($prefix == "php") {
|
||||
ZMBuf::$config[$name[0]] = include_once WORKING_DIR . '/config/' . $v;
|
||||
ZMBuf::$config[$name[0]] = include_once DataProvider::getWorkingDir() . '/config/' . $v;
|
||||
if (is_array(ZMBuf::$config[$name[0]]))
|
||||
Console::info("已读取配置文件:" . $v);
|
||||
}
|
||||
}
|
||||
if (ZMBuf::globals("sql_config")["sql_host"] != "") {
|
||||
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();
|
||||
}
|
||||
ZMBuf::$server = $this->server;
|
||||
|
||||
ZMBuf::$atomics['reload_time']->add(1);
|
||||
|
||||
Console::info("监听console输入");
|
||||
Console::listenConsole(); //这个方法只能在这里调用,且如果worker_num不为1的话,此功能不可用
|
||||
|
||||
$this->loadAllClass(); //加载composer资源、phar外置包、注解解析注册等
|
||||
|
||||
$this->setAutosaveTimer(ZMBuf::globals("auto_save_interval"));
|
||||
|
||||
$this->loadAllClass(); //加载composer资源、phar外置包、注解解析注册等
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return WorkerStartEvent
|
||||
* @throws AnnotationException
|
||||
*/
|
||||
public function onAfter(): WorkerStartEvent {
|
||||
foreach (ZMBuf::get("wait_start") as $v) {
|
||||
Coroutine::resume($v);
|
||||
}
|
||||
ZMBuf::unsetCache("wait_start");
|
||||
foreach(ZMBuf::$events[OnStart::class] ?? [] as $v) {
|
||||
$class_name = $v->class;
|
||||
/** @var ModBase $class */
|
||||
$class = new $class_name(["server" => $this->server, "worker_id" => $this->worker_id], ModHandleType::SWOOLE_WORKER_START);
|
||||
call_user_func_array([$class, $v->method], []);
|
||||
}
|
||||
set_coroutine_params(["server" => $this->server, "worker_id" => $this->worker_id]);
|
||||
|
||||
foreach (ZMBuf::$events[OnStart::class] ?? [] as $v) {
|
||||
$class_name = $v->class;
|
||||
Console::debug("正在调用启动时函数: " . $class_name . " -> " . $v->method);
|
||||
EventHandler::callWithMiddleware($class_name, $v->method, ["server" => $this->server, "worker_id" => $this->worker_id], []);
|
||||
}
|
||||
foreach (ZMBuf::$events[SwooleEventAfter::class] ?? [] as $v) {
|
||||
/** @var AnnotationBase $v */
|
||||
if (strtolower($v->type) == "workerstart") {
|
||||
$class_name = $v->class;
|
||||
/** @var ModBase $class */
|
||||
$class = new $class_name(["server" => $this->server, "worker_id" => $this->worker_id], ModHandleType::SWOOLE_WORKER_START);
|
||||
call_user_func_array([$class, $v->method], []);
|
||||
if ($class->block_continue) break;
|
||||
Console::debug("正在调用启动时函数after: " . $class_name . " -> " . $v->method);
|
||||
EventHandler::callWithMiddleware($class_name, $v->method, ["server" => $this->server, "worker_id" => $this->worker_id], []);
|
||||
if (context()->getCache("block_continue") === true) break;
|
||||
}
|
||||
}
|
||||
Console::debug("调用完毕!");
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -122,8 +149,8 @@ class WorkerStartEvent implements SwooleEvent
|
||||
foreach ($this->server->connections as $v) {
|
||||
$this->server->close($v);
|
||||
}
|
||||
if (ZMBuf::$sql_pool instanceof SqlPool) {
|
||||
ZMBuf::$sql_pool->destruct();
|
||||
if (ZMBuf::$sql_pool !== null) {
|
||||
ZMBuf::$sql_pool->close();
|
||||
ZMBuf::$sql_pool = null;
|
||||
}
|
||||
}
|
||||
@@ -136,19 +163,21 @@ class WorkerStartEvent implements SwooleEvent
|
||||
private function loadAllClass() {
|
||||
//加载phar包
|
||||
Console::info("加载外部phar包中");
|
||||
$dir = WORKING_DIR . "/resources/package/";
|
||||
$dir = DataProvider::getWorkingDir() . "/resources/package/";
|
||||
if (version_compare(SWOOLE_VERSION, "4.4.0", ">=")) Timer::clearAll();
|
||||
if (is_dir($dir)) {
|
||||
$list = scandir($dir);
|
||||
unset($list[0], $list[1]);
|
||||
foreach ($list as $v) {
|
||||
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类
|
||||
Console::info("加载composer资源中");
|
||||
require_once WORKING_DIR . "/vendor/autoload.php";
|
||||
//remove stupid duplicate code
|
||||
|
||||
//加载各个模块的注解类,以及反射
|
||||
Console::info("检索Module中");
|
||||
@@ -158,14 +187,15 @@ class WorkerStartEvent implements SwooleEvent
|
||||
ConnectionManager::registerCustomClass();
|
||||
|
||||
//加载自定义的全局函数
|
||||
if(file_exists(WORKING_DIR."/src/Custom/global_function.php"))
|
||||
require_once WORKING_DIR."/src/Custom/global_function.php";
|
||||
Console::debug("加载自定义的全局函数中");
|
||||
if (file_exists(DataProvider::getWorkingDir() . "/src/Custom/global_function.php"))
|
||||
require_once DataProvider::getWorkingDir() . "/src/Custom/global_function.php";
|
||||
$this->afterCheck();
|
||||
}
|
||||
|
||||
private function setAutosaveTimer($globals) {
|
||||
DataProvider::$buffer_list = [];
|
||||
Timer::tick($globals * 1000, function () {
|
||||
zm_timer_tick($globals * 1000, function () {
|
||||
DataProvider::saveBuffer();
|
||||
});
|
||||
}
|
||||
@@ -175,7 +205,7 @@ class WorkerStartEvent implements SwooleEvent
|
||||
*/
|
||||
private function afterCheck() {
|
||||
$context_class = ZMBuf::globals("context_class");
|
||||
if(!is_a($context_class, ContextInterface::class, true)) {
|
||||
if (!is_a($context_class, ContextInterface::class, true)) {
|
||||
throw new Exception("Context class must implemented from ContextInterface!");
|
||||
}
|
||||
}
|
||||
|
||||
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\Server;
|
||||
|
||||
/**
|
||||
* Class ModBase
|
||||
* @package ZM
|
||||
* @deprecated
|
||||
*/
|
||||
abstract class ModBase
|
||||
{
|
||||
/** @var Server */
|
||||
@@ -67,7 +72,7 @@ abstract class ModBase
|
||||
}
|
||||
|
||||
public function finalReply($msg, $yield = false) {
|
||||
$this->block_continue = true;
|
||||
$this->setBlock();
|
||||
if ($msg == "") return true;
|
||||
return $this->reply($msg, $yield);
|
||||
}
|
||||
@@ -161,5 +166,5 @@ abstract class ModBase
|
||||
|
||||
public function getConnection() { return $this->connection; }
|
||||
|
||||
public function setBlock($result = true) { $this->block_continue = $result; }
|
||||
}
|
||||
public function setBlock($result = true) { context()->setCache("block_continue", $result); }
|
||||
}
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Utils;
|
||||
|
||||
|
||||
use Co;
|
||||
use Framework\Console;
|
||||
use Framework\ZMBuf;
|
||||
|
||||
class DataProvider
|
||||
{
|
||||
public static $buffer_list = [];
|
||||
|
||||
public static function getResourceFolder()
|
||||
{
|
||||
return WORKING_DIR . '/resources/';
|
||||
}
|
||||
|
||||
public static function getDataConfig()
|
||||
{
|
||||
return CONFIG_DIR;
|
||||
}
|
||||
|
||||
public static function addSaveBuffer($buf_name, $sub_folder = null)
|
||||
{
|
||||
$name = ($sub_folder ?? "") . "/" . $buf_name . ".json";
|
||||
self::$buffer_list[$buf_name] = $name;
|
||||
ZMBuf::set($buf_name, self::getJsonData($name));
|
||||
}
|
||||
|
||||
public static function saveBuffer()
|
||||
{
|
||||
$head = Console::setColor(date("[H:i:s] ") . "[V] Saving buffer......", "blue");
|
||||
if (ZMBuf::$atomics["info_level"]->get() >= 3)
|
||||
echo $head;
|
||||
foreach (self::$buffer_list as $k => $v) {
|
||||
self::setJsonData($v, ZMBuf::get($k));
|
||||
}
|
||||
if (ZMBuf::$atomics["info_level"]->get() >= 3)
|
||||
echo Console::setColor("saved", "blue") . PHP_EOL;
|
||||
}
|
||||
|
||||
public static function getFrameworkLink()
|
||||
{
|
||||
return ZMBuf::globals("http_reverse_link");
|
||||
}
|
||||
|
||||
public static function getJsonData(string $string)
|
||||
{
|
||||
if (!file_exists(self::getDataConfig() . $string)) return [];
|
||||
return json_decode(Co::readFile(self::getDataConfig() . $string), true);
|
||||
}
|
||||
|
||||
private static function setJsonData($filename, array $args)
|
||||
{
|
||||
Co::writeFile(self::getDataConfig() . $filename, json_encode($args, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_BIGINT_AS_STRING));
|
||||
}
|
||||
|
||||
public static function getDataFolder()
|
||||
{
|
||||
return ZM_DATA;
|
||||
}
|
||||
}
|
||||
@@ -10,9 +10,10 @@ namespace ZM\Utils;
|
||||
|
||||
use framework\Console;
|
||||
use framework\ZMBuf;
|
||||
use PDO;
|
||||
use PDOException;
|
||||
use SplQueue;
|
||||
use Swoole\Coroutine;
|
||||
use Swoole\Coroutine\Mysql;
|
||||
|
||||
class SQLPool
|
||||
{
|
||||
@@ -32,6 +33,26 @@ class SQLPool
|
||||
"password" => ZMBuf::globals("sql_config")["sql_password"],
|
||||
"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
|
||||
* @return bool|mixed|Mysql
|
||||
* @return bool|mixed|PDO
|
||||
*/
|
||||
public function get($no_new_conn = false) {
|
||||
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;
|
||||
$r = $this->newConnect();
|
||||
if ($r !== false) {
|
||||
@@ -62,11 +83,12 @@ class SQLPool
|
||||
return false;
|
||||
}
|
||||
} elseif (count($this->pool) > 0) {
|
||||
/** @var PDO $con */
|
||||
$con = $this->pool->pop();
|
||||
if ($con->connected !== false) return $con;
|
||||
return $con;
|
||||
} elseif ($this->connect_cnt > 70) {
|
||||
$this->co_list[]=Coroutine::getuid();
|
||||
Console::warning("数据库连接过多,协程等待重复利用中...当前协程数 ".Coroutine::stats()["coroutine_num"]);
|
||||
$this->co_list[] = Coroutine::getuid();
|
||||
Console::warning("数据库连接过多,协程等待重复利用中...当前协程数 " . Coroutine::stats()["coroutine_num"]);
|
||||
Coroutine::suspend();
|
||||
return $this->get($no_new_conn);
|
||||
}
|
||||
@@ -87,15 +109,14 @@ class SQLPool
|
||||
|
||||
private function newConnect() {
|
||||
//无空闲连接,创建新连接
|
||||
$mysql = new Mysql();
|
||||
|
||||
Console::info("创建SQL连接中,当前有" . $this->connect_cnt . "个连接");
|
||||
$res = $mysql->connect($this->info);
|
||||
if ($res == false) {
|
||||
echo $mysql->error . PHP_EOL;
|
||||
$dsn = "mysql:host=" . $this->info["host"] . ";dbname=" . $this->info["database"] . ";charset=utf8";
|
||||
try {
|
||||
$mysql = new PDO($dsn, $this->info["user"], $this->info["password"], array(PDO::ATTR_PERSISTENT => true));
|
||||
} catch (PDOException $e) {
|
||||
Console::error("PDO Error: " . $e->getMessage());
|
||||
return false;
|
||||
} else {
|
||||
return $mysql;
|
||||
}
|
||||
Console::info("创建SQL连接中,当前有" . $this->connect_cnt . "个连接");
|
||||
return $mysql;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
namespace ZM\Utils;
|
||||
|
||||
|
||||
use Framework\Console;
|
||||
use Swlib\Saber;
|
||||
use Swoole\Coroutine\Http\Client;
|
||||
|
||||
@@ -11,18 +12,24 @@ class ZMRequest
|
||||
{
|
||||
/**
|
||||
* 使用Swoole协程客户端发起HTTP GET请求
|
||||
* @version 1.1
|
||||
* 返回请求后的body
|
||||
* 如果请求失败或返回状态不是200,则返回 false
|
||||
* @param $url
|
||||
* @param array $headers
|
||||
* @param array $set
|
||||
* @param bool $return_body
|
||||
* @return bool|string|Client
|
||||
* @version 1.1
|
||||
* 返回请求后的body
|
||||
* 如果请求失败或返回状态不是200,则返回 false
|
||||
*/
|
||||
public static function get($url, $headers = [], $set = [], $return_body = true) {
|
||||
$parse = parse_url($url);
|
||||
$cli = new Client($parse["host"], ($parse["scheme"] == "https" ? 443 : (isset($parse["port"]) ? $parse["port"] : 80)), ($parse["scheme"] == "https" ? true : false));
|
||||
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" ? true : false));
|
||||
$cli->setHeaders($headers);
|
||||
$cli->set($set == [] ? ['timeout' => 15.0] : $set);
|
||||
$cli->get($parse["path"] . (isset($parse["query"]) ? "?" . $parse["query"] : ""));
|
||||
@@ -50,7 +57,13 @@ class ZMRequest
|
||||
*/
|
||||
public static function post($url, array $header, $data, $set = [], $return_body = true) {
|
||||
$parse = parse_url($url);
|
||||
$cli = new Client($parse["host"], ($parse["scheme"] == "https" ? 443 : (isset($parse["port"]) ? $parse["port"] : 80)), ($parse["scheme"] == "https" ? true : false));
|
||||
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" ? true : false));
|
||||
$cli->set($set == [] ? ['timeout' => 15.0] : $set);
|
||||
$cli->setHeaders($header);
|
||||
$cli->post($parse["path"] . (isset($parse["query"]) ? ("?" . $parse["query"]) : ""), $data);
|
||||
@@ -65,6 +78,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
|
||||
* @return Saber
|
||||
@@ -72,4 +96,34 @@ class ZMRequest
|
||||
public static function session($option) {
|
||||
return Saber::session($option);
|
||||
}
|
||||
}
|
||||
|
||||
public static function request($url, $attribute = [], $return_body = true) {
|
||||
$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" ? true : 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -407,6 +407,6 @@ class ZMRobot
|
||||
private function getActionName(string $method) {
|
||||
$prefix = ($this->prefix == self::API_ASYNC ? '_async' : ($this->prefix == self::API_RATE_LIMITED ? '_rate_limited' : ''));
|
||||
$func_name = strtolower(preg_replace('/(?<=[a-z])([A-Z])/', '_$1', $method));
|
||||
return $prefix . $func_name;
|
||||
return $func_name . $prefix;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,9 @@ namespace ZM\Utils;
|
||||
|
||||
use Co;
|
||||
use framework\Console;
|
||||
use Framework\DataProvider;
|
||||
use Framework\ZMBuf;
|
||||
use ZM\API\CQ;
|
||||
|
||||
class ZMUtil
|
||||
{
|
||||
@@ -22,7 +24,7 @@ class ZMUtil
|
||||
|
||||
public static function stop($without_shutdown = false) {
|
||||
Console::info(Console::setColor("Stopping server...", "red"));
|
||||
foreach (ZMBuf::$server->connections as $v) {
|
||||
foreach ((ZMBuf::$server->connections ?? []) as $v) {
|
||||
ZMBuf::$server->close($v);
|
||||
}
|
||||
DataProvider::saveBuffer();
|
||||
@@ -39,6 +41,9 @@ class ZMUtil
|
||||
|
||||
public static function reload() {
|
||||
Console::info(Console::setColor("Reloading server...", "gold"));
|
||||
foreach (ZMBuf::get("wait_api", []) as $k => $v) {
|
||||
if ($v["result"] === null) Co::resume($v["coroutine"]);
|
||||
}
|
||||
foreach (ZMBuf::$server->connections as $v) {
|
||||
ZMBuf::$server->close($v);
|
||||
}
|
||||
@@ -50,28 +55,14 @@ class ZMUtil
|
||||
* 解析CQ码
|
||||
* @param $msg
|
||||
* @return array|null
|
||||
* 0123456
|
||||
* [CQ:at]
|
||||
*/
|
||||
static function getCQ($msg) {
|
||||
if (($start = mb_strpos($msg, '[')) === false) return null;
|
||||
if (($end = mb_strpos($msg, ']')) === false) return null;
|
||||
$msg = mb_substr($msg, $start + 1, $end - $start - 1);
|
||||
if (mb_substr($msg, 0, 3) != "CQ:") return null;
|
||||
$msg = mb_substr($msg, 3);
|
||||
$msg2 = explode(",", $msg);
|
||||
$type = array_shift($msg2);
|
||||
$array = [];
|
||||
foreach ($msg2 as $k => $v) {
|
||||
$ss = explode("=", $v);
|
||||
$sk = array_shift($ss);
|
||||
$array[$sk] = implode("=", $ss);
|
||||
}
|
||||
return ["type" => $type, "params" => $array, "start" => $start, "end" => $end];
|
||||
return CQ::getCQ($msg);
|
||||
}
|
||||
|
||||
public static function getModInstance($class) {
|
||||
if (!isset(ZMBuf::$instance[$class])) {
|
||||
Console::debug("Class instance $class not exist, so I created it.");
|
||||
ZMBuf::$instance[$class] = new $class();
|
||||
return ZMBuf::$instance[$class];
|
||||
} else {
|
||||
|
||||
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