initial 2.0.0-a2 commit

This commit is contained in:
jerry
2020-09-29 15:07:43 +08:00
parent 1510e2f0d0
commit f91d24aaaa
59 changed files with 2271 additions and 1475 deletions

View File

@@ -8,6 +8,8 @@ use Co;
use Doctrine\Common\Annotations\AnnotationException;
use ZM\ConnectionManager\ConnectionObject;
use ZM\Console\Console;
use ZM\Event\EventDispatcher;
use ZM\Event\EventManager;
use ZM\Store\ZMBuf;
use ZM\Annotation\CQ\CQAfter;
use ZM\Annotation\CQ\CQBefore;
@@ -36,21 +38,23 @@ class MessageEvent
* @throws AnnotationException
*/
public function onBefore() {
$obj_list = ZMBuf::$events[CQBefore::class]["message"] ?? [];
foreach ($obj_list as $v) {
if ($v->level < 200) break;
EventHandler::callWithMiddleware(
$v->class,
$v->method,
["data" => context()->getData(), "connection" => $this->connection],
[],
function ($r) {
if (!$r) context()->setCache("block_continue", true);
}
);
if (context()->getCache("block_continue") === true) return false;
}
$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"] &&
@@ -62,7 +66,7 @@ class MessageEvent
return false;
}
}
foreach (ZMBuf::$events[CQBefore::class]["message"] ?? [] as $v) {
foreach (EventManager::$events[CQBefore::class]["message"] ?? [] as $v) {
if ($v->level >= 200) continue;
$c = $v->class;
if (ctx()->getCache("level") != 0) continue;
@@ -95,70 +99,44 @@ class MessageEvent
$word[$k] = trim($word[$k]);
}
}
$obj = [];
foreach (ZMBuf::$events[CQCommand::class] ?? [] as $v) {
/** @var CQCommand $v */
if ($v->match == "" && $v->regexMatch == "" && $v->fullMatch == "") continue;
elseif (($v->user_id == 0 || ($v->user_id != 0 && $v->user_id == context()->getData()["user_id"])) &&
($v->group_id == 0 || ($v->group_id != 0 && $v->group_id == (context()->getData()["group_id"] ?? 0))) &&
($v->discuss_id == 0 || ($v->discuss_id != 0 && $v->discuss_id == (context()->getData()["discuss_id"] ?? 0))) &&
($v->message_type == '' || ($v->message_type != '' && $v->message_type == context()->getData()["message_type"]))
) {
$c = $v->class;
if (!isset($obj[$c])) {
$obj[$c] = new $c();
}
if ($word[0] != "" && $v->match == $word[0]) {
Console::debug("Calling $c -> {$v->method}");
$this->function_call = EventHandler::callWithMiddleware($obj[$c], $v->method, [], [$word], function ($r) {
if (is_string($r)) context()->reply($r);
return true;
});
return;
} elseif (in_array($word[0], $v->alias)) {
Console::debug("Calling $c -> {$v->method}");
$this->function_call = EventHandler::callWithMiddleware($obj[$c], $v->method, [], [$word], function ($r) {
if (is_string($r)) context()->reply($r);
return true;
});
return;
} elseif ($v->regexMatch != "" && ($args = matchArgs($v->regexMatch, context()->getMessage())) !== false) {
Console::debug("Calling $c -> {$v->method}");
$this->function_call = EventHandler::callWithMiddleware($obj[$c], $v->method, [], [$args], function ($r) {
if (is_string($r)) context()->reply($r);
return true;
});
return;
} elseif ($v->fullMatch != "" && (preg_match("/".$v->fullMatch."/u", ctx()->getMessage(), $args)) != 0) {
Console::debug("Calling $c -> {$v->method}");
array_shift($args);
$this->function_call = EventHandler::callWithMiddleware($obj[$c], $v->method, [], [$args], function ($r) {
if (is_string($r)) context()->reply($r);
return true;
});
return;
//分发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;
}
}
}
foreach (ZMBuf::$events[CQMessage::class] ?? [] as $v) {
/** @var CQMessage $v */
if (
($v->message == '' || ($v->message != '' && $v->message == context()->getData()["message"])) &&
return false;
});
$dispatcher->setReturnFunction(function ($result) {
if (is_string($result)) ctx()->reply($result);
EventDispatcher::interrupt();
});
$r = $dispatcher->dispatchEvents($word);
if ($r === false) 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->discuss_id == 0 || ($v->discuss_id != 0 && $v->discuss_id == (context()->getData()["discuss_id"] ?? 0))) &&
($v->message_type == '' || ($v->message_type != '' && $v->message_type == context()->getData()["message_type"])) &&
($v->raw_message == '' || ($v->raw_message != '' && $v->raw_message == context()->getData()["raw_message"]))) {
$c = $v->class;
Console::debug("Calling CQMessage: $c -> {$v->method}");
if (!isset($obj[$c]))
$obj[$c] = new $c();
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;
}
}
($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());
}

View File

@@ -64,7 +64,7 @@ class MetaEvent
if (context()->getCache("block_continue") === true) return;
}
}
} catch (WaitTimeoutException $e) {
} /** @noinspection PhpRedundantCatchClauseInspection */ catch (WaitTimeoutException $e) {
$e->module->finalReply($e->getMessage());
}
}

View File

