From 29fa9d86620c5518319a91efa9cc114f73069a67 Mon Sep 17 00:00:00 2001 From: jerry Date: Tue, 3 Nov 2020 21:02:24 +0800 Subject: [PATCH] initial 2.0.0-a4 commit --- composer.json | 8 +- config/console_color.json | 11 +- config/global.php | 30 +- src/Module/Example/Hello.php | 17 +- src/ZM/API/CQ.php | 1 - src/ZM/API/CQAPI.php | 60 ++-- src/ZM/Annotation/AnnotationParser.php | 1 + .../{SwooleEvent.php => OnSwooleEvent.php} | 4 +- src/ZM/Command/PureHttpCommand.php | 16 +- src/ZM/Command/RunServerCommand.php | 26 +- src/ZM/ConsoleApplication.php | 1 - src/ZM/Context/Context.php | 34 +- src/ZM/DB/DB.php | 26 +- src/ZM/DB/SelectBody.php | 11 - src/ZM/DB/Table.php | 8 +- src/ZM/Event/CQ/MessageEvent.php | 169 ---------- src/ZM/Event/CQ/MetaEvent.php | 71 ----- src/ZM/Event/CQ/NoticeEvent.php | 95 ------ src/ZM/Event/CQ/RequestEvent.php | 96 ------ src/ZM/Event/Event.php | 11 - src/ZM/Event/EventDispatcher.php | 9 +- src/ZM/Event/EventHandler.php | 293 ------------------ src/ZM/Event/EventManager.php | 6 +- src/ZM/Event/ServerEventHandler.php | 49 ++- src/ZM/Event/Swoole/MessageEvent.php | 97 ------ src/ZM/Event/Swoole/RequestEvent.php | 186 ----------- src/ZM/Event/Swoole/SwooleEventInterface.php | 20 -- src/ZM/Event/Swoole/WSCloseEvent.php | 70 ----- src/ZM/Event/Swoole/WSOpenEvent.php | 106 ------- src/ZM/Exception/NotInitializedException.php | 12 + src/ZM/Framework.php | 39 ++- src/ZM/Module/QQBot.php | 182 ++++++++++- src/ZM/Store/LightCache.php | 66 +++- src/ZM/Store/LightCacheInside.php | 55 ++++ src/ZM/Store/Lock/SpinLock.php | 44 +++ src/ZM/Store/MySQL/SqlPoolStorage.php | 13 + src/ZM/Store/Redis/Redis.php | 9 - src/ZM/Store/Redis/ZMRedis.php | 47 +++ src/ZM/Store/Redis/ZMRedisPool.php | 37 +++ src/ZM/Store/ZMAtomic.php | 38 +++ src/ZM/Store/ZMBuf.php | 36 --- src/ZM/Utils/CoMessage.php | 80 +++++ src/ZM/Utils/ZMUtil.php | 14 +- src/ZM/Utils/terminal_listener.php | 33 -- src/ZM/global_defines.php | 2 - src/ZM/global_functions.php | 11 +- test/LightCacheTest.php | 9 +- test/ZMTest/Mock/global.php | 5 +- 48 files changed, 794 insertions(+), 1470 deletions(-) rename src/ZM/Annotation/Swoole/{SwooleEvent.php => OnSwooleEvent.php} (93%) delete mode 100644 src/ZM/Event/CQ/MessageEvent.php delete mode 100644 src/ZM/Event/CQ/MetaEvent.php delete mode 100644 src/ZM/Event/CQ/NoticeEvent.php delete mode 100644 src/ZM/Event/CQ/RequestEvent.php delete mode 100644 src/ZM/Event/Event.php delete mode 100644 src/ZM/Event/EventHandler.php delete mode 100644 src/ZM/Event/Swoole/MessageEvent.php delete mode 100644 src/ZM/Event/Swoole/RequestEvent.php delete mode 100644 src/ZM/Event/Swoole/SwooleEventInterface.php delete mode 100644 src/ZM/Event/Swoole/WSCloseEvent.php delete mode 100644 src/ZM/Event/Swoole/WSOpenEvent.php create mode 100644 src/ZM/Exception/NotInitializedException.php create mode 100644 src/ZM/Store/LightCacheInside.php create mode 100644 src/ZM/Store/Lock/SpinLock.php create mode 100644 src/ZM/Store/MySQL/SqlPoolStorage.php delete mode 100644 src/ZM/Store/Redis/Redis.php create mode 100644 src/ZM/Store/Redis/ZMRedis.php create mode 100644 src/ZM/Store/Redis/ZMRedisPool.php create mode 100644 src/ZM/Store/ZMAtomic.php create mode 100644 src/ZM/Utils/CoMessage.php delete mode 100644 src/ZM/Utils/terminal_listener.php diff --git a/composer.json b/composer.json index 85aeec66..7b0b8bce 100644 --- a/composer.json +++ b/composer.json @@ -21,7 +21,6 @@ ], "require": { "php": ">=7.2", - "swoole/ide-helper": "@dev", "ext-mbstring": "*", "doctrine/annotations": "~1.10", "ext-json": "*", @@ -60,12 +59,17 @@ ] }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^9.3", + "swoole/ide-helper": "@dev" }, "repositories": [ { "type": "path", "url": "/Users/jerry/project/git-project/zhamao-console" + }, + { + "type": "path", + "url": "/Users/jerry/project/git-project/zhamao-lock" } ] } diff --git a/config/console_color.json b/config/console_color.json index e398759f..ffb81891 100644 --- a/config/console_color.json +++ b/config/console_color.json @@ -10,11 +10,20 @@ }, "white-term": { "success": "green", - "info": "black", + "info": "", "warning": "yellow", "error": "red", "verbose": "blue", "debug": "gray", "trace": "gray" + }, + "no-color": { + "success": "", + "info": "", + "warning": "", + "error": "", + "verbose": "", + "debug": "", + "trace": "" } } diff --git a/config/global.php b/config/global.php index c594efbc..83379206 100644 --- a/config/global.php +++ b/config/global.php @@ -27,20 +27,20 @@ $config['crash_dir'] = $config['zm_data'] . 'crash/'; /** 对应swoole的server->set参数 */ $config['swoole'] = [ 'log_file' => $config['crash_dir'] . 'swoole_error.log', - 'worker_num' => 8, + 'worker_num' => swoole_cpu_num(), 'dispatch_mode' => 2, - 'max_coroutine' => 30000, + 'max_coroutine' => 300000, //'task_worker_num' => 4, //'task_enable_coroutine' => true ]; /** 轻量字符串缓存,默认开启 */ $config['light_cache'] = [ - "status" => true, - "size" => 2048, //最多允许储存的条数(需要2的倍数) - "max_strlen" => 4096, //单行字符串最大长度(需要2的倍数) + "size" => 1024, //最多允许储存的条数(需要2的倍数) + "max_strlen" => 8192, //单行字符串最大长度(需要2的倍数) "hash_conflict_proportion" => 0.6, //Hash冲突率(越大越好,但是需要的内存更多) - "persistence_path" => $config['zm_data']."_cache.json" + "persistence_path" => $config['zm_data']."_cache.json", + 'auto_save_interval' => 900 ]; /** MySQL数据库连接信息,host留空则启动时不创建sql连接池 */ @@ -60,8 +60,17 @@ $config['sql_config'] = [ 'sql_default_fetch_mode' => PDO::FETCH_ASSOC // added in 1.5.6 ]; -/** CQHTTP连接约定的token */ -$config["access_token"] = ""; +/** Redis连接信息,host留空则启动时不创建Redis连接池 */ +$config['redis_config'] = [ + 'host' => '', + 'port' => 6379, + 'timeout' => 1, + 'db_index' => 0, + 'auth' => '' +]; + +/** onebot连接约定的token */ +$config["access_token"] = ''; /** HTTP服务器固定请求头的返回 */ $config['http_header'] = [ @@ -82,9 +91,6 @@ $config['init_atomics'] = [ /** 终端日志显示等级(0-4) */ $config["info_level"] = 2; -/** 自动保存计时器的缓存保存时间(秒) */ -$config['auto_save_interval'] = 900; - /** 上下文接口类 implemented from ContextInterface */ $config['context_class'] = \ZM\Context\Context::class; @@ -108,7 +114,7 @@ $config['command_register_class'] = [ ]; /** 服务器启用的外部第三方和内部插件 */ -$config['plugins'] = [ +$config['modules'] = [ 'qqbot' => true, // QQ机器人事件解析器,如果取消此项则默认为 true 开启状态,否则你手动填写 false 才会关闭 ]; diff --git a/src/Module/Example/Hello.php b/src/Module/Example/Hello.php index db3b718c..ad387249 100644 --- a/src/Module/Example/Hello.php +++ b/src/Module/Example/Hello.php @@ -2,16 +2,15 @@ namespace Module\Example; +use ZM\Annotation\CQ\CQMetaEvent; +use ZM\Annotation\Swoole\OnSwooleEvent; use ZM\Annotation\Swoole\OnWorkerStart; -use ZM\Annotation\Swoole\OnTick; use ZM\ConnectionManager\ConnectionObject; use ZM\Console\Console; use ZM\Annotation\CQ\CQCommand; -use ZM\Annotation\Http\Middleware; use ZM\Annotation\Http\RequestMapping; -use ZM\Annotation\Swoole\SwooleEvent; use ZM\Store\LightCache; -use ZM\Store\ZMBuf; +use ZM\Store\Lock\SpinLock; use ZM\Utils\ZMUtil; /** @@ -23,7 +22,7 @@ class Hello { /** * 在机器人连接后向终端输出信息 - * @SwooleEvent("open",rule="connectIsQQ()") + * @OnSwooleEvent("open",rule="connectIsQQ()") * @param $conn */ public function onConnect(ConnectionObject $conn) { @@ -32,7 +31,7 @@ class Hello /** * 在机器人断开连接后向终端输出信息 - * @SwooleEvent("close",rule="connectIsQQ()") + * @OnSwooleEvent("close",rule="connectIsQQ()") * @param ConnectionObject $conn */ public function onDisconnect(ConnectionObject $conn) { @@ -59,6 +58,7 @@ class Hello * @CQCommand("随机数") * @CQCommand(regexMatch="*从*到*的随机数") * @param $arg + * @return string */ public function randNum($arg) { // 获取第一个数字类型的参数 @@ -68,7 +68,7 @@ class Hello $a = min(intval($num1), intval($num2)); $b = max(intval($num1), intval($num2)); // 回复用户结果 - ctx()->reply("随机数是:" . mt_rand($a, $b)); + return "随机数是:" . mt_rand($a, $b); } /** @@ -76,7 +76,6 @@ class Hello * @RequestMapping("/httpTimer") */ public function timer() { - ZMBuf::atomic("_tmp_2")->add(1); return "This page is used as testing TimerMiddleware! Do not use it in production."; } @@ -101,7 +100,7 @@ class Hello /** * 框架会默认关闭未知的WebSocket链接,因为这个绑定的事件,你可以根据你自己的需求进行修改 - * @SwooleEvent(type="open",rule="connectIsDefault()") + * @OnSwooleEvent(type="open",rule="connectIsDefault()") */ public function closeUnknownConn() { Console::info("Unknown connection , I will close it."); diff --git a/src/ZM/API/CQ.php b/src/ZM/API/CQ.php index 346fa8ce..61191446 100644 --- a/src/ZM/API/CQ.php +++ b/src/ZM/API/CQ.php @@ -5,7 +5,6 @@ namespace ZM\API; use ZM\Console\Console; -use ZM\Utils\ZMUtil; class CQ { diff --git a/src/ZM/API/CQAPI.php b/src/ZM/API/CQAPI.php index 751a364a..d45540fd 100644 --- a/src/ZM/API/CQAPI.php +++ b/src/ZM/API/CQAPI.php @@ -6,9 +6,9 @@ namespace ZM\API; use Co; use ZM\ConnectionManager\ConnectionObject; use ZM\Console\Console; -use ZM\Event\EventHandler; -use ZM\Store\LightCache; -use ZM\Store\ZMBuf; +use ZM\Store\LightCacheInside; +use ZM\Store\Lock\SpinLock; +use ZM\Store\ZMAtomic; trait CQAPI { @@ -28,30 +28,28 @@ trait CQAPI } public function processWebsocketAPI($connection, $reply, $function = false) { - $api_id = ZMBuf::atomic("wait_msg_id")->get(); + $api_id = ZMAtomic::get("wait_msg_id")->add(1); $reply["echo"] = $api_id; - ZMBuf::atomic("wait_msg_id")->add(1); - EventHandler::callCQAPISend($reply, $connection); - if ($function === true) { - LightCache::set("sent_api_".$api_id, [ - "data" => $reply, - "time" => microtime(true), - "coroutine" => Co::getuid(), - "self_id" => $connection->getOption("connect_id") - ]); - } else { - LightCache::set("sent_api_".$api_id, [ - "data" => $reply, - "time" => microtime(true), - "self_id" => $connection->getOption("connect_id") - ]); - } - + //EventHandler::callCQAPISend($reply, $connection); + SpinLock::lock("wait_api"); + $r = LightCacheInside::get("wait_api", "wait_api"); + $r[$api_id] = [ + "data" => $reply, + "time" => microtime(true), + "self_id" => $connection->getOption("connect_id") + ]; + if ($function === true) $r[$api_id]["coroutine"] = Co::getuid(); + LightCacheInside::set("wait_api", "wait_api", $r); + SpinLock::unlock("wait_api"); if (server()->push($connection->getFd(), json_encode($reply))) { if ($function === true) { Co::suspend(); - $data = LightCache::get("sent_api_".$api_id); - LightCache::unset("sent_api_".$api_id); + SpinLock::lock("wait_api"); + $r = LightCacheInside::get("wait_api", "wait_api"); + $data = $r[$api_id]; + unset($r[$api_id]); + LightCacheInside::set("wait_api", "wait_api", $r); + SpinLock::unlock("wait_api"); return isset($data['result']) ? $data['result'] : null; } return true; @@ -63,15 +61,23 @@ trait CQAPI "data" => null, "self_id" => $connection->getOption("connect_id") ]; - $s = LightCache::get("sent_api_".$reply["echo"]); - if (($s["func"] ?? null) !== null) - call_user_func($s["func"], $response, $reply); - LightCache::unset("sent_api_".$reply["echo"]); + SpinLock::lock("wait_api"); + $r = LightCacheInside::get("wait_api", "wait_api"); + unset($r[$reply["echo"]]); + LightCacheInside::set("wait_api", "wait_api", $r); + SpinLock::unlock("wait_api"); if ($function === true) return $response; return false; } } + /** + * @param $connection + * @param $reply + * @param null $function + * @return bool + * @noinspection PhpUnusedParameterInspection + */ public function processHttpAPI($connection, $reply, $function = null) { return false; } diff --git a/src/ZM/Annotation/AnnotationParser.php b/src/ZM/Annotation/AnnotationParser.php index e5f40070..6bcb7eaf 100644 --- a/src/ZM/Annotation/AnnotationParser.php +++ b/src/ZM/Annotation/AnnotationParser.php @@ -201,6 +201,7 @@ class AnnotationParser $array[0]['method'] = $method; $array[0]['class'] = $class; $array[0]['request_method'] = $vss->request_method; + $array[0]['route'] = $vss->route; $this->req_mapping = $array; return; } diff --git a/src/ZM/Annotation/Swoole/SwooleEvent.php b/src/ZM/Annotation/Swoole/OnSwooleEvent.php similarity index 93% rename from src/ZM/Annotation/Swoole/SwooleEvent.php rename to src/ZM/Annotation/Swoole/OnSwooleEvent.php index 105125a4..d71f467b 100644 --- a/src/ZM/Annotation/Swoole/SwooleEvent.php +++ b/src/ZM/Annotation/Swoole/OnSwooleEvent.php @@ -10,12 +10,12 @@ use ZM\Annotation\Interfaces\Level; use ZM\Annotation\Interfaces\Rule; /** - * Class SwooleEvent + * Class OnSwooleEvent * @Annotation * @Target("ALL") * @package ZM\Annotation\Swoole */ -class SwooleEvent extends AnnotationBase implements Rule, Level +class OnSwooleEvent extends AnnotationBase implements Rule, Level { /** * @var string diff --git a/src/ZM/Command/PureHttpCommand.php b/src/ZM/Command/PureHttpCommand.php index d30ce4be..99c9114c 100644 --- a/src/ZM/Command/PureHttpCommand.php +++ b/src/ZM/Command/PureHttpCommand.php @@ -17,7 +17,7 @@ use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use ZM\Config\ZMConfig; use ZM\Console\Console; -use ZM\Store\ZMBuf; +use ZM\Store\ZMAtomic; use ZM\Utils\HttpUtil; class PureHttpCommand extends Command @@ -28,7 +28,7 @@ class PureHttpCommand extends Command protected function configure() { $this->setDescription("Run a simple http server | 启动一个简单的文件 HTTP 服务器"); $this->setHelp("直接运行可以启动"); - $this->addArgument('dir', InputArgument::OPTIONAL, 'Your directory'); + $this->addArgument('dir', InputArgument::REQUIRED, 'Your directory'); $this->addOption("host", 'H', InputOption::VALUE_REQUIRED, "启动监听地址"); $this->addOption("port", 'P', InputOption::VALUE_REQUIRED, "启动监听地址的端口"); // ... @@ -36,19 +36,23 @@ class PureHttpCommand extends Command protected function execute(InputInterface $input, OutputInterface $output) { $tty_width = explode(" ", trim(exec("stty size")))[1]; + if(realpath($input->getArgument('dir') ?? '.') === false) { + $output->writeln("Directory error(".($input->getArgument('dir') ?? '.')."): no such file or directory."); + return self::FAILURE; + } $global = ZMConfig::get("global"); $host = $input->getOption("host") ?? $global["host"]; $port = $input->getOption("port") ?? $global["port"]; $server = new Server($host, $port); $server->set(ZMConfig::get("global", "swoole")); Console::init(0, $server); - ZMBuf::$atomics["request"] = []; + ZMAtomic::$atomics["request"] = []; for ($i = 0; $i < 32; ++$i) { - ZMBuf::$atomics["request"][$i] = new Atomic(0); + ZMAtomic::$atomics["request"][$i] = new Atomic(0); } $index = ["index.html", "index.htm"]; $server->on("request", function (Request $request, Response $response) use ($input, $index, $server) { - ZMBuf::$atomics["request"][$server->worker_id]->add(1); + ZMAtomic::$atomics["request"][$server->worker_id]->add(1); HttpUtil::handleStaticPage( $request->server["request_uri"], $response, @@ -62,7 +66,7 @@ class PureHttpCommand extends Command Process::signal(SIGINT, function () use ($server) { Console::warning("Server interrupted by keyboard."); for ($i = 0; $i < 32; ++$i) { - $num = ZMBuf::$atomics["request"][$i]->get(); + $num = ZMAtomic::$atomics["request"][$i]->get(); if($num != 0) echo "[$i]: ".$num."\n"; } diff --git a/src/ZM/Command/RunServerCommand.php b/src/ZM/Command/RunServerCommand.php index 9ffd6614..2f063f8a 100644 --- a/src/ZM/Command/RunServerCommand.php +++ b/src/ZM/Command/RunServerCommand.php @@ -15,19 +15,23 @@ class RunServerCommand extends Command protected static $defaultName = 'server'; protected function configure() { + $this->setDefinition([ + new InputOption("debug-mode", "D", null, "开启调试模式 (这将关闭协程化)"), + new InputOption("log-debug", null, null, "调整消息等级到debug (log-level=4)"), + new InputOption("log-verbose", null, null, "调整消息等级到verbose (log-level=3)"), + new InputOption("log-info", null, null, "调整消息等级到info (log-level=2)"), + new InputOption("log-warning", null, null, "调整消息等级到warning (log-level=1)"), + new InputOption("log-error", null, null, "调整消息等级到error (log-level=0)"), + new InputOption("log-theme", null, InputOption::VALUE_REQUIRED, "改变终端的主题配色"), + new InputOption("disable-console-input", null, null, "禁止终端输入内容 (后台服务时需要)"), + new InputOption("disable-coroutine", null, null, "关闭协程Hook"), + new InputOption("daemon", null, null, "以守护进程的方式运行框架"), + new InputOption("watch", null, null, "监听 src/ 目录的文件变化并热更新"), + new InputOption("env", null, InputOption::VALUE_REQUIRED, "设置环境类型 (production, development, staging)"), + ]); $this->setDescription("Run zhamao-framework | 启动框架"); $this->setHelp("直接运行可以启动"); - $this->addOption("debug-mode", "D", null, "开启调试模式 (这将关闭协程化)"); - $this->addOption("log-debug", null, null, "调整消息等级到debug (log-level=4)"); - $this->addOption("log-verbose", null, null, "调整消息等级到verbose (log-level=3)"); - $this->addOption("log-info", null, null, "调整消息等级到info (log-level=2)"); - $this->addOption("log-warning", null, null, "调整消息等级到warning (log-level=1)"); - $this->addOption("log-error", null, null, "调整消息等级到error (log-level=0)"); - $this->addOption("log-theme", null, InputOption::VALUE_REQUIRED, "改变终端的主题配色"); - $this->addOption("disable-console-input", null, null, "禁止终端输入内容 (后台服务时需要)"); - $this->addOption("disable-coroutine", null, null, "关闭协程Hook"); - $this->addOption("watch", null, null, "监听 src/ 目录的文件变化并热更新"); - $this->addOption("env", null, InputOption::VALUE_REQUIRED, "设置环境类型 (production, development, staging)"); + // ... } diff --git a/src/ZM/ConsoleApplication.php b/src/ZM/ConsoleApplication.php index 67ea073d..dd6bb32b 100644 --- a/src/ZM/ConsoleApplication.php +++ b/src/ZM/ConsoleApplication.php @@ -31,7 +31,6 @@ class ConsoleApplication extends Application new PureHttpCommand() ]); //if (LOAD_MODE === 0) $this->add(new BuildCommand()); //只有在git源码模式才能使用打包指令 - 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; diff --git a/src/ZM/Context/Context.php b/src/ZM/Context/Context.php index f7d0ed0e..f721fdd2 100644 --- a/src/ZM/Context/Context.php +++ b/src/ZM/Context/Context.php @@ -14,8 +14,10 @@ use ZM\Exception\InvalidArgumentException; use ZM\Exception\WaitTimeoutException; use ZM\Http\Response; use ZM\API\ZMRobot; -use ZM\Store\LightCache; -use ZM\Store\ZMBuf; +use ZM\Store\LightCacheInside; +use ZM\Store\Lock\SpinLock; +use ZM\Store\ZMAtomic; +use ZM\Utils\CoMessage; class Context implements ContextInterface { @@ -137,12 +139,19 @@ class Context implements ContextInterface * @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("协程等待参数缺失"); + + + if ($prompt != "") $this->reply($prompt); + + $r = CoMessage::yieldByWS($this->getData(), ["user_id", "self_id", "message_type", onebot_target_id_name($this->getMessageType())]); + if($r === false) { + throw new WaitTimeoutException($this, $timeout_prompt); + } + $cid = Co::getuid(); - $api_id = ZMBuf::atomic("wait_msg_id")->get(); - ZMBuf::atomic("wait_msg_id")->add(1); + $api_id = ZMAtomic::get("wait_msg_id")->add(1); $hang = [ "coroutine" => $cid, "user_id" => $this->getData()["user_id"], @@ -154,17 +163,24 @@ class Context implements ContextInterface if ($hang["message_type"] == "group" || $hang["message_type"] == "discuss") { $hang[$hang["message_type"] . "_id"] = $this->getData()[$this->getData()["message_type"] . "_id"]; } - LightCache::set("wait_api_".$api_id, $hang); + SpinLock::lock("wait_api"); + $hw = LightCacheInside::get("wait_api", "wait_api") ?? []; + $hw[$api_id] = $hang; + LightCacheInside::set("wait_api", "wait_api", $hw); + SpinLock::unlock("wait_api"); $id = swoole_timer_after($timeout * 1000, function () use ($api_id, $timeout_prompt) { - $r = LightCache::get("wait_api_".$api_id); + $r = LightCacheInside::get("wait_api", "wait_api")[$api_id] ?? null; if (is_array($r)) { Co::resume($r["coroutine"]); } }); Co::suspend(); - $sess = LightCache::get("wait_api_".$api_id); - LightCache::unset("wait_api_".$api_id); + SpinLock::lock("wait_api"); + $hw = LightCacheInside::get("wait_api", "wait_api") ?? []; + $sess = $hw[$api_id]; + unset($hw[$api_id]); + LightCacheInside::set("wait_api", "wait_api", $hw); $result = $sess["result"]; if (isset($id)) swoole_timer_clear($id); if ($result === null) throw new WaitTimeoutException($this, $timeout_prompt); diff --git a/src/ZM/DB/DB.php b/src/ZM/DB/DB.php index 9163c44d..0dd1c35d 100644 --- a/src/ZM/DB/DB.php +++ b/src/ZM/DB/DB.php @@ -7,7 +7,7 @@ namespace ZM\DB; use Exception; use ZM\Config\ZMConfig; use ZM\Console\Console; -use ZM\Store\ZMBuf; +use ZM\Store\MySQL\SqlPoolStorage; use PDOException; use PDOStatement; use Swoole\Database\PDOStatementProxy; @@ -35,11 +35,11 @@ class DB * @return Table * @throws DbException */ - public static function table($table_name, $enable_cache = null) { + public static function table($table_name) { if (Table::getTableInstance($table_name) === null) { if (in_array($table_name, self::$table_list)) - return new Table($table_name, $enable_cache ?? ZMConfig::get("global", "sql_config")["sql_enable_cache"]); - elseif(ZMBuf::$sql_pool !== null){ + return new Table($table_name); + elseif(SqlPoolStorage::$sql_pool !== null){ throw new DbException("Table " . $table_name . " not exist in database."); } else { throw new DbException("Database connection not exist or connect failed. Please check sql configuration"); @@ -63,13 +63,13 @@ class DB */ public static function unprepared($line) { try { - $conn = ZMBuf::$sql_pool->get(); + $conn = SqlPoolStorage::$sql_pool->get(); if ($conn === false) { - ZMBuf::$sql_pool->put(null); + SqlPoolStorage::$sql_pool->put(null); throw new DbException("无法连接SQL!" . $line); } $result = $conn->query($line) === false ? false : true; - ZMBuf::$sql_pool->put($conn); + SqlPoolStorage::$sql_pool->put($conn); return $result; } catch (DBException $e) { Console::warning($e->getMessage()); @@ -87,19 +87,19 @@ class DB public static function rawQuery(string $line, $params = [], $fetch_mode = ZM_DEFAULT_FETCH_MODE) { Console::debug("MySQL: ".$line." | ". implode(", ", $params)); try { - $conn = ZMBuf::$sql_pool->get(); + $conn = SqlPoolStorage::$sql_pool->get(); if ($conn === false) { - ZMBuf::$sql_pool->put(null); + SqlPoolStorage::$sql_pool->put(null); throw new DbException("无法连接SQL!" . $line); } $ps = $conn->prepare($line); if ($ps === false) { - ZMBuf::$sql_pool->put(null); + SqlPoolStorage::$sql_pool->put(null); throw new DbException("SQL语句查询错误," . $line . ",错误信息:" . $conn->error); } else { if (!($ps instanceof PDOStatement) && !($ps instanceof PDOStatementProxy)) { var_dump($ps); - ZMBuf::$sql_pool->put(null); + SqlPoolStorage::$sql_pool->put(null); throw new DbException("语句查询错误!返回的不是 PDOStatement" . $line); } if ($params == []) $result = $ps->execute(); @@ -107,11 +107,11 @@ class DB $result = $ps->execute([$params]); } else $result = $ps->execute($params); if ($result !== true) { - ZMBuf::$sql_pool->put(null); + SqlPoolStorage::$sql_pool->put(null); throw new DBException("语句[$line]错误!" . $ps->errorInfo()[2]); //echo json_encode(debug_backtrace(), 128 | 256); } - ZMBuf::$sql_pool->put($conn); + SqlPoolStorage::$sql_pool->put($conn); return $ps->fetchAll($fetch_mode); } } catch (DbException $e) { diff --git a/src/ZM/DB/SelectBody.php b/src/ZM/DB/SelectBody.php index a7c882af..903916db 100644 --- a/src/ZM/DB/SelectBody.php +++ b/src/ZM/DB/SelectBody.php @@ -4,7 +4,6 @@ namespace ZM\DB; -use ZM\Console\Console; use ZM\Exception\DbException; class SelectBody @@ -46,17 +45,7 @@ class SelectBody * @throws DbException */ 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::debug('SQL query cached: ' . $rr); - return $this->table->cache[$rr]->getResult(); - } - } $this->execute($fetch_mode); - if ($this->table->isCacheEnabled() && !in_array($rr, $this->table->cache)) { - $this->table->cache[$rr] = $this; - } return $this->getResult(); } diff --git a/src/ZM/DB/Table.php b/src/ZM/DB/Table.php index cf85d4cd..939bc8ad 100644 --- a/src/ZM/DB/Table.php +++ b/src/ZM/DB/Table.php @@ -14,10 +14,7 @@ class Table private static $table_instance = []; - private $enable_cache; - - public function __construct($table_name, $enable_cache) { - $this->enable_cache = $enable_cache; + public function __construct($table_name) { $this->table_name = $table_name; self::$table_instance[$table_name] = $this; } @@ -75,5 +72,4 @@ class Table */ public function getTableName() { return $this->table_name; } - public function isCacheEnabled() { return $this->enable_cache; } -} \ No newline at end of file +} diff --git a/src/ZM/Event/CQ/MessageEvent.php b/src/ZM/Event/CQ/MessageEvent.php deleted file mode 100644 index d128af8e..00000000 --- a/src/ZM/Event/CQ/MessageEvent.php +++ /dev/null @@ -1,169 +0,0 @@ -data = $data; - $this->connection = $conn_or_response; - $this->circle = $circle; - } - - /** - * @return bool - * @throws AnnotationException - */ - public function onBefore() { - $dispatcher = new EventDispatcher(CQBefore::class . "::message"); - $dispatcher->setRuleFunction(function ($v) { - if($v->level < 200) EventDispatcher::interrupt(); - return true; - }); - $dispatcher->setReturnFunction(function($result){ - if(!$result) EventDispatcher::interrupt(); - }); - $dispatcher->dispatchEvents(); - - foreach (ZMBuf::get("wait_api", []) as $k => $v) { - if(zm_data_hash(ctx()->getData()) == $v["hash"]) { - $v["result"] = context()->getData()["message"]; - ZMBuf::appendKey("wait_api", $k, $v); - Co::resume($v["coroutine"]); - return false; - } - 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 (EventManager::$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 - */ - public function onActivate() { - try { - $word = split_explode(" ", str_replace("\r", "", context()->getMessage())); - if (count(explode("\n", $word[0])) >= 2) { - $enter = explode("\n", context()->getMessage()); - $first = split_explode(" ", array_shift($enter)); - $word = array_merge($first, $enter); - foreach ($word as $k => $v) { - $word[$k] = trim($word[$k]); - } - } - - //分发CQCommand事件 - $dispatcher = new EventDispatcher(CQCommand::class); - $dispatcher->setRuleFunction(function ($v) use ($word) { - if ($v->match == "" && $v->regexMatch == "" && $v->fullMatch == "") return false; - elseif (($v->user_id == 0 || ($v->user_id != 0 && $v->user_id == ctx()->getUserId())) && - ($v->group_id == 0 || ($v->group_id != 0 && $v->group_id == (ctx()->getGroupId() ?? 0))) && - ($v->message_type == '' || ($v->message_type != '' && $v->message_type == ctx()->getMessageType())) - ) { - if (($word[0] != "" && $v->match == $word[0]) || - in_array($word[0], $v->alias) || - ($v->regexMatch != "" && ($args = matchArgs($v->regexMatch, ctx()->getMessage())) !== false) || - ($v->fullMatch != "" && (preg_match("/" . $v->fullMatch . "/u", ctx()->getMessage(), $args)) != 0)) { - return true; - } - } - return false; - }); - $dispatcher->setReturnFunction(function ($result) { - if (is_string($result)) ctx()->reply($result); - EventDispatcher::interrupt(); - }); - $r = $dispatcher->dispatchEvents($word); - if ($r === null) return; - - //分发CQMessage事件 - $msg_dispatcher = new EventDispatcher(CQMessage::class); - $msg_dispatcher->setRuleFunction(function ($v) { - return ($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->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"])); - }); - $msg_dispatcher->setReturnFunction(function ($result) { - if (is_string($result)) ctx()->reply($result); - }); - $msg_dispatcher->dispatchEvents(ctx()->getMessage()); - } 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; - 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; - } - - public function hasReply() { - return $this->function_call; - } -} diff --git a/src/ZM/Event/CQ/MetaEvent.php b/src/ZM/Event/CQ/MetaEvent.php deleted file mode 100644 index 78a7671e..00000000 --- a/src/ZM/Event/CQ/MetaEvent.php +++ /dev/null @@ -1,71 +0,0 @@ -data = $data; - $this->connection = $connection; - $this->circle = $circle; - } - - /** - * @return bool - * @throws AnnotationException - */ - public function onBefore() { - foreach (ZMBuf::$events[CQBefore::class]["meta_event"] ?? [] as $v) { - $c = $v->class; - 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 { - $obj = []; - foreach (ZMBuf::$events[CQMetaEvent::class] ?? [] as $v) { - /** @var CQMetaEvent $v */ - if ( - ($v->meta_event_type == '' || ($v->meta_event_type != '' && $v->meta_event_type == $this->data["meta_event_type"])) && - ($v->sub_type == 0 || ($v->sub_type != 0 && $v->sub_type == $this->data["sub_type"]))) { - $c = $v->class; - if (!isset($obj[$c])) - $obj[$c] = new $c(); - 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) { - $e->module->finalReply($e->getMessage()); - } - } -} diff --git a/src/ZM/Event/CQ/NoticeEvent.php b/src/ZM/Event/CQ/NoticeEvent.php deleted file mode 100644 index 4ff05dcf..00000000 --- a/src/ZM/Event/CQ/NoticeEvent.php +++ /dev/null @@ -1,95 +0,0 @@ -data = $data; - $this->connection = $connection; - $this->circle = $circle; - } - - /** - * @return bool - * @throws AnnotationException - */ - public function onBefore() { - foreach (ZMBuf::$events[CQBefore::class]["notice"] ?? [] as $v) { - $c = $v->class; - 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 { - $obj = []; - foreach (ZMBuf::$events[CQNotice::class] ?? [] as $v) { - /** @var CQNotice $v */ - if ( - ($v->notice_type == '' || ($v->notice_type != '' && $v->notice_type == $this->data["notice_type"])) && - ($v->sub_type == 0 || ($v->sub_type != 0 && $v->sub_type == $this->data["sub_type"])) && - ($v->group_id == 0 || ($v->group_id != 0 && $v->group_id == ($this->data["group_id"] ?? 0))) && - ($v->operator_id == 0 || ($v->operator_id != 0 && $v->operator_id == ($this->data["operator_id"] ?? 0)))) { - $c = $v->class; - if (!isset($obj[$c])) - $obj[$c] = new $c(); - 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) { - $e->module->finalReply($e->getMessage()); - } - } - - /** - * @return bool - * @throws AnnotationException - */ - public function onAfter() { - foreach (ZMBuf::$events[CQAfter::class]["notice"] ?? [] as $v) { - $c = $v->class; - 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; - } -} diff --git a/src/ZM/Event/CQ/RequestEvent.php b/src/ZM/Event/CQ/RequestEvent.php deleted file mode 100644 index c600fb77..00000000 --- a/src/ZM/Event/CQ/RequestEvent.php +++ /dev/null @@ -1,96 +0,0 @@ -data = $data; - $this->connection = $connection; - $this->circle = $circle; - } - - /** - * @return bool - * @throws AnnotationException - */ - public function onBefore() { - foreach (ZMBuf::$events[CQBefore::class]["request"] ?? [] as $v) { - $c = $v->class; - 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 - * @noinspection PhpRedundantCatchClauseInspection - */ - public function onActivate() { - try { - $obj = []; - foreach (ZMBuf::$events[CQRequest::class] ?? [] as $v) { - /** @var CQRequest $v */ - if ( - ($v->request_type == '' || ($v->request_type != '' && $v->request_type == $this->data["request_type"])) && - ($v->sub_type == 0 || ($v->sub_type != 0 && $v->sub_type == $this->data["sub_type"])) && - ($v->user_id == 0 || ($v->user_id != 0 && $v->user_id == ($this->data["user_id"] ?? 0))) && - ($v->comment == 0 || ($v->comment != 0 && $v->comment == ($this->data["comment"] ?? 0)))) { - $c = $v->class; - if (!isset($obj[$c])) - $obj[$c] = new $c(); - 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()); - } - } - - /** - * @return bool - * @throws AnnotationException - */ - public function onAfter() { - foreach (ZMBuf::$events[CQAfter::class]["request"] ?? [] as $v) { - $c = $v->class; - 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; - } -} diff --git a/src/ZM/Event/Event.php b/src/ZM/Event/Event.php deleted file mode 100644 index f78aff33..00000000 --- a/src/ZM/Event/Event.php +++ /dev/null @@ -1,11 +0,0 @@ -class] ?? [] as $v) { + + foreach ((EventManager::$events[$this->class] ?? []) as $v) { + if($this->class == CQMetaEvent::class) { + //eval(BP); + } $result = $this->dispatchEvent($v, $this->rule, ...$params); - if (is_callable($this->return_func)) ($this->return_func)($result); + if ($result !== false && is_callable($this->return_func)) ($this->return_func)($result); } return true; } catch (InterruptException $e) { diff --git a/src/ZM/Event/EventHandler.php b/src/ZM/Event/EventHandler.php deleted file mode 100644 index 6636d2e5..00000000 --- a/src/ZM/Event/EventHandler.php +++ /dev/null @@ -1,293 +0,0 @@ -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::trace(); - } - break; - case "request": - try { - set_coroutine_params(["request" => $param0, "response" => $param1]); - (new RequestEvent($param0, $param1))->onActivate()->onAfter(); - } catch (Exception $e) { - /** @var Response $param1 */ - $param1->status(500); - Console::info($param0->server["remote_addr"] . ":" . $param0->server["remote_port"] . - " [" . $param1->getStatusCode() . "] " . $param0->server["request_uri"] - ); - if (!$param1->isEnd()) { - if (ZMConfig::get("global", "debug_mode")) - $param1->end("Internal server error: " . $e->getMessage()); - else - $param1->end("Internal server error."); - } - 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
"; - $error_msg = $e->getMessage() . " at " . $e->getFile() . "(" . $e->getLine() . ")"; - if (Console::getLevel() >= 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": - /** @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::trace(); - } - break; - case "close": - 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::trace(); - } - 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::trace(); - return false; - } - $starttime = microtime(true); - switch ($event_data["post_type"]) { - case "message": - $event = new CQ\MessageEvent($event_data, $conn_or_response, $level); - if ($event->onBefore()) $event->onActivate(); - $event->onAfter(); - return $event->hasReply(); - break; - case "notice": - $event = new CQ\NoticeEvent($event_data, $conn_or_response, $level); - if ($event->onBefore()) $event->onActivate(); - $event->onAfter(); - return true; - case "request": - $event = new CQ\RequestEvent($event_data, $conn_or_response, $level); - if ($event->onBefore()) $event->onActivate(); - $event->onAfter(); - return true; - case "meta_event": - $event = new CQ\MetaEvent($event_data, $conn_or_response, $level); - if ($event->onBefore()) $event->onActivate(); - return true; - } - unset($starttime); - return false; - } - - /** - * @param $req - * @throws AnnotationException - */ - public static function callCQResponse($req) { - Console::debug("收到来自API连接的回复:" . json_encode($req, 128 | 256)); - $status = $req["status"]; - $retcode = $req["retcode"]; - $data = $req["data"]; - if (isset($req["echo"]) && LightCache::isset("sent_api_" . $req["echo"])) { - $origin = LightCache::get("sent_api_" . $req["echo"]); - $self_id = $origin["self_id"]; - $response = [ - "status" => $status, - "retcode" => $retcode, - "data" => $data, - "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) { - $r = LightCache::get("sent_api_" . $req["echo"]); - $r["result"] = $response; - LightCache::set("sent_api_" . $req["echo"], $r); - Co::resume($origin['coroutine']); - } - LightCache::unset("sent_api_" . $req["echo"]); - } - } - - public static function callCQAPISend($reply, ?ConnectionObject $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->getOption('connect_id')]); - 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"]; - //var_dump($middleware_obj); - $r[$k] = new $before(); - $r[$k]->class = is_object($c) ? get_class($c) : $c; - $r[$k]->method = $method; - if (isset($middleware_obj["before"])) { - $rs = $middleware_obj["before"]; - $before_result = $r[$k]->$rs(...$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 = $class->$method(...$func_args); - if (is_callable($after_call)) - $return_value = $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])) { - $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; - } -} diff --git a/src/ZM/Event/EventManager.php b/src/ZM/Event/EventManager.php index e6546abc..3fea9f3e 100644 --- a/src/ZM/Event/EventManager.php +++ b/src/ZM/Event/EventManager.php @@ -11,7 +11,7 @@ use ZM\Annotation\AnnotationBase; use ZM\Annotation\AnnotationParser; use ZM\Annotation\Swoole\OnTick; use ZM\Console\Console; -use ZM\Store\ZMBuf; +use ZM\Store\ZMAtomic; class EventManager { @@ -20,7 +20,7 @@ class EventManager public static $middlewares = []; public static $req_mapping = []; - public static function addEvent($event_name, AnnotationBase $event_obj) { + public static function addEvent($event_name, ?AnnotationBase $event_obj) { self::$events[$event_name][] = $event_obj; } @@ -43,7 +43,7 @@ class EventManager Console::debug("Added Middleware-based timer: " . $plain_class . " -> " . $vss->method); Timer::tick($vss->tick_ms, function () use ($vss, $dispatcher) { set_coroutine_params([]); - if (ZMBuf::atomic("stop_signal")->get() != 0) { + if (ZMAtomic::get("stop_signal")->get() != 0) { Timer::clearAll(); return; } diff --git a/src/ZM/Event/ServerEventHandler.php b/src/ZM/Event/ServerEventHandler.php index 4551fedc..a7ebcebd 100644 --- a/src/ZM/Event/ServerEventHandler.php +++ b/src/ZM/Event/ServerEventHandler.php @@ -17,7 +17,7 @@ use Swoole\Timer; use ZM\Annotation\AnnotationParser; use ZM\Annotation\Http\RequestMapping; use ZM\Annotation\Swoole\OnWorkerStart; -use ZM\Annotation\Swoole\SwooleEvent; +use ZM\Annotation\Swoole\OnSwooleEvent; use ZM\Config\ZMConfig; use ZM\ConnectionManager\ManagerGM; use ZM\Console\Console; @@ -33,6 +33,8 @@ use ZM\Exception\DbException; use ZM\Framework; use ZM\Http\Response; use ZM\Module\QQBot; +use ZM\Store\MySQL\SqlPoolStorage; +use ZM\Store\Redis\ZMRedisPool; use ZM\Store\ZMBuf; use ZM\Utils\DataProvider; use ZM\Utils\HttpUtil; @@ -132,9 +134,9 @@ class ServerEventHandler foreach ($server->connections as $v) { $server->close($v); } - if (ZMBuf::$sql_pool !== null) { - ZMBuf::$sql_pool->close(); - ZMBuf::$sql_pool = null; + if (SqlPoolStorage::$sql_pool !== null) { + SqlPoolStorage::$sql_pool->close(); + SqlPoolStorage::$sql_pool = null; } // 这里执行的是只需要执行一遍的代码,比如终端监听器和键盘监听器 @@ -175,7 +177,7 @@ class ServerEventHandler } } $sql = ZMConfig::get("global", "sql_config"); - ZMBuf::$sql_pool = new PDOPool((new PDOConfig()) + SqlPoolStorage::$sql_pool = new PDOPool((new PDOConfig()) ->withHost($sql["sql_host"]) ->withPort($sql["sql_port"]) // ->withUnixSocket('/tmp/mysql.sock') @@ -188,6 +190,13 @@ class ServerEventHandler DB::initTableList(); } + // 开箱即用的Redis + $redis = ZMConfig::get("global", "redis_config"); + if($redis !== null && $redis["host"] != "") { + if (!extension_loaded("redis")) Console::error("Can not find redis extension.\n"); + else ZMRedisPool::init($redis); + } + $this->loadAnnotations(); //加载composer资源、phar外置包、注解解析注册等 //echo json_encode(debug_backtrace(), 128|256); @@ -238,11 +247,11 @@ class ServerEventHandler * @param Frame $frame */ public function onMessage($server, Frame $frame) { - Console::debug("Calling Swoole \"message\" from fd=" . $frame->fd); + Console::debug("Calling Swoole \"message\" from fd=" . $frame->fd.": ".TermColor::ITALIC.$frame->data.TermColor::RESET); unset(Context::$context[Co::getCid()]); $conn = ManagerGM::get($frame->fd); set_coroutine_params(["server" => $server, "frame" => $frame, "connection" => $conn]); - $dispatcher = new EventDispatcher(SwooleEvent::class); + $dispatcher = new EventDispatcher(OnSwooleEvent::class); $dispatcher->setRuleFunction(function ($v) { if ($v->getRule() == '') { return strtolower($v->type) == 'message'; @@ -278,7 +287,7 @@ class ServerEventHandler Console::debug("Calling Swoole \"request\" event from fd=" . $request->fd); set_coroutine_params(["request" => $request, "response" => $response]); - $dis = new EventDispatcher(); + $dis = new EventDispatcher(OnSwooleEvent::class); $dis->setRuleFunction(function ($v) { if ($v->getRule() == '') { return strtolower($v->type) == 'request'; @@ -322,7 +331,7 @@ class ServerEventHandler else $response->end("Internal server error."); } - Console::error("Internal server exception (500), caused by " . get_class($e)); + Console::error("Internal server exception (500), caused by " . get_class($e).": ".$e->getMessage()); Console::log($e->getTraceAsString(), "gray"); } catch (Error $e) { $response->status(500); @@ -354,7 +363,8 @@ class ServerEventHandler ManagerGM::pushConnect($request->fd, $type_conn); $conn = ManagerGM::get($request->fd); set_coroutine_params(["server" => $server, "request" => $request, "connection" => $conn, "fd" => $request->fd]); - $dispatcher = new EventDispatcher(SwooleEvent::class); + $conn->setOption("connect_id", strval($request->header["x-self-id"]) ?? ""); + $dispatcher = new EventDispatcher(OnSwooleEvent::class); $dispatcher->setRuleFunction(function ($v) { if ($v->getRule() == '') { return strtolower($v->type) == 'open'; @@ -389,7 +399,7 @@ class ServerEventHandler if ($conn === null) return; Console::debug("Calling Swoole \"close\" event from fd=" . $fd); set_coroutine_params(["server" => $server, "connection" => $conn, "fd" => $fd]); - $dispatcher = new EventDispatcher(SwooleEvent::class); + $dispatcher = new EventDispatcher(OnSwooleEvent::class); $dispatcher->setRuleFunction(function ($v) { if ($v->getRule() == '') { return strtolower($v->type) == 'close'; @@ -421,9 +431,13 @@ class ServerEventHandler */ public function onPipeMessage(Server $server, $src_worker_id, $data) { //var_dump($data, $server->worker_id); - unset(Context::$context[Co::getCid()]); + //unset(Context::$context[Co::getCid()]); $data = json_decode($data, true); - switch ($data["action"]) { + switch ($data["action"] ?? '') { + case "resume_ws_message": + $obj = $data["data"]; + Co::resume($obj["coroutine"]); + break; case "stop": Console::verbose('正在清理 #' . $server->worker_id . ' 的计时器'); Timer::clearAll(); @@ -434,6 +448,9 @@ class ServerEventHandler case 'echo': Console::success('接收到来自 #' . $src_worker_id . ' 的消息'); break; + case 'send': + $server->sendMessage(json_encode(["action" => "echo"]), $data["target"]); + break; default: echo $data . PHP_EOL; } @@ -481,17 +498,17 @@ class ServerEventHandler } //加载插件 - $plugins = ZMConfig::get("global", "plugins") ?? []; + $plugins = ZMConfig::get("global", "modules") ?? []; if (!isset($plugins["qqbot"])) $plugins["qqbot"] = true; if ($plugins["qqbot"]) { - $obj = new SwooleEvent(); + $obj = new OnSwooleEvent(); $obj->class = QQBot::class; $obj->method = 'handle'; $obj->type = 'message'; $obj->level = 99999; $obj->rule = 'connectIsQQ()'; - EventManager::addEvent(SwooleEvent::class, $obj); + EventManager::addEvent(OnSwooleEvent::class, $obj); } //TODO: 编写加载外部插件的方式 diff --git a/src/ZM/Event/Swoole/MessageEvent.php b/src/ZM/Event/Swoole/MessageEvent.php deleted file mode 100644 index 55fde62f..00000000 --- a/src/ZM/Event/Swoole/MessageEvent.php +++ /dev/null @@ -1,97 +0,0 @@ -server = $server; - $this->frame = $frame; - } - - /** - * @inheritDoc - */ - public function onActivate() { - $conn = ManagerGM::get(context()->getFrame()->fd); - try { - if ($conn->getName() == "qq") { - $data = json_decode(context()->getFrame()->data, true); - if (isset($data["post_type"])) { - set_coroutine_params(["data" => $data, "connection" => $conn]); - ctx()->setCache("level", 0); - Console::debug("Calling CQ Event from fd=" . $conn->getFd()); - EventHandler::callCQEvent($data, ManagerGM::get(context()->getFrame()->fd), 0); - } else{ - set_coroutine_params(["connection" => $conn]); - EventHandler::callCQResponse($data); - } - } - foreach (ZMBuf::$events[SwooleEvent::class] ?? [] as $v) { - if (strtolower($v->type) == "message" && $this->parseSwooleRule($v)) { - $c = $v->class; - 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: " . (($cs = $e->getMessage()) == "" ? get_class($e) : $cs)); - Console::warning("In ". $e->getFile() . " at line ".$e->getLine()); - } - return $this; - } - - /** - * @inheritDoc - */ - public function onAfter() { - foreach (ZMBuf::$events[SwooleEventAfter::class] ?? [] as $v) { - if (strtolower($v->type) == "message" && $this->parseSwooleRule($v) === true) { - $c = $v->class; - $class = new $c(); - call_user_func_array([$class, $v->method], []); - 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, ManagerGM::get($this->frame->fd)); - break; - case "dataEqual": //handle websocket message事件时才能用 - if ($v->callback instanceof Closure) return call_user_func($v->callback, $this->frame->data); - break; - } - return true; - } -} diff --git a/src/ZM/Event/Swoole/RequestEvent.php b/src/ZM/Event/Swoole/RequestEvent.php deleted file mode 100644 index f05f7a54..00000000 --- a/src/ZM/Event/Swoole/RequestEvent.php +++ /dev/null @@ -1,186 +0,0 @@ -request = $request; - $this->response = $response; - } - - /** - * @return $this|SwooleEvent - * @throws Exception - */ - public function onActivate() { - foreach (ZMConfig::get("global", "http_header") as $k => $v) { - $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 = EventManager::$req_mapping; - $params = []; - while (true) { - $r = array_shift($uri); - if ($r === null) break; - if (($cnt = count($node["son"] ?? [])) == 1) { - if (isset($node["param_route"])) { - foreach ($node["son"] as $k => $v) { - if ($v["id"] == $node["param_route"]) { - $node = $v; - $params[mb_substr($v["name"], 1, -1)] = $r; - continue 2; - } - } - } elseif ($node["son"][0]["name"] == $r) { - $node = $node["son"][0]; - continue; - } - } elseif ($cnt >= 1) { - if (isset($node["param_route"])) { - foreach ($node["son"] as $k => $v) { - if ($v["id"] == $node["param_route"]) { - $node = $v; - $params[mb_substr($v["name"], 1, -1)] = $r; - continue 2; - } - } - } - foreach ($node["son"] as $k => $v) { - if ($v["name"] == $r) { - $node = $v; - continue 2; - } - } - } - - if (ZMConfig::get("global", "static_file_server")["status"]) { - $base_dir = ZMConfig::get("global", "static_file_server")["document_root"]; - $base_index = ZMConfig::get("global", "static_file_server")["document_index"]; - $uri = $this->request->server["request_uri"]; - $path = realpath($base_dir . urldecode($uri)); - if ($path !== false) { - if (is_dir($path)) $path = $path . '/'; - $work = realpath(DataProvider::getWorkingDir()) . '/'; - if (strpos($path, $work) !== 0) { - $this->responseStatus(403); - return $this; - } - if (is_dir($path)) { - foreach ($base_index as $vp) { - if (is_file($path . $vp)) { - Console::info("[200] " . $uri . " (static)"); - $exp = strtolower(pathinfo($path . $vp)['extension'] ?? "unknown"); - $this->response->setHeader("Content-Type", ZMConfig::get("file_header")[$exp] ?? "application/octet-stream"); - $this->response->end(file_get_contents($path . $vp)); - return $this; - } - } - } elseif (is_file($path)) { - Console::info("[200] " . $uri . " (static)"); - $exp = strtolower(pathinfo($path)['extension'] ?? "unknown"); - $this->response->setHeader("Content-Type", ZMConfig::get("file_header")[$exp] ?? "application/octet-stream"); - $this->response->end(file_get_contents($path)); - return $this; - } - } - } - $this->response->status(404); - $this->response->end(HttpUtil::getHttpCodePage(404)); - return $this; - } - context()->setCache("params", $params); - - if (in_array(strtoupper($this->request->server["request_method"]), $node["request_method"] ?? [])) { //判断目标方法在不在里面 - $c_name = $node["class"]; - 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); - } - ); - } - foreach (ZMBuf::$events[SwooleEvent::class] ?? [] as $v) { - if (strtolower($v->type) == "request" && $this->parseSwooleRule($v)) { - $c = $v->class; - EventHandler::callWithMiddleware($c, $v->method, ["request" => $this->request, "response" => $this->response], []); - if (context()->getCache("block_continue") === true) break; - } - } - - if (!$this->response->isEnd()) { - $this->response->status(404); - $this->response->end(HttpUtil::getHttpCodePage(404)); - } - return $this; - } - - /** - * @inheritDoc - */ - public function onAfter() { - foreach (ZMBuf::$events[SwooleEventAfter::class] ?? [] as $v) { - if (strtolower($v->type) == "request" && $this->parseSwooleRule($v)) { - $c = $v->class; - $class = new $c(["request" => $this->request, "response" => $this->response]); - call_user_func_array([$class, $v->method], []); - if ($class->block_continue) break; - } - } - return $this; - } - - private function responseStatus(int $int) { - $this->response->status($int); - $this->response->end(); - } - - private function parseSwooleRule($v) { - switch (explode(":", $v->rule)[0]) { - case "containsGet": - case "containsPost": - if ($v->callback instanceof Closure) return call_user_func($v->callback, $this->request); - break; - case "containsJson": - $content = $this->request->rawContent(); - $content = json_decode($content, true); - if ($content === null) return false; - if ($v->callback instanceof Closure) return call_user_func($v->callback, $content); - break; - } - return true; - } -} diff --git a/src/ZM/Event/Swoole/SwooleEventInterface.php b/src/ZM/Event/Swoole/SwooleEventInterface.php deleted file mode 100644 index 2918f3ff..00000000 --- a/src/ZM/Event/Swoole/SwooleEventInterface.php +++ /dev/null @@ -1,20 +0,0 @@ -server = $server; - $this->fd = $fd; - } - - /** - * @inheritDoc - * @throws AnnotationException - */ - public function onActivate() { - Console::debug("Websocket closed #{$this->fd}"); - set_coroutine_params(["server" => $this->server, "fd" => $this->fd, "connection" => ManagerGM::get($this->fd)]); - foreach(ZMBuf::$events[SwooleEvent::class] ?? [] as $v) { - if(strtolower($v->type) == "close" && $this->parseSwooleRule($v)) { - $c = $v->class; - EventHandler::callWithMiddleware($c, $v->method, ["server" => $this->server, "fd" => $this->fd], []); - if(context()->getCache("block_continue") === true) break; - } - } - ManagerGM::popConnect($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; - 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, ManagerGM::get($this->fd)); - break; - } - return true; - } -} diff --git a/src/ZM/Event/Swoole/WSOpenEvent.php b/src/ZM/Event/Swoole/WSOpenEvent.php deleted file mode 100644 index b0117435..00000000 --- a/src/ZM/Event/Swoole/WSOpenEvent.php +++ /dev/null @@ -1,106 +0,0 @@ -server = $server; - $this->request = $request; - } - - /** - * @inheritDoc - * @throws AnnotationException - */ - public function onActivate() { - $type = strtolower($this->request->get["type"] ?? $this->request->header["x-client-role"] ?? ""); - $type_conn = $this->getTypeClassName($type); - ManagerGM::pushConnect($this->request->fd, $type_conn); - if ($type_conn == "qq") { - ManagerGM::setName($this->request->fd, "qq"); - $qq = $this->request->get["qq"] ?? $this->request->header["x-self-id"] ?? ""; - $self_token = ZMConfig::get("global", "access_token") ?? ""; - 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)) { - ManagerGM::setOption($this->request->fd, "connect_id", $qq); - $this->conn = ManagerGM::get($this->request->fd); - } else { - $this->conn = ManagerGM::get($this->request->fd); - Console::warning("connection of CQ has invalid QQ or token!"); - Console::debug("Remote token: " . $remote_token); - } - } else { - $this->conn = ManagerGM::get($this->request->fd); - } - set_coroutine_params(["server" => $this->server, "request" => $this->request, "connection" => $this->conn]); - foreach (ZMBuf::$events[SwooleEvent::class] ?? [] as $v) { - if (strtolower($v->type) == "open" && $this->parseSwooleRule($v) === true) { - $c = $v->class; - 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; - } - - /** - * @inheritDoc - */ - public function onAfter() { - if (!$this->server->exists($this->conn->getFd())) return $this; - foreach (ZMBuf::$events[SwooleEventAfter::class] ?? [] as $v) { - if (strtolower($v->type) == "open" && $this->parseSwooleRule($v) === true) { - $class = new $v["class"](); - call_user_func_array([$class, $v["method"]], [$this->conn]); - 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, $this->conn); - break; - } - return true; - } - - -} diff --git a/src/ZM/Exception/NotInitializedException.php b/src/ZM/Exception/NotInitializedException.php new file mode 100644 index 00000000..0f30c04f --- /dev/null +++ b/src/ZM/Exception/NotInitializedException.php @@ -0,0 +1,12 @@ + ZMConfig::get("global", "port"), "log_level" => Console::getLevel(), "version" => ZM_VERSION, - "config" => $args["env"] === null ? 'global.php' : $args["env"], - "working_dir" => DataProvider::getWorkingDir() + "config" => $args["env"] === null ? 'global.php' : $args["env"] ]; if (isset(ZMConfig::get("global", "swoole")["task_worker_num"])) { $out["task_worker_num"] = ZMConfig::get("global", "swoole")["task_worker_num"]; @@ -94,6 +92,7 @@ class Framework if (($num = ZMConfig::get("global", "swoole")["worker_num"] ?? swoole_cpu_num()) != 1) { $out["worker_num"] = $num; } + $out["working_dir"] = DataProvider::getWorkingDir(); Console::printProps($out, $tty_width); self::$server->set($this->server_set); @@ -112,12 +111,16 @@ class Framework $asd = get_included_files(); // 注册 Swoole Server 的事件 $this->registerServerEvents(); - LightCache::init(ZMConfig::get("global", "light_cache") ?? [ - "size" => 2048, - "max_strlen" => 4096, - "hash_conflict_proportion" => 0.6, - "persistence_path" => realpath(DataProvider::getDataFolder()."_cache.json") - ]); + $r = ZMConfig::get("global", "light_cache") ?? [ + "size" => 1024, + "max_strlen" => 8192, + "hash_conflict_proportion" => 0.6, + "persistence_path" => realpath(DataProvider::getDataFolder() . "_cache.json"), + "auto_save_interval" => 900 + ]; + LightCache::init($r); + LightCacheInside::init(); + SpinLock::init($r["size"]); self::$server->start(); } catch (Exception $e) { Console::error("Framework初始化出现错误,请检查!"); @@ -189,12 +192,12 @@ class Framework foreach ($args as $x => $y) { switch ($x) { case 'disable-coroutine': - if($y) { + if ($y) { $coroutine_mode = false; } break; case 'debug-mode': - if ($y) { + if ($y || ZMConfig::get("global", "debug_mode")) { $coroutine_mode = false; $terminal_id = null; Console::warning("You are in debug mode, do not use in production!"); @@ -243,11 +246,7 @@ class Framework if ($coroutine_mode) Runtime::enableCoroutine(true, SWOOLE_HOOK_ALL); } - private function getTtyWidth() { + public static function getTtyWidth() { return explode(" ", trim(exec("stty size")))[1]; } - - public static function getServer() { - return self::$server; - } } diff --git a/src/ZM/Module/QQBot.php b/src/ZM/Module/QQBot.php index 0de1be68..f1cd1de2 100644 --- a/src/ZM/Module/QQBot.php +++ b/src/ZM/Module/QQBot.php @@ -3,6 +3,23 @@ namespace ZM\Module; +use Swoole\Coroutine; +use ZM\Annotation\CQ\CQAPIResponse; +use ZM\Annotation\CQ\CQBefore; +use ZM\Annotation\CQ\CQCommand; +use ZM\Annotation\CQ\CQMessage; +use ZM\Annotation\CQ\CQMetaEvent; +use ZM\Annotation\CQ\CQNotice; +use ZM\Annotation\CQ\CQRequest; +use ZM\Console\Console; +use ZM\Console\TermColor; +use ZM\Event\EventDispatcher; +use ZM\Exception\InterruptException; +use ZM\Exception\WaitTimeoutException; +use ZM\Store\LightCacheInside; +use ZM\Store\Lock\SpinLock; +use ZM\Utils\CoMessage; + /** * Class QQBot * @package ZM\Module @@ -10,7 +27,170 @@ namespace ZM\Module; */ class QQBot { + /** + * @throws InterruptException + */ public function handle() { - //TODO: 写处理机器人事件的函数 + try { + $data = json_decode(context()->getFrame()->data, true); + if (isset($data["post_type"])) { + //echo TermColor::ITALIC.json_encode($data, 128|256).TermColor::RESET.PHP_EOL; + set_coroutine_params(["data" => $data]); + ctx()->setCache("level", 0); + //Console::debug("Calling CQ Event from fd=" . ctx()->getConnection()->getFd()); + $this->dispatchBeforeEvents($data); // >= 200 的level before在这里执行 + if (false) { + Console::error("哦豁,停下了"); + EventDispatcher::interrupt(); + } + //Console::warning("最上数据包:".json_encode($data)); + $this->dispatchEvents($data); + } else { + $this->dispatchAPIResponse($data); + } + } /** @noinspection PhpRedundantCatchClauseInspection */ catch (WaitTimeoutException $e) { + $e->module->finalReply($e->getMessage()); + } + } + + public function dispatchBeforeEvents($data) { + $before = new EventDispatcher(CQBefore::class); + $before->setRuleFunction(function ($v) use ($data) { + if ($v->level < 200) EventDispatcher::interrupt(); + elseif ($v->cq_event != $data["post_type"]) return false; + return true; + }); + $before->setReturnFunction(function ($result) { + if (!$result) EventDispatcher::interrupt(); + }); + $before->dispatchEvents($data); + } + + private function dispatchEvents($data) { + //Console::warning("最xia数据包:".json_encode($data)); + switch ($data["post_type"]) { + case "message": + $word = split_explode(" ", str_replace("\r", "", context()->getMessage())); + if (count(explode("\n", $word[0])) >= 2) { + $enter = explode("\n", context()->getMessage()); + $first = split_explode(" ", array_shift($enter)); + $word = array_merge($first, $enter); + foreach ($word as $k => $v) { + $word[$k] = trim($word[$k]); + } + } + + //分发CQCommand事件 + $dispatcher = new EventDispatcher(CQCommand::class); + $dispatcher->setRuleFunction(function ($v) use ($word) { + if ($v->match == "" && $v->regexMatch == "" && $v->fullMatch == "") return false; + elseif (($v->user_id == 0 || ($v->user_id != 0 && $v->user_id == ctx()->getUserId())) && + ($v->group_id == 0 || ($v->group_id != 0 && $v->group_id == (ctx()->getGroupId() ?? 0))) && + ($v->message_type == '' || ($v->message_type != '' && $v->message_type == ctx()->getMessageType())) + ) { + if (($word[0] != "" && $v->match == $word[0]) || + in_array($word[0], $v->alias) || + ($v->regexMatch != "" && ($args = matchArgs($v->regexMatch, ctx()->getMessage())) !== false) || + ($v->fullMatch != "" && (preg_match("/" . $v->fullMatch . "/u", ctx()->getMessage(), $args)) != 0)) { + return true; + } + } + return false; + }); + $dispatcher->setReturnFunction(function ($result) { + if (is_string($result)) ctx()->reply($result); + EventDispatcher::interrupt(); + }); + $r = $dispatcher->dispatchEvents($word); + if ($r === null) EventDispatcher::interrupt(); + + //分发CQMessage事件 + $msg_dispatcher = new EventDispatcher(CQMessage::class); + $msg_dispatcher->setRuleFunction(function ($v) { + return ($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->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"])); + }); + $msg_dispatcher->setReturnFunction(function ($result) { + if (is_string($result)) ctx()->reply($result); + }); + $msg_dispatcher->dispatchEvents(ctx()->getMessage()); + return; + case "meta_event": + //Console::success("当前数据包:".json_encode(ctx()->getData())); + $dispatcher = new EventDispatcher(CQMetaEvent::class); + $dispatcher->setRuleFunction(function (CQMetaEvent $v) { + return ($v->meta_event_type == '' || ($v->meta_event_type != '' && $v->meta_event_type == ctx()->getData()["meta_event_type"])) && + ($v->sub_type == '' || ($v->sub_type != '' && $v->sub_type == (ctx()->getData()["sub_type"] ?? ''))); + }); + //eval(BP); + $dispatcher->dispatchEvents(ctx()->getData()); + return; + case "notice": + $dispatcher = new EventDispatcher(CQNotice::class); + $dispatcher->setRuleFunction(function (CQNotice $v) { + return + ($v->notice_type == '' || ($v->notice_type != '' && $v->notice_type == ctx()->getData()["notice_type"])) && + ($v->sub_type == '' || ($v->sub_type != '' && $v->sub_type == ctx()->getData()["sub_type"])) && + ($v->group_id == '' || ($v->group_id != '' && $v->group_id == ctx()->getData()["group_id"])) && + ($v->operator_id == '' || ($v->operator_id != '' && $v->operator_id == ctx()->getData()["operator_id"])); + }); + $dispatcher->dispatchEvents(ctx()->getData()); + return; + case "request": + $dispatcher = new EventDispatcher(CQRequest::class); + $dispatcher->setRuleFunction(function (CQRequest $v) { + return ($v->request_type == '' || ($v->request_type != '' && $v->request_type == ctx()->getData()['request_type'])) && + ($v->sub_type == '' || ($v->sub_type != '' && $v->sub_type == ctx()->getData()['sub_type'])) && + ($v->user_id == 0 || ($v->user_id != 0 && $v->user_id == ctx()->getData()["user_id"])) && + ($v->comment == '' || ($v->comment != '' && $v->comment == ctx()->getData()['comment'])); + }); + $dispatcher->dispatchEvents(ctx()->getData()); + return; + } + } + + private function dispatchAPIResponse($req) { + $status = $req["status"]; + $retcode = $req["retcode"]; + $data = $req["data"]; + if (isset($req["echo"]) && is_numeric($req["echo"])) { + $r = LightCacheInside::get("wait_api", "wait_api"); + if (isset($r[$req["echo"]])) { + $origin = $r[$req["echo"]]; + $self_id = $origin["self_id"]; + $response = [ + "status" => $status, + "retcode" => $retcode, + "data" => $data, + "self_id" => $self_id, + "echo" => $req["echo"] + ]; + set_coroutine_params(["cq_response" => $response]); + $dispatcher = new EventDispatcher(CQAPIResponse::class); + $dispatcher->setRuleFunction(function (CQAPIResponse $response) { + return $response->retcode == ctx()->getCQResponse()["retcode"]; + }); + $dispatcher->dispatchEvents($response); + + $origin_ctx = ctx()->copy(); + set_coroutine_params($origin_ctx); + if (($origin["coroutine"] ?? false) !== false) { + SpinLock::lock("wait_api"); + $r = LightCacheInside::get("wait_api", "wait_api"); + $r[$req["echo"]]["result"] = $response; + LightCacheInside::set("wait_api", "wait_api", $r); + SpinLock::unlock("wait_api"); + Coroutine::resume($origin['coroutine']); + } + SpinLock::lock("wait_api"); + $r = LightCacheInside::get("wait_api", "wait_api"); + unset($r[$req["echo"]]); + LightCacheInside::set("wait_api", "wait_api", $r); + SpinLock::unlock("wait_api"); + } + } } } diff --git a/src/ZM/Store/LightCache.php b/src/ZM/Store/LightCache.php index 964f8f71..5607c2e3 100644 --- a/src/ZM/Store/LightCache.php +++ b/src/ZM/Store/LightCache.php @@ -12,6 +12,8 @@ class LightCache { /** @var Table|null */ private static $kv_table = null; + /** @var Table|null */ + private static $kv_lock = null; private static $config = []; @@ -22,17 +24,21 @@ class LightCache self::$kv_table = new Table($config["size"], $config["hash_conflict_proportion"]); self::$kv_table->column("value", Table::TYPE_STRING, $config["max_strlen"]); self::$kv_table->column("expire", Table::TYPE_INT); - self::$kv_table->column("data_type", Table::TYPE_STRING, 12); + self::$kv_table->column("data_type", Table::TYPE_STRING, 8); $result = self::$kv_table->create(); + self::$kv_lock = new Table($config["size"], $config["hash_conflict_proportion"]); + $result = $result && self::$kv_lock->create(); if ($result === true && isset($config["persistence_path"])) { - $r = json_decode(file_get_contents($config["persistence_path"]), true); - if ($r === null) $r = []; - foreach ($r as $k => $v) { - $write = self::set($k, $v); - Console::debug("Writing LightCache: " . $k); - if ($write === false) { - self::$last_error = '可能是由于 Hash 冲突过多导致动态空间无法分配内存'; - return false; + if (file_exists($config["persistence_path"])) { + $r = json_decode(file_get_contents($config["persistence_path"]), true); + if ($r === null) $r = []; + foreach ($r as $k => $v) { + $write = self::set($k, $v, -2); + Console::verbose("Writing LightCache: " . $k); + if ($write === false) { + self::$last_error = '可能是由于 Hash 冲突过多导致动态空间无法分配内存'; + return false; + } } } } @@ -83,13 +89,46 @@ class LightCache $data_type = ""; } elseif (is_int($value)) { $data_type = "int"; - } else { + } elseif (is_bool($value)) { + $data_type = "bool"; + $value = json_encode($value); + }else { throw new Exception("Only can set string, array and int"); } try { return self::$kv_table->set($key, [ "value" => $value, - "expire" => $expire != -1 ? $expire + time() : -1, + "expire" => $expire >= 0 ? $expire + time() : $expire, + "data_type" => $data_type + ]); + } catch (Exception $e) { + return false; + } + } + + /** + * @param string $key + * @param $value + * @return bool|mixed + * @throws Exception + */ + public static function update(string $key, $value) { + if (self::$kv_table === null) throw new Exception("not initialized LightCache."); + if (is_array($value)) { + $value = json_encode($value, JSON_UNESCAPED_UNICODE); + if (strlen($value) >= self::$config["max_strlen"]) return false; + $data_type = "json"; + } elseif (is_string($value)) { + $data_type = ""; + } elseif (is_int($value)) { + $data_type = "int"; + } else { + throw new Exception("Only can set string, array and int"); + } + try { + if(self::$kv_table->get($key) === false) return false; + return self::$kv_table->set($key, [ + "value" => $value, "data_type" => $data_type ]); } catch (Exception $e) { @@ -131,14 +170,16 @@ class LightCache } public static function savePersistence() { + if(self::$kv_table === null) return; $r = []; foreach (self::$kv_table as $k => $v) { if ($v["expire"] === -2) { + Console::verbose("Saving " . $k); $r[$k] = self::parseGet($v); } } $r = file_put_contents(self::$config["persistence_path"], json_encode($r, 128 | 256)); - if($r === false) Console::error("Not saved, please check your \"persistence_path\"!"); + if ($r === false) Console::error("Not saved, please check your \"persistence_path\"!"); } private static function checkExpire($key) { @@ -153,6 +194,7 @@ class LightCache switch ($r["data_type"]) { case "json": case "int": + case "bool": return json_decode($r["value"], true); case "": default: diff --git a/src/ZM/Store/LightCacheInside.php b/src/ZM/Store/LightCacheInside.php new file mode 100644 index 00000000..f3838cc7 --- /dev/null +++ b/src/ZM/Store/LightCacheInside.php @@ -0,0 +1,55 @@ +column("value", Table::TYPE_STRING, 65536); + $result = self::$kv_table["wait_api"]->create(); + if ($result === false) { + self::$last_error = '系统内存不足,申请失败'; + return $result; + } + return $result; + } + + public static function get(string $table, string $key) { + if (!isset(self::$kv_table[$table])) throw new Exception("not initialized LightCache"); + $r = self::$kv_table[$table]->get($key); + return $r === false ? null : json_decode($r["value"], true); + } + + /** + * @param string $table + * @param string $key + * @param string|array|int $value + * @return mixed + * @throws Exception + */ + public static function set(string $table, string $key, $value) { + if (self::$kv_table === null) throw new Exception("not initialized LightCache"); + try { + return self::$kv_table[$table]->set($key, [ + "value" => json_encode($value, 256) + ]); + } catch (Exception $e) { + return false; + } + } + + public static function unset(string $table, string $key) { + return self::$kv_table[$table]->del($key); + } +} diff --git a/src/ZM/Store/Lock/SpinLock.php b/src/ZM/Store/Lock/SpinLock.php new file mode 100644 index 00000000..25b85fa4 --- /dev/null +++ b/src/ZM/Store/Lock/SpinLock.php @@ -0,0 +1,44 @@ +column('lock_num', Table::TYPE_INT, 8); + return self::$kv_lock->create(); + } + + public static function lock(string $key) + { + while (($r = self::$kv_lock->incr($key, 'lock_num')) > 1) { //此资源已经被锁上了 + if(Coroutine::getCid() != -1) System::sleep(self::$delay / 1000); + else usleep(self::$delay * 1000); + } + } + + public static function tryLock(string $key) { + if (($r = self::$kv_lock->incr($key, 'lock_num')) > 1) { + return false; + } + return true; + } + + public static function unlock(string $key) { + return self::$kv_lock->set($key, ['lock_num' => 0]); + } +} diff --git a/src/ZM/Store/MySQL/SqlPoolStorage.php b/src/ZM/Store/MySQL/SqlPoolStorage.php new file mode 100644 index 00000000..1df61eda --- /dev/null +++ b/src/ZM/Store/MySQL/SqlPoolStorage.php @@ -0,0 +1,13 @@ +get(); + $result = $callable($r); + if (isset($r->wasted)) ZMRedisPool::$pool->put(null); + else ZMRedisPool::$pool->put($r); + return $result; + } + + /** + * ZMRedis constructor. + * @throws NotInitializedException + */ + public function __construct() { + if(ZMRedisPool::$pool === null) throw new NotInitializedException("Redis pool is not initialized."); + $this->conn = ZMRedisPool::$pool->get(); + } + + /** + * @return Redis + */ + public function get() { + return $this->conn; + } + + public function __destruct() { + if (isset($this->conn->wasted)) ZMRedisPool::$pool->put(null); + else ZMRedisPool::$pool->put($this->conn); + } +} diff --git a/src/ZM/Store/Redis/ZMRedisPool.php b/src/ZM/Store/Redis/ZMRedisPool.php new file mode 100644 index 00000000..2ca1473e --- /dev/null +++ b/src/ZM/Store/Redis/ZMRedisPool.php @@ -0,0 +1,37 @@ +withHost($config['host']) + ->withPort($config['port']) + ->withAuth($config['auth']) + ->withDbIndex($config['db_index']) + ->withTimeout($config['timeout'] ?? 1) + ); + try { + $r = self::$pool->get()->ping('123'); + if(strpos(strtolower($r), "123") !== false) { + Console::debug("成功连接redis连接池!"); + } else { + var_dump($r); + } + } catch (RedisException $e) { + Console::error("Redis init failed! ".$e->getMessage()); + self::$pool = null; + } + } +} diff --git a/src/ZM/Store/ZMAtomic.php b/src/ZM/Store/ZMAtomic.php new file mode 100644 index 00000000..850a465e --- /dev/null +++ b/src/ZM/Store/ZMAtomic.php @@ -0,0 +1,38 @@ + $v) { + self::$atomics[$k] = new Atomic($v); + } + self::$atomics["stop_signal"] = new Atomic(0); + self::$atomics["wait_msg_id"] = new Atomic(0); + for ($i = 0; $i < 10; ++$i) { + self::$atomics["_tmp_" . $i] = new Atomic(0); + } + } + + +} diff --git a/src/ZM/Store/ZMBuf.php b/src/ZM/Store/ZMBuf.php index 25ef3a90..32c0621a 100755 --- a/src/ZM/Store/ZMBuf.php +++ b/src/ZM/Store/ZMBuf.php @@ -8,46 +8,10 @@ namespace ZM\Store; -use Swoole\Atomic; -use Swoole\Database\PDOPool; -use ZM\Config\ZMConfig; - class ZMBuf { - //读写的缓存数据,需要在worker_num = 1下才能正常使用 - //Swoole SQL连接池,多进程下每个进程一个连接池 - /** @var PDOPool */ - static $sql_pool = null;//保存sql连接池的类 - - /** @var array 事件注解的绑定对 */ public static $events = []; - - // 下面的有用,上面的没用了 - /** @var Atomic[] */ - public static $atomics; public static $instance = []; public static $context_class = []; public static $terminal = null; - - /** - * 初始化atomic计数器 - */ - public static function initAtomic() { - foreach (ZMConfig::get("global", "init_atomics") as $k => $v) { - self::$atomics[$k] = new Atomic($v); - } - self::$atomics["stop_signal"] = new Atomic(0); - self::$atomics["wait_msg_id"] = new Atomic(0); - for($i = 0; $i < 10; ++$i) { - self::$atomics["_tmp_".$i] = new Atomic(0); - } - } - - /** - * @param $name - * @return Atomic|null - */ - public static function atomic($name) { - return self::$atomics[$name] ?? null; - } } diff --git a/src/ZM/Utils/CoMessage.php b/src/ZM/Utils/CoMessage.php new file mode 100644 index 00000000..485229e0 --- /dev/null +++ b/src/ZM/Utils/CoMessage.php @@ -0,0 +1,80 @@ +add(1); + $hang["compare"] = $compare; + $hang["coroutine"] = $cid; + $hang["worker_id"] = server()->worker_id; + $hang["result"] = null; + SpinLock::lock("wait_api"); + $wait = LightCacheInside::get("wait_api", "wait_api"); + $wait[$api_id] = $hang; + LightCacheInside::set("wait_api", "wait_api", $wait); + SpinLock::unlock("wait_api"); + $id = swoole_timer_after($timeout * 1000, function () use ($api_id) { + $r = LightCacheInside::get("wait_api", "wait_api")[$api_id] ?? null; + if (is_array($r)) { + Co::resume($r["coroutine"]); + } + }); + Co::suspend(); + SpinLock::lock("wait_api"); + $sess = LightCacheInside::get("wait_api", "wait_api"); + $result = $sess[$api_id]["result"]; + unset($sess[$api_id]); + LightCacheInside::set("wait_api", "wait_api", $sess); + SpinLock::unlock("wait_api"); + if (isset($id)) swoole_timer_clear($id); + if ($result === null) return false; + return $result; + } + + public static function resumeByWS() { + $dat = ctx()->getData(); + $last = null; + SpinLock::lock("wait_api"); + $all = LightCacheInside::get("wait_api", "wait_api") ?? []; + foreach ($all as $k => $v) { + foreach ($v["compare"] as $vs) { + if ($v[$vs] != ($dat[$vs] ?? null)) { + continue 2; + } + } + $last = $k; + } + if($last !== null) { + $all[$last]["result"] = $dat; + LightCacheInside::set("wait_api", "wait_api", $all); + SpinLock::unlock("wait_api"); + if ($all[$last]["worker_id"] != server()->worker_id) { + ZMUtil::sendActionToWorker($all[$k]["worker_id"], "resume_ws_message", $all[$last]); + } else { + Co::resume($all[$last]["coroutine"]); + } + return true; + } else { + SpinLock::unlock("wait_api"); + return false; + } + } +} diff --git a/src/ZM/Utils/ZMUtil.php b/src/ZM/Utils/ZMUtil.php index 8b2e1960..928369fe 100644 --- a/src/ZM/Utils/ZMUtil.php +++ b/src/ZM/Utils/ZMUtil.php @@ -10,15 +10,18 @@ use Swoole\Event; use Swoole\Timer; use ZM\Console\Console; use ZM\Store\LightCache; +use ZM\Store\LightCacheInside; +use ZM\Store\ZMAtomic; use ZM\Store\ZMBuf; class ZMUtil { public static function stop() { Console::warning(Console::setColor("Stopping server...", "red")); + LightCache::savePersistence(); if (ZMBuf::$terminal !== null) Event::del(ZMBuf::$terminal); - ZMBuf::atomic("stop_signal")->set(1); + ZMAtomic::get("stop_signal")->set(1); try { LightCache::set('stop', 'OK'); } catch (Exception $e) { @@ -30,9 +33,8 @@ class ZMUtil public static function reload($delay = 800) { Console::info(Console::setColor("Reloading server...", "gold")); usleep($delay * 1000); - foreach (LightCache::getAll() as $k => $v) { - if (mb_substr($k, 0, 8) == "wait_api") - if ($v["result"] === null) Co::resume($v["coroutine"]); + foreach ((LightCacheInside::get("wait_api", "wait_api") ?? []) as $k => $v) { + if ($v["result"] === null && isset($v["coroutine"])) Co::resume($v["coroutine"]); } foreach (server()->connections as $v) { server()->close($v); @@ -50,4 +52,8 @@ class ZMUtil return ZMBuf::$instance[$class]; } } + + public static function sendActionToWorker($target_id, $action, $data) { + server()->sendMessage(json_encode(["action" => $action, "data" => $data]), $target_id); + } } diff --git a/src/ZM/Utils/terminal_listener.php b/src/ZM/Utils/terminal_listener.php deleted file mode 100644 index 24f72ac8..00000000 --- a/src/ZM/Utils/terminal_listener.php +++ /dev/null @@ -1,33 +0,0 @@ -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"; - } -}); - diff --git a/src/ZM/global_defines.php b/src/ZM/global_defines.php index eaff3954..9612da44 100644 --- a/src/ZM/global_defines.php +++ b/src/ZM/global_defines.php @@ -5,10 +5,8 @@ use ZM\Config\ZMConfig; define("ZM_START_TIME", microtime(true)); define("ZM_DATA", ZMConfig::get("global", "zm_data")); define("ZM_VERSION", json_decode(file_get_contents(__DIR__ . "/../../composer.json"), true)["version"] ?? "unknown"); -define("CONFIG_DIR", ZMConfig::get("global", "config_dir")); define("CRASH_DIR", ZMConfig::get("global", "crash_dir")); @mkdir(ZM_DATA); -@mkdir(CONFIG_DIR); @mkdir(CRASH_DIR); define("CONN_WEBSOCKET", 0); diff --git a/src/ZM/global_functions.php b/src/ZM/global_functions.php index c0d462f5..605c74dd 100644 --- a/src/ZM/global_functions.php +++ b/src/ZM/global_functions.php @@ -1,5 +1,6 @@ 2048, + "size" => 2, "max_strlen" => 4096, - "hash_conflict_proportion" => 0.6, + "hash_conflict_proportion" => 0, + "persistence_path" => "../composer.json" ]); //LightCache::set("bool", true); $this->assertEquals(true, LightCache::set("2048", 123, 3)); $this->assertArrayHasKey("2048", LightCache::getAll()); sleep(3); $this->assertArrayNotHasKey("2048", LightCache::getAll()); + $this->assertEquals("Apache-2.0", LightCache::get("license")); + $this->assertEquals("zhamao/framework", LightCache::get("name")); + //$this->assertTrue(LightCache::set("storage", "asdasd", -2)); + //LightCache::savePersistence(); } } diff --git a/test/ZMTest/Mock/global.php b/test/ZMTest/Mock/global.php index f92f439c..c2f5915c 100644 --- a/test/ZMTest/Mock/global.php +++ b/test/ZMTest/Mock/global.php @@ -18,9 +18,6 @@ $config['debug_mode'] = false; /** 存放框架内文件数据的目录 */ $config['zm_data'] = realpath(__DIR__ . "/../") . '/zm_data/'; -/** 存放各个模块配置文件的目录 */ -$config['config_dir'] = $config['zm_data'] . 'config/'; - /** 存放崩溃和运行日志的目录 */ $config['crash_dir'] = $config['zm_data'] . 'crash/'; @@ -107,7 +104,7 @@ $config['command_register_class'] = [ ]; /** 服务器启用的外部第三方和内部插件 */ -$config['plugins'] = [ +$config['modules'] = [ 'qqbot' => true, // QQ机器人事件解析器,如果取消此项则默认为 true 开启状态,否则你手动填写 false 才会关闭 ];