Compare commits

...

13 Commits
1.2 ... 1.3

Author SHA1 Message Date
whale
ec88c56137 update README.md 2020-05-08 18:45:40 +08:00
whale
acb4bdf9b4 update to 1.3.0 version totally. 2020-05-08 16:37:38 +08:00
whale
a1b013ee53 update to 1.3.0 version. 2020-05-06 17:26:50 +08:00
whale
086c65af26 fix some DataProvider bug. 2020-05-06 17:25:09 +08:00
whale
9b3a2e5296 fix some DataProvider bug. 2020-05-06 17:19:59 +08:00
whale
181f6430a4 update CQ.php 2020-05-06 15:01:32 +08:00
whale
d5c192108e update Dockerfile 2020-05-03 16:38:27 +08:00
whale
0c28dda808 update Dockerfile 2020-05-03 12:03:56 +08:00
whale
b61eeccdb8 fix composer requirement 2020-05-03 11:41:05 +08:00
whale
19d618f167 update README.md 2020-05-02 23:36:54 +08:00
whale
17ce0c20ae update SECURITY.md 2020-05-02 23:28:11 +08:00
whale
82a1f86bbd update to 1.2.1
add phar build script
2020-05-02 23:27:26 +08:00
whale
97e579b8a1 update README.md 2020-04-29 18:11:21 +08:00
41 changed files with 1097 additions and 389 deletions

View File

@@ -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
View File

@@ -4,4 +4,5 @@
/vendor/
zm.json
/zm_data/
composer.lock
composer.lock
/resources/server.phar

View File

@@ -11,5 +11,4 @@ ADD . /app/zhamao-framework-bak
#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"]

View File