@@ -0,0 +1,109 @@
<?php
namespace ZM\Event;
use Doctrine\Common\Annotations\AnnotationException;
use Exception;
use ZM\Annotation\AnnotationBase;
use ZM\Annotation\Interfaces\Rule;
use ZM\Exception\InterruptException;
use ZM\Utils\ZMUtil;
class EventDispatcher
{
/** @var string */
private $class;
/** @var null|callable */
private $rule = null;
/** @var null|callable */
private $return_func = null;
public static function interrupt() {
throw new InterruptException('interrupt');
}
public function __construct(string $class = '') {
$this->class = $class;
}
public function setRuleFunction(callable $rule = null) {
$this->rule = $rule;
return $this;
}
public function setReturnFunction(callable $return_func) {
$this->return_func = $return_func;
return $this;
}
public function dispatchEvents(...$params) {
try {
foreach (EventManager::$events[$this->class] ?? [] as $v) {
$result = $this->dispatchEvent($v, $this->rule, ...$params);
if (is_callable($this->return_func)) ($this->return_func)($result);
}
return true;
} catch (InterruptException $e) {
return false;
} catch (AnnotationException $e) {
return false;
}
}
public function dispatchEvent(?AnnotationBase $v, $rule_func = null, ...$params) {
$q_c = $v->class;
$q_f = $v->method;
if ($rule_func !== null && !$rule_func($v)) return false;
if (isset(EventManager::$middleware_map[$q_c][$q_f])) {
$middlewares = EventManager::$middleware_map[$q_c][$q_f];
$before_result = true;
$r = [];
foreach ($middlewares as $k => $middleware) {
if (!isset(EventManager::$middlewares[$middleware])) throw new AnnotationException("Annotation parse error: Unknown MiddlewareClass named \"{$middleware}\"!");
$middleware_obj = EventManager::$middlewares[$middleware];
$before = $middleware_obj["class"];
//var_dump($middleware_obj);
$r[$k] = new $before();
$r[$k]->class = $q_c;
$r[$k]->method = $q_f;
if (isset($middleware_obj["before"])) {
$rs = $middleware_obj["before"];
$before_result = $r[$k]->$rs(...$params);
if ($before_result === false) break;
}
}
if ($before_result) {
try {
$q_o = ZMUtil::getModInstance($q_c);
$result = $q_o->$q_f(...$params);
} catch (Exception $e) {
if ($e instanceof InterruptException) throw $e;
for ($i = count($middlewares) - 1; $i >= 0; --$i) {
$middleware_obj = EventManager::$middlewares[$middlewares[$i]];
if (!isset($middleware_obj["exceptions"])) continue;
foreach ($middleware_obj["exceptions"] as $name => $method) {
if ($e instanceof $name) {
$r[$i]->$method($e);
self::interrupt();
}
}
}
throw $e;
}
for ($i = count($middlewares) - 1; $i >= 0; --$i) {
$middleware_obj = EventManager::$middlewares[$middlewares[$i]];
if (isset($middleware_obj["after"], $r[$i])) {
$r[$i]->{$middleware_obj["after"]}(...$params);
}
}
return $result;
}
return false;
} else {
$q_o = ZMUtil::getModInstance($q_c);
return $q_o->$q_f(...$params);
}
}
}

View File

@@ -8,20 +8,21 @@ use Co;
use Doctrine\Common\Annotations\AnnotationException;
use Error;
use Exception;
use ZM\Config\ZMConfig;
use ZM\ConnectionManager\ConnectionObject;
use ZM\ConnectionManager\ManagerGM;
use ZM\Console\Console;
use ZM\Event\Swoole\{MessageEvent, RequestEvent, WorkerStartEvent, WSCloseEvent, WSOpenEvent};
use ZM\Event\Swoole\{MessageEvent, RequestEvent, WSCloseEvent, WSOpenEvent};
use Swoole\Http\Request;
use Swoole\Server;
use Swoole\WebSocket\Frame;
use ZM\Annotation\CQ\CQAPIResponse;
use ZM\Annotation\CQ\CQAPISend;
use ZM\Annotation\Http\MiddlewareClass;
use ZM\Context\Context;
use ZM\Http\MiddlewareInterface;
use ZM\Http\Response;
use ZM\Store\ZMBuf;
use ZM\Utils\DataProvider;
use ZM\Utils\ZMUtil;
class EventHandler
@@ -34,38 +35,9 @@ class EventHandler
*/
public static function callSwooleEvent($event_name, $param0, $param1 = null) {
//$starttime = microtime(true);
unset(ZMBuf::$context[Co::getCid()]);
unset(Context::$context[Co::getCid()]);
$event_name = strtolower($event_name);
switch ($event_name) {
case "workerstart":
try {
register_shutdown_function(function () use ($param0) {
$error = error_get_last();
if ($error["type"] != 0) {
Console::error("Internal fatal error: " . $error["message"] . " at " . $error["file"] . "({$error["line"]})");
}
DataProvider::saveBuffer();
/** @var Server $param0 */
if (ZMBuf::$server === null) $param0->shutdown();
else ZMBuf::$server->shutdown();
});
ZMBuf::$server = $param0;
$r = (new WorkerStartEvent($param0, $param1))->onActivate();
Console::success("Worker #" . $param0->worker_id . " 已启动");
$r->onAfter();
self::startTick();
} catch (Exception $e) {
Console::error("Worker加载出错停止服务");
Console::error($e->getMessage() . "\n" . $e->getTraceAsString());
ZMUtil::stop();
return;
} catch (Error $e) {
Console::error("PHP Error: " . $e->getMessage() . " in " . $e->getFile() . " on line " . $e->getLine());
Console::error("Maybe it caused by your own code if in your own Module directory.");
Console::log($e->getTraceAsString(), 'gray');
ZMUtil::stop();
}
break;
case "message":
/** @var Frame $param1 */
/** @var Server $param0 */
@@ -90,7 +62,7 @@ class EventHandler
" [" . $param1->getStatusCode() . "] " . $param0->server["request_uri"]
);
if (!$param1->isEnd()) {
if (\ZM\Config\ZMConfig::get("global", "debug_mode"))
if (ZMConfig::get("global", "debug_mode"))
$param1->end("Internal server error: " . $e->getMessage());
else
$param1->end("Internal server error.");
@@ -268,11 +240,13 @@ class EventHandler
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"])) {
$before_result = call_user_func_array([$r[$k], $middleware_obj["before"]], $func_args);
$rs = $middleware_obj["before"];
$before_result = $r[$k]->$rs(...$func_args);
if ($before_result === false) break;
}
}
@@ -281,9 +255,9 @@ class EventHandler
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);
$result = $class->$method(...$func_args);
if (is_callable($after_call))
$return_value = call_user_func_array($after_call, [$result]);
$return_value = $after_call($result);
} catch (Exception $e) {
for ($i = count($middlewares) - 1; $i >= 0; --$i) {
$middleware_obj = ZMBuf::$events[MiddlewareClass::class][$middlewares[$i]];
@@ -301,8 +275,9 @@ class EventHandler
}
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);
if (isset($middleware_obj["after"], $r[$i])) {
$r[$i]->{$middleware_obj["after"]}(...$func_args);
}
}
} else {
if (is_object($c)) $class = $c;
@@ -314,12 +289,4 @@ class EventHandler
}
return $return_value;
}
private static function startTick() {
Console::debug("Starting " . count(ZMBuf::get("paused_tick", [])) . " custom tick function");
foreach (ZMBuf::get("paused_tick", []) as $cid) {
Co::resume($cid);
}
}
}

View File

@@ -0,0 +1,62 @@
<?php
namespace ZM\Event;
use Error;
use Exception;
use Swoole\Timer;
use ZM\Annotation\AnnotationBase;
use ZM\Annotation\AnnotationParser;
use ZM\Annotation\Swoole\OnTick;
use ZM\Console\Console;
use ZM\Store\ZMBuf;
class EventManager
{
public static $events = [];
public static $middleware_map = [];
public static $middlewares = [];
public static $req_mapping = [];
public static function addEvent($event_name, AnnotationBase $event_obj) {
self::$events[$event_name][] = $event_obj;
}
public static function loadEventByParser(AnnotationParser $parser) {
self::$events = $parser->generateAnnotationEvents();
self::$middlewares = $parser->getMiddlewares();
self::$middleware_map = $parser->getMiddlewareMap();
self::$req_mapping = $parser->getReqMapping();
}
/**
* 注册所有计时器给每个进程
*/
public static function registerTimerTick() {
$dispatcher = new EventDispatcher(OnTick::class);
foreach (self::$events[OnTick::class] ?? [] as $vss) {
if (server()->worker_id !== $vss->worker_id) return;
//echo server()->worker_id.PHP_EOL;
$plain_class = $vss->class;
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) {
Timer::clearAll();
return;
}
try {
$dispatcher->dispatchEvent($vss, null);
} catch (Exception $e) {
Console::error("Uncaught error from TimerTick: " . $e->getMessage() . " at " . $e->getFile() . "({$e->getLine()})");
} catch (Error $e) {
Console::error("Uncaught fatal error from TimerTick: " . $e->getMessage());
echo Console::setColor($e->getTraceAsString(), "gray");
Console::error("Please check your code!");
}
});
}
}
}

View File