@@ -2,12 +2,12 @@
[![作者QQ](https://img.shields.io/badge/作者QQ-627577391-orange.svg)]()
[![zhamao License](https://img.shields.io/hexpm/l/plug.svg?maxAge=2592000)](https://github.com/zhamao-robot/zhamao-framework/blob/master/LICENSE)
[![版本](https://img.shields.io/badge/version-1.2-green.svg)]()
[![版本](https://img.shields.io/badge/version-1.3-green.svg)]()
[![stupid counter](https://img.shields.io/github/search/zhamao-robot/zhamao-framework/stupid.svg)](https://github.com/zhamao-robot/zhamao-framework/search?q=stupid)
[![TODO counter](https://img.shields.io/github/search/zhamao-robot/zhamao-framework/TODO.svg)](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,25 @@ zhamao-framework 是一个基于 酷Q 的 PHP Swoole 的机器人框架,它会
除了起到解析消息的作用,炸毛框架 还提供了完整的 WebSocket + HTTP 服务器,你还能用此框架构建出高性能的 API 接口服务器。
## 开始
你可以使用 GitHub `Use This Template` 功能快速将本项目克隆到你的公开或私有仓库,也可以从 `release` 中下载本项目的最新版本到本地私有开发
1. 你可以使用项目`Use this template` 功能将框架克隆到你的公开或私有仓库进行开发
2. 你也可以直接到 **Release** 中下载最新的 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/)
## 特点
- 支持多账号
- 灵活的注解事件绑定机制
- 易用的上下文,模块内随处可用
- 采用模块化编写,功能之间高内聚低耦合
- 常驻内存,全局缓存变量随处使用
- 自带 MySQL 查询器、数据库连接池等数据库连接方案
- 自带 HTTP 服务器、WebSocket 服务器可复用,可以构建属于自己的 HTTP API 接口
- 静态文件服务器
- 支持 phar 一键打包
## 炸毛特色模块
@@ -54,11 +56,10 @@ Pages[https://framework.zhamao.xin/](https://framework.zhamao.xin/)
- [ ] 分词模块(可能会放弃计划,因为目前好用的分词都是其他语言的)
- [ ] HTTP 过滤器、Auth 模块、完整的 MVC 兼容(可能会放弃计划,因为框架主打机器人开发)
- [ ] Redis 连接池或开箱即用的相应功能内置
- [ ] 2.0 版本抛弃 `ModBase` 继承结构,完全使用上下文代替
- [X] 1.3 版本使用上下文代替
- [X] 更好的 Logger稳定和漂亮的控制台输出
- [ ] 日志服务
- [ ] 模块支持 Phar 打包(可能会比较靠后支持)
- [ ] 兼容面向过程的模块编写方案(可能会比较靠后支持)
- [X] 框架支持 Phar 打包(可能会比较靠后支持)
- [ ] 完整的单元测试(如果有需求则尽快开发)
- [X] 静态文件服务器

View File

@@ -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: |

64
bin/phar-build Executable file

File diff suppressed because one or more lines are too long

View File

@@ -3,7 +3,7 @@
"description": "high-performance intelligent assistant",
"minimum-stability": "stable",
"license": "proprietary",
"version": "1.2.0",
"version": "1.3.0",
"authors": [
{
"name": "whale",
@@ -22,7 +22,7 @@
"ext-json": "*",
"ext-posix": "*",
"ext-ctype": "*",
"ext-pcntl": "*"
"nikic/php-parser": "^4.4"
},
"repositories": {
"packagist": {

17
docker_mixed/Dockerfile Normal file
View File

@@ -0,0 +1,17 @@
FROM richardchien/cqhttp:latest
RUN apt-get update && apt-get install -y software-properties-common && \
add-apt-repository ppa:ondrej/php && \
apt-get update && \
apt-get install php7.3 php7.3-dev php7.3-mbstring gcc make openssl \
php7.3-mbstring php7.3-json php7.3-ctype php7.3-curl -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/ && \
phpize7.3 && ./configure --enable-openssl && 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
View File

@@ -0,0 +1,6 @@
#!/bin/bash
unzip master.zip
mv zhamao-framework-master/* zhamao-framework/
cd zhamao-framework
php bin/start

82
phar-starter.php Normal file
View File

@@ -0,0 +1,82 @@
<?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__);
$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') {
//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//这里写你的全局方法");
}
}

View File

@@ -61,14 +61,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 +78,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 +95,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 +107,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);
@@ -173,7 +173,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,6 +207,12 @@ 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:

View File

@@ -0,0 +1,68 @@
<?php
namespace Framework;
class DataProvider
{
public static $buffer_list = [];
public static function getResourceFolder() {
return self::getWorkingDir() . '/resources/';
}
public static function getWorkingDir() {
global $is_phar;
if ($is_phar === true) {
return realpath('.');
} else {
return WORKING_DIR;
}
}
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));
}
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);
}
private static function setJsonData($filename, array $args) {
$pathinfo = pathinfo($filename);
if (!is_dir($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;
}
}

View File

@@ -35,17 +35,20 @@ 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();
if (!isPharMode()) {
define('WORKING_DIR', getcwd());
} else {
echo "Phar mode: " . WORKING_DIR . PHP_EOL;
}
$this->registerAutoloader('classLoader');
Runtime::enableCoroutine();
self::$settings = new GlobalConfig();
ZMBuf::$globals = self::$settings;
if (!self::$settings->success) die("Failed to load global config. Please check config/global.php file");
@@ -65,7 +68,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) {
@@ -91,7 +94,8 @@ 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: " . json_decode(file_get_contents(WORKING_DIR . "/composer.json"), true)["version"] .
"\nworking_dir: ".(isPharMode() ? realpath('.') : WORKING_DIR)
);
global $motd;
echo $motd . PHP_EOL;
@@ -103,18 +107,15 @@ 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("CONFIG_DIR", self::$settings->get("config_dir"));
@@ -128,8 +129,7 @@ class FrameworkLoader
define("ZM_MATCH_SECOND", 3);
}
private function selfCheck()
{
private function selfCheck() {
if (!extension_loaded("swoole")) die("Can not find swoole extension.\n");
//if (!extension_loaded("gd")) die("Can not find gd extension.\n");
if (!extension_loaded("sockets")) die("Can not find sockets extension.\n");
@@ -141,8 +141,7 @@ class FrameworkLoader
return true;
}
public function onWorkerStart(\Swoole\Server $server, $worker_id)
{
public function onWorkerStart(\Swoole\Server $server, $worker_id) {
self::$instance = $this;
self::$run_time = microtime(true);
EventHandler::callSwooleEvent("WorkerStart", $server, $worker_id);

View File

@@ -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;

View File

@@ -1,9 +1,14 @@
<?php
use Framework\Console;
use Framework\DataProvider;
use Framework\ZMBuf;
use ZM\Context\ContextInterface;
function isPharMode() {
return substr(__DIR__, 0, 7) == 'phar://';
}
function classLoader($p) {
$filepath = getClassPath($p);
if ($filepath === null)
@@ -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;
}
}
/**
@@ -152,7 +162,8 @@ 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]);
}
@@ -165,16 +176,19 @@ function context() {
$cid = Co::getCid();
$c_class = ZMBuf::globals("context_class");
if (isset(ZMBuf::$context[$cid])) {
return new $c_class(ZMBuf::$context[$cid], $cid);
return 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 new $c_class($cid);
}
return null;
}
}
function ctx() { return context(); }
function debug($msg) {
if (ZMBuf::$atomics["info_level"]->get() >= 4)
Console::log(date("[H:i:s] ") . "[D] " . $msg, 'gray');

View File

@@ -11,6 +11,7 @@ use ZM\Annotation\Http\RequestMapping;
use ZM\Annotation\Swoole\SwooleEventAt;
use ZM\Connection\CQConnection;
use ZM\ModBase;
use ZM\Utils\ZMUtil;
/**
* Class Hello
@@ -24,32 +25,66 @@ class Hello extends ModBase
* @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() . " 已连接!");
}
/**
* 向机器人发送"你好",即可回复这句话
* @CQCommand("你好")
*/
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")
*/
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();
}
}

View File

@@ -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];
}
}

View File

@@ -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,7 +198,7 @@ class CQAPI
}
/**
* @param WSConnection $connection
* @param CQConnection $connection
* @param $reply
* @param |null $function
* @return bool
@@ -207,7 +207,7 @@ class CQAPI
$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,

View File

@@ -8,7 +8,15 @@ use Framework\{Console, ZMBuf};
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;
@@ -18,7 +26,7 @@ use ZM\Annotation\Swoole\{OnStart, OnTick, SwooleEventAfter, SwooleEventAt};
use ZM\Annotation\Interfaces\Rule;
use ZM\Connection\WSConnection;
use ZM\Http\MiddlewareInterface;
use ZM\Utils\DataProvider;
use Framework\DataProvider;
use ZM\Utils\ZMUtil;
class AnnotationParser
@@ -30,7 +38,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 +46,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 +56,15 @@ 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 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,6 +94,10 @@ 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);
@@ -99,14 +112,14 @@ 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 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];
}
}
}
@@ -299,9 +312,9 @@ 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";
require_once $s;
}
}

View 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;
}

View File

@@ -0,0 +1,38 @@
<?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 = "";
public $level = 20;
/**
* @return mixed
*/
public function getLevel() {
return $this->level;
}
/**
* @param mixed $level
*/
public function setLevel($level) {
$this->level = $level;
}
}

View File

@@ -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;
}
}

View File

@@ -20,4 +20,4 @@ class CQConnection extends WSConnection
public function getType() {
return "qq";
}
}
}

View File

@@ -5,6 +5,7 @@ namespace ZM\Connection;
use Framework\ZMBuf;
use Framework\DataProvider;
class ConnectionManager
{
@@ -67,7 +68,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;
}
}

View File

@@ -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->getFrame()->fd); }
/**
* @return int|null
*/
public function getCid() {
return $this->cid;
}
public function getCid() { return $this->cid; }
/**
* @return ZMRobot|null
@@ -79,4 +68,136 @@ 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; }
/**
* 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":
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]["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]; }
}

View File

@@ -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,74 @@ 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);
/**
* @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();
}

View File

@@ -91,6 +91,7 @@ class DB
if (ZMBuf::get("sql_log") === true) {
$starttime = microtime(true);
}
Console::debug("MySQL: ".$line);
try {
$conn = ZMBuf::$sql_pool->get();
if ($conn === false) {

View File

@@ -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,23 +33,31 @@ 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;
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;
}
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;
@@ -56,12 +66,14 @@ class MessageEvent
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) {
@@ -75,20 +87,25 @@ class MessageEvent
if ($v->match == "" && $v->regexMatch == "") continue;
else {
$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 ($v->regexMatch != "" && ($args = matchArgs($v->regexMatch, context()->getMessage())) !== false) {
$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 +113,47 @@ 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;
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 +161,4 @@ class MessageEvent
public function hasReply() {
return $this->function_call;
}
}
}

View File

@@ -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());
}
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -5,19 +5,35 @@ 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\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":
@@ -43,10 +59,15 @@ class EventHandler
}
break;
case "message":
/** @var Frame $param1 */
/** @var Server $param0 */
$conn = ConnectionManager::get($param1->fd);
set_coroutine_params(["server" => $param0, "frame" => $param1, "connection" => $conn]);
(new MessageEvent($param0, $param1))->onActivate()->onAfter();
break;
case "request":
try {
set_coroutine_params(["request" => $param0, "response" => $param1]);
(new RequestEvent($param0, $param1))->onActivate()->onAfter();
} catch (Exception $e) {
/** @var Response $param1 */
@@ -60,15 +81,24 @@ class EventHandler
}
break;
case "open":
set_coroutine_params(["server" => $param0, "request" => $param1]);
(new WSOpenEvent($param0, $param1))->onActivate()->onAfter();
break;
case "close":
set_coroutine_params(["server" => $param0, "fd" => $param1]);
(new WSCloseEvent($param0, $param1))->onActivate()->onAfter();
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) {
if ($level >= 5) {
Console::warning("Recursive call reached " . $level . " times");
@@ -104,10 +134,10 @@ class EventHandler
public static function callCQResponse($req) {
//Console::info("收到来自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 = [
@@ -116,6 +146,11 @@ class EventHandler
"data" => $data,
"self_id" => $self_id
];
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]);
}
if (($origin["func"] ?? null) !== null) {
call_user_func($origin["func"], $response, $origin["data"]);
} elseif (($origin["coroutine"] ?? false) !== false) {
@@ -127,4 +162,91 @@ 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;
}
$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) {
$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;
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) {
call_user_func_array([$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;
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;
}
}

View File

@@ -39,24 +39,27 @@ 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);
Console::debug("Calling CQ Event from fd=" . $conn->fd);
EventHandler::callCQEvent($data, ConnectionManager::get(context()->getFrame()->fd), 0);
} else
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) {
@@ -76,7 +79,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;

View File

@@ -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
@@ -90,7 +87,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 +116,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;
}
}

View File

@@ -4,11 +4,13 @@
namespace ZM\Event\Swoole;
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\Event\EventHandler;
use ZM\ModBase;
use ZM\ModHandleType;
use ZM\Utils\ZMUtil;
@@ -26,6 +28,7 @@ class WSCloseEvent implements SwooleEvent
/**
* @inheritDoc
* @throws AnnotationException
*/
public function onActivate() {
ZMUtil::checkWait();
@@ -34,9 +37,8 @@ class WSCloseEvent implements SwooleEvent
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;
}
}
return $this;
@@ -44,15 +46,14 @@ class WSCloseEvent implements SwooleEvent
/**
* @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;

View File

@@ -5,6 +5,7 @@ namespace ZM\Event\Swoole;
use Closure;
use Doctrine\Common\Annotations\AnnotationException;
use Framework\ZMBuf;
use Swoole\Http\Request;
use Swoole\WebSocket\Server;
@@ -14,6 +15,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 +42,7 @@ class WSOpenEvent implements SwooleEvent
/**
* @inheritDoc
* @throws AnnotationException
*/
public function onActivate() {
ZMUtil::checkWait();
@@ -59,9 +62,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 +84,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;

View File

@@ -22,10 +22,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 Framework\DataProvider;
use ZM\Utils\SQLPool;
use ZM\Utils\ZMUtil;
@@ -50,7 +49,7 @@ class WorkerStartEvent implements SwooleEvent
*/
public function onActivate(): WorkerStartEvent {
Console::info("Worker启动中");
Process::signal(SIGINT, function (){
Process::signal(SIGINT, function () {
Console::warning("Server interrupted by keyboard.");
ZMUtil::stop(true);
});
@@ -61,16 +60,16 @@ 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);
}
@@ -85,34 +84,31 @@ class WorkerStartEvent implements SwooleEvent
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;
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;
EventHandler::callWithMiddleware($class_name, $v->method, ["server" => $this->server, "worker_id" => $this->worker_id], []);
if (context()->getCache("block_continue") === true) break;
}
}
return $this;
@@ -136,7 +132,7 @@ class WorkerStartEvent implements SwooleEvent
private function loadAllClass() {
//加载phar包
Console::info("加载外部phar包中");
$dir = WORKING_DIR . "/resources/package/";
$dir = DataProvider::getWorkingDir() . "/resources/package/";
if (is_dir($dir)) {
$list = scandir($dir);
unset($list[0], $list[1]);
@@ -147,8 +143,12 @@ class WorkerStartEvent implements SwooleEvent
}
//加载composer类
Console::info("加载composer资源中");
require_once WORKING_DIR . "/vendor/autoload.php";
if (file_exists(DataProvider::getWorkingDir() . "/vendor/autoload.php")) {
Console::info("加载composer资源中");
require_once DataProvider::getWorkingDir() . "/vendor/autoload.php";
} else {
if(isPharMode()) require_once WORKING_DIR . "/vendor/autoload.php";
}
//加载各个模块的注解类,以及反射
Console::info("检索Module中");
@@ -158,8 +158,8 @@ 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";
if (file_exists(DataProvider::getWorkingDir() . "/src/Custom/global_function.php"))
require_once DataProvider::getWorkingDir() . "/src/Custom/global_function.php";
$this->afterCheck();
}
@@ -175,7 +175,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!");
}
}

View File

@@ -67,7 +67,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 +161,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); }
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -6,7 +6,9 @@ namespace ZM\Utils;
use Co;
use framework\Console;
use Framework\DataProvider;
use Framework\ZMBuf;
use ZM\API\CQ;
class ZMUtil
{
@@ -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 {