@@ -1,80 +1,507 @@
<?php
<?php /** @noinspection PhpComposerExtensionStubsInspection */
namespace ZM\Event;
use Co;
use Doctrine\Common\Annotations\AnnotationException;
use Error;
use Exception;
use PDO;
use ReflectionException;
use Swoole\Database\PDOConfig;
use Swoole\Database\PDOPool;
use Swoole\Event;
use Swoole\Process;
use Swoole\Timer;
use ZM\Annotation\AnnotationParser;
use ZM\Annotation\Http\RequestMapping;
use ZM\Annotation\Swoole\OnStart;
use ZM\Annotation\Swoole\SwooleEvent;
use ZM\Config\ZMConfig;
use ZM\ConnectionManager\ManagerGM;
use ZM\Console\Console;
use Swoole\Http\Request;
use Swoole\Server;
use Swoole\WebSocket\Frame;
use ZM\Annotation\Swoole\OnEvent;
use ZM\Annotation\Swoole\HandleEvent;
use ZM\Console\TermColor;
use ZM\Context\Context;
use ZM\Context\ContextInterface;
use ZM\DB\DB;
use ZM\Exception\DbException;
use ZM\Framework;
use ZM\Http\Response;
use ZM\Module\QQBot;
use ZM\Store\ZMBuf;
use ZM\Utils\DataProvider;
use ZM\Utils\HttpUtil;
use ZM\Utils\Terminal;
use ZM\Utils\ZMUtil;
class ServerEventHandler
{
/**
* @OnEvent("WorkerStart")
* @param Server $server
* @param $worker_id
* @throws AnnotationException
* @HandleEvent("start")
*/
public function onWorkerStart(Server $server, $worker_id) {
if ($server->taskworker === false) {
EventHandler::callSwooleEvent("WorkerStart", $server, $worker_id);
} else {
ob_start();
//AnnotationParser::registerMods();
ob_get_clean();
public function onStart() {
global $terminal_id;
$r = null;
if ($terminal_id !== null) {
ZMBuf::$terminal = $r = STDIN;
Event::add($r, function () use ($r) {
$var = trim(fgets($r));
try {
Terminal::executeCommand($var, $r);
} catch (Exception $e) {
Console::error("Uncaught exception ".get_class($e).": ".$e->getMessage()." at ".$e->getFile()."(".$e->getLine().")");
} catch (Error $e) {
Console::error("Uncaught error ".get_class($e).": ".$e->getMessage()." at ".$e->getFile()."(".$e->getLine().")");
}
});
}
Process::signal(SIGINT, function () use ($r) {
Console::warning("Server interrupted by keyboard on Master.");
if ((Framework::$server->inotify ?? null) !== null)
/** @noinspection PhpUndefinedFieldInspection */ Event::del(Framework::$server->inotify);
ZMUtil::stop();
});
if(Framework::$argv["watch"]) {
if (extension_loaded('inotify')) {
Console::warning("Enabled File watcher, do not use in production.");
Framework::$server->inotify = $fd = inotify_init();
$this->addWatcher(DataProvider::getWorkingDir() . "/src", $fd);
Event::add($fd, function () use ($fd) {
$r = inotify_read($fd);
var_dump($r);
ZMUtil::reload();
});
} else {
Console::warning("You have not loaded inotify extension.");
}
}
}
/**
* @OnEvent("message")
* @param $server
* @param Frame $frame
* @throws AnnotationException
* @HandleEvent("shutdown")
*/
public function onMessage($server, Frame $frame) {
if ($frame->fd !== ZMBuf::get("terminal_fd"))
Console::debug("Calling Swoole \"message\" from fd=" . $frame->fd);
EventHandler::callSwooleEvent("message", $server, $frame);
public function onShutdown() {
Console::debug("正在关闭 Master 进程pid=" . posix_getpid());
}
/**
* @OnEvent("request")
* @HandleEvent("WorkerStop")
* @param $server
* @param $worker_id
*/
public function onWorkerStop(Server $server, $worker_id) {
Console::debug(($server->taskworker ? "Task" : "") . "Worker #$worker_id 已停止");
}
/**
* @HandleEvent("WorkerStart")
* @param Server $server
* @param $worker_id
*/
public function onWorkerStart(Server $server, $worker_id) {
//if (ZMBuf::atomic("stop_signal")->get() != 0) return;
Process::signal(SIGINT, function () use ($worker_id, $server) {
Console::debug("正在关闭 " . ($server->taskworker ? "Task" : "") . "Worker 进程 " . Console::setColor("#" . \server()->worker_id, "gold") . TermColor::frontColor256(59) . ", pid=" . posix_getpid());
server()->stop($worker_id);
});
unset(Context::$context[Co::getCid()]);
if ($server->taskworker === false) {
try {
register_shutdown_function(function () use ($server) {
$error = error_get_last();
if ($error["type"] != 0) {
Console::error("Internal fatal error: " . $error["message"] . " at " . $error["file"] . "({$error["line"]})");
}
DataProvider::saveBuffer();
/** @var Server $server */
if (server() === null) $server->shutdown();
else server()->shutdown();
});
Console::info("Worker #{$server->worker_id} 启动中");
Framework::$server = $server;
ZMBuf::resetCache(); //清空变量缓存
//ZMBuf::set("wait_start", []); //添加队列在workerStart运行完成前先让其他协程等待执行
foreach ($server->connections as $v) {
$server->close($v);
}
if (ZMBuf::$sql_pool !== null) {
ZMBuf::$sql_pool->close();
ZMBuf::$sql_pool = null;
}
// 这里执行的是只需要执行一遍的代码,比如终端监听器和键盘监听器
/*if ($server->worker_id === 0) {
global $terminal_id;
if ($terminal_id !== null)
go(function () {
while (true) {
$r = server()->process->exportSocket();
$result = $r->recv();
try {
if (!Terminal::executeCommand($result)) {
//if ($result == "stop" || $result == "reload" || $result == "r") {
//echo "Stopped.\n";
break;
}
} catch (Exception $e) {
Console::error($e->getMessage());
} catch (Error $e) {
Console::error($e->getMessage());
}
}
});
}*/
//TODO: 单独抽出来MySQL和Redis连接池
if (ZMConfig::get("global", "sql_config")["sql_host"] != "") {
Console::info("新建SQL连接池中");
ob_start();
phpinfo();
$str = ob_get_clean();
$str = explode("\n", $str);
foreach ($str as $k => $v) {
$v = trim($v);
if ($v == "") continue;
if (mb_strpos($v, "API Extensions") === false) continue;
if (mb_strpos($v, "pdo_mysql") === false) {
throw new DbException("未安装 mysqlnd php-mysql扩展。");
}
}
$sql = ZMConfig::get("global", "sql_config");
ZMBuf::$sql_pool = new PDOPool((new PDOConfig())
->withHost($sql["sql_host"])
->withPort($sql["sql_port"])
// ->withUnixSocket('/tmp/mysql.sock')
->withDbName($sql["sql_database"])
->withCharset('utf8mb4')
->withUsername($sql["sql_username"])
->withPassword($sql["sql_password"])
->withOptions($sql["sql_options"] ?? [PDO::ATTR_STRINGIFY_FETCHES => false])
);
DB::initTableList();
}
$this->loadAnnotations(); //加载composer资源、phar外置包、注解解析注册等
//echo json_encode(debug_backtrace(), 128|256);
Console::success("Worker #" . $worker_id . " 已启动");
EventManager::registerTimerTick(); //启动计时器
//ZMBuf::unsetCache("wait_start");
set_coroutine_params(["server" => $server, "worker_id" => $worker_id]);
$dispatcher = new EventDispatcher(OnStart::class);
$dispatcher->setRuleFunction(function ($v) {
return server()->worker_id === $v->worker_id || $v->worker_id === -1;
});
$dispatcher->dispatchEvents($server, $worker_id);
Console::debug("@OnStart 执行完毕");
} catch (Exception $e) {
Console::error("Worker加载出错停止服务");
Console::error($e->getMessage() . "\n" . $e->getTraceAsString());
ZMUtil::stop();
return;
} catch (Error $e) {
Console::error("PHP Error: " . $e->getMessage() . " in " . $e->getFile() . " on line " . $e->getLine());
Console::error("Maybe it caused by your own code if in your own Module directory.");
Console::log($e->getTraceAsString(), 'gray');
ZMUtil::stop();
}
} else {
// 这里是TaskWorker初始化的内容部分
try {
Framework::$server = $server;
$this->loadAnnotations();
Console::debug("TaskWorker #" . $server->worker_id . " 已启动");
} catch (Exception $e) {
Console::error("Worker加载出错停止服务");
Console::error($e->getMessage() . "\n" . $e->getTraceAsString());
ZMUtil::stop();
return;
} catch (Error $e) {
Console::error("PHP Error: " . $e->getMessage() . " in " . $e->getFile() . " on line " . $e->getLine());
Console::error("Maybe it caused by your own code if in your own Module directory.");
Console::log($e->getTraceAsString(), 'gray');
ZMUtil::stop();
}
}
}
/**
* @HandleEvent("message")
* @param $server
* @param Frame $frame
*/
public function onMessage($server, Frame $frame) {
Console::debug("Calling Swoole \"message\" from fd=" . $frame->fd);
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->setRuleFunction(function ($v) {
if ($v->getRule() == '') {
return strtolower($v->type) == 'message';
} else {
/** @noinspection PhpUnreachableStatementInspection
* @noinspection RedundantSuppression
*/
if (strtolower($v->type) == 'message' && eval("return " . $v->getRule() . ";")) return true;
else return false;
}
});
try {
$dispatcher->dispatchEvents($conn);
} catch (Exception $e) {
$error_msg = $e->getMessage() . " at " . $e->getFile() . "(" . $e->getLine() . ")";
Console::error("Uncaught exception " . get_class($e) . " when calling \"message\": " . $error_msg);
Console::trace();
} catch (Error $e) {
$error_msg = $e->getMessage() . " at " . $e->getFile() . "(" . $e->getLine() . ")";
Console::error("Uncaught Error " . get_class($e) . " when calling \"message\": " . $error_msg);
Console::trace();
}
}
/**
* @HandleEvent("request")
* @param $request
* @param $response
* @throws AnnotationException
*/
public function onRequest($request, $response) {
$response = new Response($response);
Console::debug("Receiving Http request event, cid=" . Co::getCid());
EventHandler::callSwooleEvent("request", $request, $response);
unset(Context::$context[Co::getCid()]);
Console::debug("Calling Swoole \"request\" event from fd=" . $request->fd);
set_coroutine_params(["request" => $request, "response" => $response]);
$dis = new EventDispatcher();
$dis->setRuleFunction(function ($v) {
if ($v->getRule() == '') {
return strtolower($v->type) == 'request';
} else {
/** @noinspection PhpUnreachableStatementInspection */
if (strtolower($v->type) == 'request' && eval("return " . $v->getRule() . ";")) return true;
else return false;
}
});
try {
$no_interrupt = $dis->dispatchEvents($request, $response);
if (!$no_interrupt) {
$result = HttpUtil::parseUri($request, $response, $request->server["request_uri"], $node, $params);
if (!$result) {
ctx()->setCache("params", $params);
$dispatcher = new EventDispatcher(RequestMapping::class);
$div = new RequestMapping();
$div->route = $node["route"];
$div->params = $params;
$div->method = $node["method"];
$div->request_method = $node["request_method"];
$div->class = $node["class"];
$r = $dispatcher->dispatchEvent($div, null, $params, $request, $response);
if (is_string($r) && !$response->isEnd()) $response->end($r);
}
}
if (!$response->isEnd()) {
HttpUtil::responseCodePage($response, 404);
}
} catch (Exception $e) {
$response->status(500);
Console::info($request->server["remote_addr"] . ":" . $request->server["remote_port"] .
" [" . $response->getStatusCode() . "] " . $request->server["request_uri"]
);
if (!$response->isEnd()) {
if (ZMConfig::get("global", "debug_mode"))
$response->end("Internal server exception: " . $e->getMessage());
else
$response->end("Internal server error.");
}
Console::error("Internal server exception (500), caused by " . get_class($e));
Console::log($e->getTraceAsString(), "gray");
} catch (Error $e) {
$response->status(500);
Console::info($request->server["remote_addr"] . ":" . $request->server["remote_port"] .
" [" . $response->getStatusCode() . "] " . $request->server["request_uri"]
);
if (!$response->isEnd()) {
$error_msg = $e->getMessage() . " at " . $e->getFile() . "(" . $e->getLine() . ")";
if (ZMConfig::get("global", "debug_mode"))
$response->end("Internal server error: " . $error_msg);
else
$response->end("Internal server error.");
}
Console::error("Internal server error (500), caused by " . get_class($e));
Console::log($e->getTraceAsString(), "gray");
}
}
/**
* @OnEvent("open")
* @HandleEvent("open")
* @param $server
* @param Request $request
* @throws AnnotationException
*/
public function onOpen($server, Request $request) {
Console::debug("Calling Swoole \"open\" event from fd=" . $request->fd);
EventHandler::callSwooleEvent("open", $server, $request);
unset(Context::$context[Co::getCid()]);
$type = strtolower($request->get["type"] ?? $request->header["x-client-role"] ?? "");
$type_conn = ManagerGM::getTypeClassName($type);
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);
$dispatcher->setRuleFunction(function ($v) {
if ($v->getRule() == '') {
return strtolower($v->type) == 'open';
} else {
/** @noinspection PhpUnreachableStatementInspection */
if (strtolower($v->type) == 'open' && eval("return " . $v->getRule() . ";")) return true;
else return false;
}
});
try {
$dispatcher->dispatchEvents($conn);
} catch (Exception $e) {
$error_msg = $e->getMessage() . " at " . $e->getFile() . "(" . $e->getLine() . ")";
Console::error("Uncaught exception " . get_class($e) . " when calling \"open\": " . $error_msg);
Console::trace();
} catch (Error $e) {
$error_msg = $e->getMessage() . " at " . $e->getFile() . "(" . $e->getLine() . ")";
Console::error("Uncaught Error " . get_class($e) . " when calling \"open\": " . $error_msg);
Console::trace();
}
//EventHandler::callSwooleEvent("open", $server, $request);
}
/**
* @OnEvent("close")
* @HandleEvent("close")
* @param $server
* @param $fd
* @throws AnnotationException
*/
public function onClose($server, $fd) {
Console::debug("Calling Swoole \"close\" event from fd=" . $fd);
EventHandler::callSwooleEvent("close", $server, $fd);
unset(Context::$context[Co::getCid()]);
$conn = ManagerGM::get($fd);
set_coroutine_params(["server" => $server, "connection" => $conn, "fd" => $fd]);
$dispatcher = new EventDispatcher(SwooleEvent::class);
$dispatcher->setRuleFunction(function ($v) {
if ($v->getRule() == '') {
return strtolower($v->type) == 'close';
} else {
/** @noinspection PhpUnreachableStatementInspection */
if (strtolower($v->type) == 'close' && eval("return " . $v->getRule() . ";")) return true;
else return false;
}
});
try {
$dispatcher->dispatchEvents($conn);
} catch (Exception $e) {
$error_msg = $e->getMessage() . " at " . $e->getFile() . "(" . $e->getLine() . ")";
Console::error("Uncaught exception " . get_class($e) . " when calling \"close\": " . $error_msg);
Console::trace();
} catch (Error $e) {
$error_msg = $e->getMessage() . " at " . $e->getFile() . "(" . $e->getLine() . ")";
Console::error("Uncaught Error " . get_class($e) . " when calling \"close\": " . $error_msg);
Console::trace();
}
ManagerGM::popConnect($fd);
}
/**
* @HandleEvent("pipeMessage")
* @param $server
* @param $src_worker_id
* @param $data
*/
public function onPipeMessage(Server $server, $src_worker_id, $data) {
//var_dump($data, $server->worker_id);
unset(Context::$context[Co::getCid()]);
$data = json_decode($data, true);
switch ($data["action"]) {
case "stop":
Console::verbose('正在清理 #' . $server->worker_id . ' 的计时器');
Timer::clearAll();
break;
case "terminate":
$server->stop();
break;
case 'echo':
Console::success('接收到来自 #' . $src_worker_id . ' 的消息');
break;
default:
echo $data . PHP_EOL;
}
}
/**
* @HandleEvent("task")
*/
public function onTask() {
}
/**
* @throws ReflectionException
* @throws Exception
*/
private function loadAnnotations() {
//加载phar包
/*Console::debug("加载外部phar包中");
$dir = DataProvider::getWorkingDir() . "/resources/package/";
if (version_compare(SWOOLE_VERSION, "4.4.0", ">=")) Timer::clearAll();
if (is_dir($dir)) {
$list = scandir($dir);
unset($list[0], $list[1]);
foreach ($list as $v) {
if (is_dir($dir . $v)) continue;
if (pathinfo($dir . $v, 4) == "phar") {
Console::debug("加载Phar: " . $dir . $v . " 中");
require_once($dir . $v);
}
}
}*/
//加载各个模块的注解类,以及反射
Console::debug("检索Module中");
$parser = new AnnotationParser();
$parser->addRegisterPath(DataProvider::getWorkingDir() . "/src/Module/", "Module");
$parser->registerMods();
EventManager::loadEventByParser($parser); //加载事件
//加载自定义的全局函数
Console::debug("加载自定义上下文中...");
$context_class = ZMConfig::get("global", "context_class");
if (!is_a($context_class, ContextInterface::class, true)) {
throw new Exception("Context class must implemented from ContextInterface!");
}
//加载插件
$plugins = ZMConfig::get("global", "plugins") ?? [];
if (!isset($plugins["qqbot"])) $plugins["qqbot"] = true;
if ($plugins["qqbot"]) {
$obj = new SwooleEvent();
$obj->class = QQBot::class;
$obj->method = 'handle';
$obj->type = 'message';
$obj->level = 99999;
$obj->rule = 'connectIsQQ()';
EventManager::addEvent(SwooleEvent::class, $obj);
}
//TODO: 编写加载外部插件的方式
//$this->loadExternalModules();
}
private function addWatcher($maindir, $fd) {
$dir = scandir($maindir);
unset($dir[0], $dir[1]);
foreach ($dir as $subdir) {
if (is_dir($maindir . "/" . $subdir)) {
Console::debug("添加监听目录:" . $maindir . "/" . $subdir);
inotify_add_watch($fd, $maindir . "/" . $subdir, IN_ATTRIB | IN_ISDIR);
$this->addWatcher($maindir . "/" . $subdir, $fd);
}
}
}
}

View File

@@ -10,13 +10,12 @@ use ZM\Console\Console;
use Swoole\WebSocket\Frame;
use Swoole\WebSocket\Server;
use ZM\Annotation\Swoole\SwooleEventAfter;
use ZM\Annotation\Swoole\SwooleEventAt;
use ZM\Annotation\Swoole\SwooleEvent;
use Exception;
use ZM\Event\EventHandler;
use ZM\Store\ZMBuf;
use ZM\Utils\ZMUtil;
class MessageEvent implements SwooleEvent
class MessageEvent implements SwooleEventInterface
{
/**
* @var Server
@@ -36,7 +35,6 @@ class MessageEvent implements SwooleEvent
* @inheritDoc
*/
public function onActivate() {
ZMUtil::checkWait();
$conn = ManagerGM::get(context()->getFrame()->fd);
try {
if ($conn->getName() == "qq") {
@@ -51,7 +49,7 @@ class MessageEvent implements SwooleEvent
EventHandler::callCQResponse($data);
}
}
foreach (ZMBuf::$events[SwooleEventAt::class] ?? [] as $v) {
foreach (ZMBuf::$events[SwooleEvent::class] ?? [] as $v) {
if (strtolower($v->type) == "message" && $this->parseSwooleRule($v)) {
$c = $v->class;
EventHandler::callWithMiddleware(

View File

@@ -6,17 +6,20 @@ namespace ZM\Event\Swoole;
use Closure;
use Exception;
use ZM\Config\ZMConfig;
use ZM\Console\Console;
use Framework\ZMBuf;
use ZM\Event\EventManager;
use ZM\Store\ZMBuf;
use Swoole\Http\Request;
use ZM\Annotation\Swoole\SwooleEventAfter;
use ZM\Annotation\Swoole\SwooleEventAt;
use ZM\Annotation\Swoole\SwooleEvent;
use ZM\Event\EventHandler;
use ZM\Http\Response;
use ZM\Utils\DataProvider;
use ZM\Utils\HttpUtil;
use ZM\Utils\ZMUtil;
class RequestEvent implements SwooleEvent
class RequestEvent implements SwooleEventInterface
{
/**
* @var Request
@@ -37,15 +40,14 @@ class RequestEvent implements SwooleEvent
* @throws Exception
*/
public function onActivate() {
ZMUtil::checkWait();
foreach (\ZM\Config\ZMConfig::get("global", "http_header") as $k => $v) {
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 = ZMBuf::$req_mapping;
$node = EventManager::$req_mapping;
$params = [];
while (true) {
$r = array_shift($uri);
@@ -81,9 +83,9 @@ class RequestEvent implements SwooleEvent
}
}
if (\ZM\Config\ZMConfig::get("global", "static_file_server")["status"]) {
$base_dir = \ZM\Config\ZMConfig::get("global", "static_file_server")["document_root"];
$base_index = \ZM\Config\ZMConfig::get("global", "static_file_server")["document_index"];
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) {
@@ -98,7 +100,7 @@ class RequestEvent implements SwooleEvent
if (is_file($path . $vp)) {
Console::info("[200] " . $uri . " (static)");
$exp = strtolower(pathinfo($path . $vp)['extension'] ?? "unknown");
$this->response->setHeader("Content-Type", ZMBuf::config("file_header")[$exp] ?? "application/octet-stream");
$this->response->setHeader("Content-Type", ZMConfig::get("file_header")[$exp] ?? "application/octet-stream");
$this->response->end(file_get_contents($path . $vp));
return $this;
}
@@ -106,14 +108,14 @@ class RequestEvent implements SwooleEvent
} elseif (is_file($path)) {
Console::info("[200] " . $uri . " (static)");
$exp = strtolower(pathinfo($path)['extension'] ?? "unknown");
$this->response->setHeader("Content-Type", ZMBuf::config("file_header")[$exp] ?? "application/octet-stream");
$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(ZMUtil::getHttpCodePage(404));
$this->response->end(HttpUtil::getHttpCodePage(404));
return $this;
}
context()->setCache("params", $params);
@@ -131,7 +133,7 @@ class RequestEvent implements SwooleEvent
}
);
}
foreach (ZMBuf::$events[SwooleEventAt::class] ?? [] as $v) {
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], []);
@@ -141,7 +143,7 @@ class RequestEvent implements SwooleEvent
if (!$this->response->isEnd()) {
$this->response->status(404);
$this->response->end(ZMUtil::getHttpCodePage(404));
$this->response->end(HttpUtil::getHttpCodePage(404));
}
return $this;
}

View File

@@ -6,15 +6,15 @@ namespace ZM\Event\Swoole;
use ZM\Event\Event;
interface SwooleEvent extends Event
interface SwooleEventInterface extends Event
{
/**
* @return SwooleEvent
* @return SwooleEventInterface
*/
public function onActivate();
/**
* @return SwooleEvent
* @return SwooleEventInterface
*/
public function onAfter();
}
}

View File

@@ -10,12 +10,12 @@ use ZM\ConnectionManager\ManagerGM;
use ZM\Console\Console;
use Swoole\Server;
use ZM\Annotation\Swoole\SwooleEventAfter;
use ZM\Annotation\Swoole\SwooleEventAt;
use ZM\Annotation\Swoole\SwooleEvent;
use ZM\Event\EventHandler;
use ZM\Store\ZMBuf;
use ZM\Utils\ZMUtil;
class WSCloseEvent implements SwooleEvent
class WSCloseEvent implements SwooleEventInterface
{
public $server;
@@ -31,10 +31,9 @@ class WSCloseEvent implements SwooleEvent
* @throws AnnotationException
*/
public function onActivate() {
Console::info("Closed #{$this->fd}");
ZMUtil::checkWait();
Console::debug("Websocket closed #{$this->fd}");
set_coroutine_params(["server" => $this->server, "fd" => $this->fd, "connection" => ManagerGM::get($this->fd)]);
foreach(ZMBuf::$events[SwooleEventAt::class] ?? [] as $v) {
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], []);

View File

@@ -13,12 +13,11 @@ use ZM\Console\Console;
use Swoole\Http\Request;
use Swoole\WebSocket\Server;
use ZM\Annotation\Swoole\SwooleEventAfter;
use ZM\Annotation\Swoole\SwooleEventAt;
use ZM\Annotation\Swoole\SwooleEvent;
use ZM\Event\EventHandler;
use ZM\Store\ZMBuf;
use ZM\Utils\ZMUtil;
class WSOpenEvent implements SwooleEvent
class WSOpenEvent implements SwooleEventInterface
{
/**
* @var Server
@@ -41,10 +40,9 @@ class WSOpenEvent implements SwooleEvent
* @throws AnnotationException
*/
public function onActivate() {
ZMUtil::checkWait();
ManagerGM::pushConnect($this->request->fd);
$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"] ?? "";
@@ -65,7 +63,7 @@ class WSOpenEvent implements SwooleEvent
$this->conn = ManagerGM::get($this->request->fd);
}
set_coroutine_params(["server" => $this->server, "request" => $this->request, "connection" => $this->conn]);
foreach (ZMBuf::$events[SwooleEventAt::class] ?? [] as $v) {
foreach (ZMBuf::$events[SwooleEvent::class] ?? [] as $v) {
if (strtolower($v->type) == "open" && $this->parseSwooleRule($v) === true) {
$c = $v->class;
EventHandler::callWithMiddleware(
@@ -104,14 +102,5 @@ class WSOpenEvent implements SwooleEvent
return true;
}
private function getTypeClassName(string $type) {
$map = [
"qq" => "qq",
"universal" => "qq",
"webconsole" => "webconsole",
"proxy" => "proxy",
"terminal" => "terminal"
];
return $map[$type] ?? "default";
}
}

View File

@@ -1,189 +0,0 @@
<?php
namespace ZM\Event\Swoole;
use Doctrine\Common\Annotations\AnnotationException;
use Exception;
use PDO;
use ReflectionException;
use Swoole\Coroutine;
use Swoole\Database\PDOConfig;
use Swoole\Database\PDOPool;
use Swoole\Process;
use Swoole\Timer;
use ZM\Annotation\AnnotationParser;
use ZM\Annotation\Swoole\OnStart;
use ZM\Config\ZMConfig;
use ZM\Context\ContextInterface;
use ZM\DB\DB;
use ZM\Console\Console;
use Swoole\Server;
use ZM\Event\EventHandler;
use ZM\Exception\DbException;
use ZM\Store\ZMBuf;
use ZM\Utils\DataProvider;
use ZM\Utils\Terminal;
use ZM\Utils\ZMUtil;
class WorkerStartEvent implements SwooleEvent
{
private $worker_id;
/**
* @var Server
*/
private $server;
public function __construct(Server $server, $worker_id) {
$this->server = $server;
$this->worker_id = $worker_id;
}
/**
* @return WorkerStartEvent
* @throws AnnotationException
* @throws ReflectionException
* @throws DbException
*/
public function onActivate(): WorkerStartEvent {
Console::info("Worker #{$this->server->worker_id} 启动中");
ZMBuf::$server = $this->server;
ZMBuf::resetCache(); //清空变量缓存
ZMBuf::set("wait_start", []); //添加队列在workerStart运行完成前先让其他协程等待执行
$this->resetConnections();//释放所有与framework的连接
global $terminal_id;
Terminal::listenConsole($terminal_id); //这个方法只能在这里调用且如果worker_num不为1的话此功能不可用
// 这里执行的是只需要执行一遍的代码,比如终端监听器和键盘监听器
if ($this->server->worker_id === 0) {
if($terminal_id !== null) Console::info("监听console输入");
Process::signal(SIGINT, function () {
echo PHP_EOL;
Console::warning("Server interrupted by keyboard.");
ZMUtil::stop();
});
ZMBuf::$atomics['reload_time']->add(1);
$this->setAutosaveTimer(ZMConfig::get("global", "auto_save_interval"));
} else {
Process::signal(SIGINT, function () {
// Do Nothing
});
}
if (ZMConfig::get("global", "sql_config")["sql_host"] != "") {
Console::info("新建SQL连接池中");
ob_start();
phpinfo();
$str = ob_get_clean();
$str = explode("\n", $str);
foreach ($str as $k => $v) {
$v = trim($v);
if ($v == "") continue;
if (mb_strpos($v, "API Extensions") === false) continue;
if (mb_strpos($v, "pdo_mysql") === false) {
throw new DbException("未安装 mysqlnd php-mysql扩展。");
}
}
$sql = ZMConfig::get("global", "sql_config");
ZMBuf::$sql_pool = new PDOPool((new PDOConfig())
->withHost($sql["sql_host"])
->withPort($sql["sql_port"])
// ->withUnixSocket('/tmp/mysql.sock')
->withDbName($sql["sql_database"])
->withCharset('utf8mb4')
->withUsername($sql["sql_username"])
->withPassword($sql["sql_password"])
->withOptions($sql["sql_options"] ?? [PDO::ATTR_STRINGIFY_FETCHES => false])
);
DB::initTableList();
}
$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");
set_coroutine_params(["server" => $this->server, "worker_id" => $this->worker_id]);
if($this->server->worker_id === 0) {
foreach (ZMBuf::$events[OnStart::class] ?? [] as $v) {
$class_name = $v->class;
Console::debug("正在调用启动时函数: " . $class_name . " -> " . $v->method);
EventHandler::callWithMiddleware($class_name, $v->method, ["server" => $this->server, "worker_id" => $this->worker_id], []);
}
Console::debug("@OnStart 执行完毕");
}
return $this;
}
private function resetConnections() {
foreach ($this->server->connections as $v) {
$this->server->close($v);
}
if (ZMBuf::$sql_pool !== null) {
ZMBuf::$sql_pool->close();
ZMBuf::$sql_pool = null;
}
}
/**
* @throws ReflectionException
* @throws Exception
*/
private function loadAllClass() {
//加载phar包
Console::info("加载外部phar包中");
$dir = DataProvider::getWorkingDir() . "/resources/package/";
if (version_compare(SWOOLE_VERSION, "4.4.0", ">=")) Timer::clearAll();
if (is_dir($dir)) {
$list = scandir($dir);
unset($list[0], $list[1]);
foreach ($list as $v) {
if (is_dir($dir . $v)) continue;
if (pathinfo($dir . $v, 4) == "phar") {
Console::verbose("加载Phar: " . $dir . $v . "");
require_once($dir . $v);
}
}
}
//加载composer类
//remove stupid duplicate code
//加载各个模块的注解类,以及反射
Console::info("检索Module中");
$parser = new AnnotationParser();
$parser->addRegisterPath(DataProvider::getWorkingDir() . "/src/Module/", "Module");
$parser->registerMods();
$parser->sortLevels();
//加载自定义的全局函数
Console::debug("加载自定义的全局函数中");
$this->afterCheck();
}
private function setAutosaveTimer($globals) {
DataProvider::$buffer_list = [];
zm_timer_tick($globals * 1000, function () {
DataProvider::saveBuffer();
});
}
/**
* @throws Exception
*/
private function afterCheck() {
$context_class = ZMConfig::get("global", "context_class");
if (!is_a($context_class, ContextInterface::class, true)) {
throw new Exception("Context class must implemented from ContextInterface!");
}
}
}