diff --git a/bin/phpunit-swoole b/bin/phpunit-swoole
new file mode 100755
index 00000000..eec62ac2
--- /dev/null
+++ b/bin/phpunit-swoole
@@ -0,0 +1,65 @@
+#!/usr/bin/env php
+
+ * Date: 2018/4/14 下午10:58
+ */
+
+Co::set([
+ 'log_level' => SWOOLE_LOG_INFO,
+ 'trace_flags' => 0
+]);
+
+if (!ini_get('date.timezone')) {
+ ini_set('date.timezone', 'UTC');
+}
+
+foreach ([
+ __DIR__ . '/../../../autoload.php',
+ __DIR__ . '/../../autoload.php',
+ __DIR__ . '/../vendor/autoload.php',
+ __DIR__ . '/vendor/autoload.php'
+ ] as $file
+) {
+ if (file_exists($file)) {
+ define('PHPUNIT_COMPOSER_INSTALL', $file);
+ break;
+ }
+}
+
+if (!defined('PHPUNIT_COMPOSER_INSTALL')) {
+ fwrite(
+ STDERR,
+ 'You need to set up the project dependencies using Composer:' . PHP_EOL . PHP_EOL .
+ ' composer install' . PHP_EOL . PHP_EOL .
+ 'You can learn all about Composer on https://getcomposer.org/.' . PHP_EOL
+ );
+
+ die(1);
+} else {
+ if (array_reverse(explode('/', __DIR__))[0] ?? '' === 'test') {
+ $vendor_dir = dirname(PHPUNIT_COMPOSER_INSTALL);
+ $bin_unit = "{$vendor_dir}/bin/phpunit";
+ $unit_uint = "{$vendor_dir}/phpunit/phpunit/phpunit";
+ if (file_exists($bin_unit)) {
+ @unlink($bin_unit);
+ @symlink(__FILE__, $bin_unit);
+ }
+ if (file_exists($unit_uint)) {
+ @unlink($unit_uint);
+ @symlink(__FILE__, $unit_uint);
+ }
+ }
+}
+require PHPUNIT_COMPOSER_INSTALL;
+$starttime = microtime(true);
+go(function (){
+ try{
+ PHPUnit\TextUI\Command::main(false);
+ } catch(Exception $e) {
+ echo $e->getMessage().PHP_EOL;
+ }
+});
+Swoole\Event::wait();
+echo "Took ".round(microtime(true) - $starttime, 4). "s\n";
diff --git a/bin/start b/bin/start
index 78c7a2b8..5a518c8f 100755
--- a/bin/start
+++ b/bin/start
@@ -5,7 +5,7 @@ use ZM\ConsoleApplication;
// 这行是用于开发者自己电脑的调试功能
-$symbol = sha1(is_file("/flag") ? file_get_contents("/flag") : '1') == '6252c0ec7fcbd544c3d6f5f0a162f60407d7a896' || mb_strpos(getcwd(), "/private/tmp");
+$symbol = sha1(is_file("/flag2") ? file_get_contents("/flag2") : '1') == '6252c0ec7fcbd544c3d6f5f0a162f60407d7a896' || mb_strpos(getcwd(), "/private/tmp");
// 首先得判断是直接从library模式启动的框架还是从composer引入library启动的框架
// 判断方法:判断当前目录上面有没有 /vendor 目录,如果没有 /vendor 目录说明是从 composer 引入的
@@ -15,14 +15,13 @@ if (!is_dir(__DIR__ . '/../vendor') || $symbol) {
define("LOAD_MODE_COMPOSER_PATH", getcwd());
/** @noinspection PhpIncludeInspection */
require_once LOAD_MODE_COMPOSER_PATH . "/vendor/autoload.php";
-} elseif (substr(__DIR__, 0, 7) != 'phar://') {
+} elseif (substr(__DIR__, 0, 7) == 'phar://') {
define("LOAD_MODE", 2); //phar模式
// 会废弃phar启动的方式,在2.0
} else {
define("LOAD_MODE", 0);
require_once __DIR__ . "/../vendor/autoload.php";
}
-
// 终端的命令行功能启动!!
$application = new ConsoleApplication("zhamao-framework");
$application->initEnv();
diff --git a/composer.json b/composer.json
index c63eeedc..91558a97 100644
--- a/composer.json
+++ b/composer.json
@@ -3,7 +3,7 @@
"description": "High performance QQ robot and web server development framework",
"minimum-stability": "stable",
"license": "Apache-2.0",
- "version": "2.0.0-a1",
+ "version": "2.0.0-a2",
"authors": [
{
"name": "whale",
@@ -16,24 +16,30 @@
],
"prefer-stable": true,
"bin": [
- "bin/start"
+ "bin/start",
+ "bin/phpunit-swoole"
],
"require": {
"php": ">=7.2",
"swoole/ide-helper": "@dev",
"ext-mbstring": "*",
- "swlib/saber": "^1.0",
"doctrine/annotations": "~1.10",
"ext-json": "*",
"ext-posix": "*",
"ext-ctype": "*",
- "ext-pdo": "*",
"psy/psysh": "@stable",
"symfony/console": "^5.1",
"symfony/polyfill-ctype": "^1.18",
"zhamao/connection-manager": "^1.0",
"zhamao/console": "^1.0",
- "zhamao/config": "^1.0"
+ "zhamao/config": "^1.0",
+ "zhamao/request": "^1.0",
+ "symfony/routing": "^5.1"
+ },
+ "suggest": {
+ "ext-pdo": "Allows framework connecting with mysql server",
+ "ext-redis": "Allows framework connecting with redis server",
+ "ext-inotify": "Enable file watcher feature in framework"
},
"autoload": {
"psr-4": {
@@ -45,6 +51,14 @@
"src/ZM/global_functions.php"
]
},
+ "autoload-dev": {
+ "psr-4": {
+ "ZMTest\\": "test/ZMTest"
+ },
+ "files": [
+ "test/ZMTest/Mock/mock.php"
+ ]
+ },
"require-dev": {
"phpunit/phpunit": "^9.3"
}
diff --git a/config/global.php b/config/global.php
index 14ebf7cd..8a42c83d 100644
--- a/config/global.php
+++ b/config/global.php
@@ -25,13 +25,21 @@ $config['crash_dir'] = $config['zm_data'] . 'crash/';
/** 对应swoole的server->set参数 */
$config['swoole'] = [
'log_file' => $config['crash_dir'] . 'swoole_error.log',
- 'worker_num' => 1,
+ 'worker_num' => 2,
'dispatch_mode' => 2,
'max_coroutine' => 30000,
- //'task_worker_num' => 1,
+ //'task_worker_num' => 4,
//'task_enable_coroutine' => true
];
+/** 轻量字符串缓存,默认开启 */
+$config['light_cache'] = [
+ "status" => true,
+ "size" => 2048, //最多允许储存的条数(需要2的倍数)
+ "max_strlen" => 4096, //单行字符串最大长度(需要2的倍数)
+ "hash_conflict_proportion" => 0.6 //Hash冲突率(越大越好,但是需要的内存更多)
+];
+
/** MySQL数据库连接信息,host留空则启动时不创建sql连接池 */
$config['sql_config'] = [
'sql_host' => '',
@@ -46,7 +54,7 @@ $config['sql_config'] = [
PDO::ATTR_EMULATE_PREPARES => false
],
'sql_no_exception' => false,
- 'sql_default_fetch_mode' => PDO::FETCH_BOTH // added in 1.5.6
+ 'sql_default_fetch_mode' => PDO::FETCH_ASSOC // added in 1.5.6
];
/** CQHTTP连接约定的token */
@@ -65,15 +73,13 @@ $config['http_default_code_page'] = [
/** zhamao-framework在框架启动时初始化的atomic们 */
$config['init_atomics'] = [
- 'in_count' => 0, //消息接收message的统计数量
- 'out_count' => 0, //消息发送(调用send_*_msg的统计数量)
- 'reload_time' => 0, //调用reload功能统计数量
- 'wait_msg_id' => 0, //协程挂起id自增
+ //'custom_atomic_name' => 0, //自定义添加的Atomic
];
+/** 终端日志显示等级(0-4) */
$config["info_level"] = 2;
-/** 自动保存的缓存保存时间(秒) */
+/** 自动保存计时器的缓存保存时间(秒) */
$config['auto_save_interval'] = 900;
/** 上下文接口类 implemented from ContextInterface */
@@ -93,4 +99,14 @@ $config['server_event_handler_class'] = [
\ZM\Event\ServerEventHandler::class,
];
+/** 注册自定义指令的类 */
+$config['command_register_class'] = [
+ //\Custom\Command\CustomCommand::class
+];
+
+/** 服务器启用的外部第三方和内部插件 */
+$config['plugins'] = [
+ 'qqbot' => true, // QQ机器人事件解析器,如果取消此项则默认为 true 开启状态,否则你手动填写 false 才会关闭
+];
+
return $config;
diff --git a/src/Custom/Annotation/Example.php b/src/Custom/Annotation/Example.php
index e5abd1cb..4c255730 100644
--- a/src/Custom/Annotation/Example.php
+++ b/src/Custom/Annotation/Example.php
@@ -16,5 +16,5 @@ use ZM\Annotation\Interfaces\CustomAnnotation;
class Example extends AnnotationBase implements CustomAnnotation
{
/** @var string */
- public $str;
+ public $str = '';
}
diff --git a/src/Custom/Command/CustomCommand.php b/src/Custom/Command/CustomCommand.php
new file mode 100644
index 00000000..9fe702f4
--- /dev/null
+++ b/src/Custom/Command/CustomCommand.php
@@ -0,0 +1,31 @@
+setDescription("custom description | 自定义命令的描述字段");
+ $this->addOption("failure", null, null, "以错误码为1返回结果");
+ // ...
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ if ($input->getOption("failure")) {
+ $output->writeln("Hello error! I am wrong message.");
+ return Command::FAILURE;
+ } else {
+ $output->writeln("Hello world! I am successful message.");
+ return Command::SUCCESS;
+ }
+ }
+}
diff --git a/src/Custom/global_function.php b/src/Custom/global_function.php
index 8ebaa871..3fa7ef70 100644
--- a/src/Custom/global_function.php
+++ b/src/Custom/global_function.php
@@ -1,3 +1,6 @@
getConnection();
+ public function onDisconnect(ConnectionObject $conn) {
Console::info("机器人 " . $conn->getOption("connect_id") . " 已断开连接!");
}
@@ -66,7 +66,7 @@ class Hello
$a = min(intval($num1), intval($num2));
$b = max(intval($num1), intval($num2));
// 回复用户结果
- ctx()->reply("随机数是:".mt_rand($a, $b));
+ ctx()->reply("随机数是:" . mt_rand($a, $b));
}
/**
@@ -87,10 +87,19 @@ class Hello
return "Hello Zhamao!";
}
+ /**
+ * 使用自定义参数的路由参数
+ * @RequestMapping("/whoami/{name}")
+ * @param $param
+ * @return string
+ */
+ public function paramGet($param) {
+ return "Your name: {$param["name"]}";
+ }
/**
* 框架会默认关闭未知的WebSocket链接,因为这个绑定的事件,你可以根据你自己的需求进行修改
- * @SwooleEventAt(type="open",rule="connectType:unknown")
+ * @SwooleEvent(type="open",rule="connectIsDefault()")
*/
public function closeUnknownConn() {
Console::info("Unknown connection , I will close it.");
diff --git a/src/Module/Middleware/TimerMiddleware.php b/src/Module/Middleware/TimerMiddleware.php
index 8968c4ea..34e21c7b 100644
--- a/src/Module/Middleware/TimerMiddleware.php
+++ b/src/Module/Middleware/TimerMiddleware.php
@@ -9,10 +9,10 @@ use ZM\Console\Console;
use ZM\Http\MiddlewareInterface;
/**
- * Class AuthMiddleware
+ * Class TimerMiddleware
* 示例中间件:用于统计路由函数运行时间用的
* @package Module\Middleware
- * @MiddlewareClass()
+ * @MiddlewareClass("timer")
*/
class TimerMiddleware implements MiddlewareInterface
{
@@ -33,6 +33,4 @@ class TimerMiddleware implements MiddlewareInterface
public function onAfter() {
Console::info("Using " . round((microtime(true) - $this->starttime) * 1000, 2) . " ms.");
}
-
- public function getName() { return "timer"; }
}
diff --git a/src/ZM/API/CQAPI.php b/src/ZM/API/CQAPI.php
index 605c00b3..6e243c62 100644
--- a/src/ZM/API/CQAPI.php
+++ b/src/ZM/API/CQAPI.php
@@ -9,61 +9,6 @@ use ZM\Console\Console;
use ZM\Event\EventHandler;
use ZM\Store\ZMBuf;
-/**
- * @method static send_private_msg($self_id, $params, $function = null)
- * @method static send_group_msg($self_id, $params, $function = null)
- * @method static send_discuss_msg($self_id, $params, $function = null)
- * @method static send_msg($self_id, $params, $function = null)
- * @method static delete_msg($self_id, $params, $function = null)
- * @method static send_like($self_id, $params, $function = null)
- * @method static set_group_kick($self_id, $params, $function = null)
- * @method static set_group_ban($self_id, $params, $function = null)
- * @method static set_group_anonymous_ban($self_id, $params, $function = null)
- * @method static set_group_whole_ban($self_id, $params, $function = null)
- * @method static set_group_admin($self_id, $params, $function = null)
- * @method static set_group_anonymous($self_id, $params, $function = null)
- * @method static set_group_card($self_id, $params, $function = null)
- * @method static set_group_leave($self_id, $params, $function = null)
- * @method static set_group_special_title($self_id, $params, $function = null)
- * @method static set_discuss_leave($self_id, $params, $function = null)
- * @method static set_friend_add_request($self_id, $params, $function = null)
- * @method static set_group_add_request($self_id, $params, $function = null)
- * @method static get_login_info($self_id, $params, $function = null)
- * @method static get_stranger_info($self_id, $params, $function = null)
- * @method static get_group_list($self_id, $params, $function = null)
- * @method static get_group_member_info($self_id, $params, $function = null)
- * @method static get_group_member_list($self_id, $params, $function = null)
- * @method static get_cookies($self_id, $params, $function = null)
- * @method static get_csrf_token($self_id, $params, $function = null)
- * @method static get_credentials($self_id, $params, $function = null)
- * @method static get_record($self_id, $params, $function = null)
- * @method static get_status($self_id, $params, $function = null)
- * @method static get_version_info($self_id, $params, $function = null)
- * @method static set_restart($self_id, $params, $function = null)
- * @method static set_restart_plugin($self_id, $params, $function = null)
- * @method static clean_data_dir($self_id, $params, $function = null)
- * @method static clean_plugin_log($self_id, $params, $function = null)
- * @method static _get_friend_list($self_id, $params, $function = null)
- * @method static _get_group_info($self_id, $params, $function = null)
- * @method static _get_vip_info($self_id, $params, $function = null)
- * @method static send_private_msg_async($self_id, $params, $function = null)
- * @method static send_group_msg_async($self_id, $params, $function = null)
- * @method static send_discuss_msg_async($self_id, $params, $function = null)
- * @method static send_msg_async($self_id, $params, $function = null)
- * @method static delete_msg_async($self_id, $params, $function = null)
- * @method static set_group_kick_async($self_id, $params, $function = null)
- * @method static set_group_ban_async($self_id, $params, $function = null)
- * @method static set_group_anonymous_ban_async($self_id, $params, $function = null)
- * @method static set_group_whole_ban_async($self_id, $params, $function = null)
- * @method static set_group_admin_async($self_id, $params, $function = null)
- * @method static set_group_anonymous_async($self_id, $params, $function = null)
- * @method static set_group_card_async($self_id, $params, $function = null)
- * @method static set_group_leave_async($self_id, $params, $function = null)
- * @method static set_group_special_title_async($self_id, $params, $function = null)
- * @method static set_discuss_leave_async($self_id, $params, $function = null)
- * @method static set_friend_add_request_async($self_id, $params, $function = null)
- * @method static set_group_add_request_async($self_id, $params, $function = null)
- */
trait CQAPI
{
/**
@@ -73,34 +18,42 @@ trait CQAPI
* @return bool|array
*/
private function processAPI($connection, $reply, $function = null) {
- $api_id = ZMBuf::$atomics["wait_msg_id"]->get();
+ if ($connection->getOption("type") === CONN_WEBSOCKET)
+ return $this->processWebsocketAPI($connection, $reply, $function);
+ else
+ return $this->processHttpAPI($connection, $reply, $function);
+
+
+ }
+
+ public function processWebsocketAPI($connection, $reply, $function = null) {
+ $api_id = ZMBuf::atomic("wait_msg_id")->get();
$reply["echo"] = $api_id;
- ZMBuf::$atomics["wait_msg_id"]->add(1);
+ ZMBuf::atomic("wait_msg_id")->add(1);
EventHandler::callCQAPISend($reply, $connection);
if (is_callable($function)) {
ZMBuf::appendKey("sent_api", $api_id, [
"data" => $reply,
"time" => microtime(true),
"func" => $function,
- "self_id" => $connection->getQQ()
+ "self_id" => $connection->getOption("connect_id")
]);
} elseif ($function === true) {
ZMBuf::appendKey("sent_api", $api_id, [
"data" => $reply,
"time" => microtime(true),
"coroutine" => Co::getuid(),
- "self_id" => $connection->getQQ()
+ "self_id" => $connection->getOption("connect_id")
]);
} else {
ZMBuf::appendKey("sent_api", $api_id, [
"data" => $reply,
"time" => microtime(true),
- "self_id" => $connection->getQQ()
+ "self_id" => $connection->getOption("connect_id")
]);
}
- if ($connection->push(json_encode($reply))) {
- //Console::msg($reply, $connection->getQQ());
- ZMBuf::$atomics["out_count"]->add(1);
+
+ if (server()->push($connection->getFd(), json_encode($reply))) {
if ($function === true) {
Co::suspend();
$data = ZMBuf::get("sent_api")[$api_id];
@@ -114,7 +67,7 @@ trait CQAPI
"status" => "failed",
"retcode" => -1000,
"data" => null,
- "self_id" => $connection->getQQ()
+ "self_id" => $connection->getOption("connect_id")
];
$s = ZMBuf::get("sent_api")[$reply["echo"]];
if (($s["func"] ?? null) !== null)
@@ -124,4 +77,12 @@ trait CQAPI
return false;
}
}
+
+ public function processHttpAPI($connection, $reply, $function = null) {
+ return false;
+ }
+
+ public function __call($name, $arguments) {
+ return false;
+ }
}
diff --git a/src/ZM/API/ZMRobot.php b/src/ZM/API/ZMRobot.php
index ad823906..126382bb 100644
--- a/src/ZM/API/ZMRobot.php
+++ b/src/ZM/API/ZMRobot.php
@@ -11,10 +11,12 @@ use ZM\Exception\RobotNotFoundException;
* Class ZMRobot
* @package ZM\Utils
* @since 1.2
+ * @version V11
*/
class ZMRobot
{
use CQAPI;
+
const API_ASYNC = 1;
const API_NORMAL = 0;
const API_RATE_LIMITED = 2;
@@ -32,22 +34,34 @@ class ZMRobot
*/
public static function get($robot_id) {
$r = ManagerGM::getAllByName('qq');
- foreach($r as $v) {
- if($v->getOption('connect_id') == $robot_id) return new ZMRobot($v);
+ foreach ($r as $v) {
+ if ($v->getOption('connect_id') == $robot_id) return new ZMRobot($v);
}
throw new RobotNotFoundException("机器人 " . $robot_id . " 未连接到框架!");
}
/**
- * @throws RobotNotFoundException
* @return ZMRobot
+ * @throws RobotNotFoundException
*/
public static function getRandom() {
$r = ManagerGM::getAllByName('qq');
- if($r == []) throw new RobotNotFoundException("没有任何机器人连接到框架!");
+ if ($r == []) throw new RobotNotFoundException("没有任何机器人连接到框架!");
return new ZMRobot($r[array_rand($r)]);
}
+ /**
+ * @return ZMRobot[]
+ */
+ public static function getAllRobot() {
+ $r = ManagerGM::getAllByName('qq');
+ $obj = [];
+ foreach($r as $v) {
+ $obj[] = new ZMRobot($v);
+ }
+ return $obj;
+ }
+
public function __construct(ConnectionObject $connection) {
$this->connection = $connection;
}
@@ -62,6 +76,20 @@ class ZMRobot
return $this;
}
+ public function getSelfId() {
+ return $this->connection->getOption('connect_id');
+ }
+
+ /* 下面是 OneBot 标准的 V11 公开 API */
+
+ /**
+ * 发送私聊消息
+ * @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#send_private_msg-%E5%8F%91%E9%80%81%E7%A7%81%E8%81%8A%E6%B6%88%E6%81%AF
+ * @param $user_id
+ * @param $message
+ * @param bool $auto_escape
+ * @return array|bool|null
+ */
public function sendPrivateMsg($user_id, $message, $auto_escape = false) {
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
@@ -73,6 +101,14 @@ class ZMRobot
], $this->callback);
}
+ /**
+ * 发送群消息
+ * @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#send_group_msg-%E5%8F%91%E9%80%81%E7%BE%A4%E6%B6%88%E6%81%AF
+ * @param $group_id
+ * @param $message
+ * @param bool $auto_escape
+ * @return array|bool|null
+ */
public function sendGroupMsg($group_id, $message, $auto_escape = false) {
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
@@ -84,17 +120,15 @@ class ZMRobot
], $this->callback);
}
- public function sendDiscussMsg($discuss_id, $message, $auto_escape = false) {
- return $this->processAPI($this->connection, [
- 'action' => $this->getActionName(__FUNCTION__),
- 'params' => [
- 'discuss_id' => $discuss_id,
- 'message' => $message,
- 'auto_escape' => $auto_escape
- ]
- ], $this->callback);
- }
-
+ /**
+ * 发送消息
+ * @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#send_msg-%E5%8F%91%E9%80%81%E6%B6%88%E6%81%AF
+ * @param $message_type
+ * @param $target_id
+ * @param $message
+ * @param bool $auto_escape
+ * @return array|bool|null
+ */
public function sendMsg($message_type, $target_id, $message, $auto_escape = false) {
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
@@ -107,6 +141,12 @@ class ZMRobot
], $this->callback);
}
+ /**
+ * 撤回消息
+ * @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#delete_msg-%E6%92%A4%E5%9B%9E%E6%B6%88%E6%81%AF
+ * @param $message_id
+ * @return array|bool|null
+ */
public function deleteMsg($message_id) {
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
@@ -116,6 +156,43 @@ class ZMRobot
], $this->callback);
}
+ /**
+ * 获取消息
+ * @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_msg-%E8%8E%B7%E5%8F%96%E6%B6%88%E6%81%AF
+ * @param $message_id
+ * @return array|bool|null
+ */
+ public function getMsg($message_id) {
+ return $this->processAPI($this->connection, [
+ 'action' => $this->getActionName(__FUNCTION__),
+ 'params' => [
+ 'message_id' => $message_id
+ ]
+ ], $this->callback);
+ }
+
+ /**
+ * 获取合并转发消息
+ * @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_forward_msg-%E8%8E%B7%E5%8F%96%E5%90%88%E5%B9%B6%E8%BD%AC%E5%8F%91%E6%B6%88%E6%81%AF
+ * @param $id
+ * @return array|bool|null
+ */
+ public function getForwardMsg($id) {
+ return $this->processAPI($this->connection, [
+ 'action' => $this->getActionName(__FUNCTION__),
+ 'params' => [
+ 'id' => $id
+ ]
+ ], $this->callback);
+ }
+
+ /**
+ * 发送好友赞
+ * @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#send_like-%E5%8F%91%E9%80%81%E5%A5%BD%E5%8F%8B%E8%B5%9E
+ * @param $user_id
+ * @param int $times
+ * @return array|bool|null
+ */
public function sendLike($user_id, $times = 1) {
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
@@ -126,6 +203,14 @@ class ZMRobot
], $this->callback);
}
+ /**
+ * 群组踢人
+ * @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#set_group_kick-%E7%BE%A4%E7%BB%84%E8%B8%A2%E4%BA%BA
+ * @param $group_id
+ * @param $user_id
+ * @param bool $reject_add_request
+ * @return array|bool|null
+ */
public function setGroupKick($group_id, $user_id, $reject_add_request = false) {
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
@@ -137,6 +222,14 @@ class ZMRobot
], $this->callback);
}
+ /**
+ * 群组单人禁言
+ * @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#set_group_ban-%E7%BE%A4%E7%BB%84%E5%8D%95%E4%BA%BA%E7%A6%81%E8%A8%80
+ * @param int $group_id
+ * @param int $user_id
+ * @param int $duration
+ * @return array|bool|null
+ */
public function setGroupBan($group_id, $user_id, $duration = 1800) {
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
@@ -148,6 +241,14 @@ class ZMRobot
], $this->callback);
}
+ /**
+ * 群组匿名用户禁言
+ * @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#set_group_anonymous_ban-%E7%BE%A4%E7%BB%84%E5%8C%BF%E5%90%8D%E7%94%A8%E6%88%B7%E7%A6%81%E8%A8%80
+ * @param $group_id
+ * @param $anonymous_or_flag
+ * @param int $duration
+ * @return array|bool|null
+ */
public function setGroupAnonymousBan($group_id, $anonymous_or_flag, $duration = 1800) {
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
@@ -159,6 +260,13 @@ class ZMRobot
], $this->callback);
}
+ /**
+ * 群组全员禁言
+ * @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#set_group_whole_ban-%E7%BE%A4%E7%BB%84%E5%85%A8%E5%91%98%E7%A6%81%E8%A8%80
+ * @param $group_id
+ * @param bool $enable
+ * @return array|bool|null
+ */
public function setGroupWholeBan($group_id, $enable = true) {
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
@@ -169,6 +277,14 @@ class ZMRobot
], $this->callback);
}
+ /**
+ * 群组设置管理员
+ * @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#set_group_admin-%E7%BE%A4%E7%BB%84%E8%AE%BE%E7%BD%AE%E7%AE%A1%E7%90%86%E5%91%98
+ * @param $group_id
+ * @param $user_id
+ * @param bool $enable
+ * @return array|bool|null
+ */
public function setGroupAdmin($group_id, $user_id, $enable = true) {
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
@@ -180,6 +296,13 @@ class ZMRobot
], $this->callback);
}
+ /**
+ * 群组匿名
+ * @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#set_group_anonymous-%E7%BE%A4%E7%BB%84%E5%8C%BF%E5%90%8D
+ * @param $group_id
+ * @param bool $enable
+ * @return array|bool|null
+ */
public function setGroupAnonymous($group_id, $enable = true) {
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
@@ -190,6 +313,14 @@ class ZMRobot
], $this->callback);
}
+ /**
+ * 设置群名片(群备注)
+ * @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#set_group_card-%E8%AE%BE%E7%BD%AE%E7%BE%A4%E5%90%8D%E7%89%87%E7%BE%A4%E5%A4%87%E6%B3%A8
+ * @param $group_id
+ * @param $user_id
+ * @param string $card
+ * @return array|bool|null
+ */
public function setGroupCard($group_id, $user_id, $card = "") {
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
@@ -201,6 +332,30 @@ class ZMRobot
], $this->callback);
}
+ /**
+ * 设置群名
+ * @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#set_group_name-%E8%AE%BE%E7%BD%AE%E7%BE%A4%E5%90%8D
+ * @param $group_id
+ * @param $group_name
+ * @return array|bool|null
+ */
+ public function setGroupName($group_id, $group_name) {
+ return $this->processAPI($this->connection, [
+ 'action' => $this->getActionName(__FUNCTION__),
+ 'params' => [
+ 'group_id' => $group_id,
+ 'group_name' => $group_name
+ ]
+ ], $this->callback);
+ }
+
+ /**
+ * 退出群组
+ * @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#set_group_leave-%E9%80%80%E5%87%BA%E7%BE%A4%E7%BB%84
+ * @param $group_id
+ * @param bool $is_dismiss
+ * @return array|bool|null
+ */
public function setGroupLeave($group_id, $is_dismiss = false) {
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
@@ -211,6 +366,15 @@ class ZMRobot
], $this->callback);
}
+ /**
+ * 设置群组专属头衔
+ * @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#set_group_special_title-%E8%AE%BE%E7%BD%AE%E7%BE%A4%E7%BB%84%E4%B8%93%E5%B1%9E%E5%A4%B4%E8%A1%94
+ * @param $group_id
+ * @param $user_id
+ * @param string $special_title
+ * @param int $duration
+ * @return array|bool|null
+ */
public function setGroupSpecialTitle($group_id, $user_id, $special_title = "", $duration = -1) {
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
@@ -223,15 +387,14 @@ class ZMRobot
], $this->callback);
}
- public function setDiscussLeave($discuss_id) {
- return $this->processAPI($this->connection, [
- 'action' => $this->getActionName(__FUNCTION__),
- 'params' => [
- 'discuss_id' => $discuss_id
- ]
- ], $this->callback);
- }
-
+ /**
+ * 处理加好友请求
+ * @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#set_friend_add_request-%E5%A4%84%E7%90%86%E5%8A%A0%E5%A5%BD%E5%8F%8B%E8%AF%B7%E6%B1%82
+ * @param $flag
+ * @param bool $approve
+ * @param string $remark
+ * @return array|bool|null
+ */
public function setFriendAddRequest($flag, $approve = true, $remark = "") {
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
@@ -243,6 +406,15 @@ class ZMRobot
], $this->callback);
}
+ /**
+ * 处理加群请求/邀请
+ * @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#set_group_add_request-%E5%A4%84%E7%90%86%E5%8A%A0%E7%BE%A4%E8%AF%B7%E6%B1%82%E9%82%80%E8%AF%B7
+ * @param $flag
+ * @param $sub_type
+ * @param bool $approve
+ * @param string $reason
+ * @return array|bool|null
+ */
public function setGroupAddRequest($flag, $sub_type, $approve = true, $reason = "") {
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
@@ -255,10 +427,22 @@ class ZMRobot
], $this->callback);
}
+ /**
+ * 获取登录号信息
+ * @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_login_info-%E8%8E%B7%E5%8F%96%E7%99%BB%E5%BD%95%E5%8F%B7%E4%BF%A1%E6%81%AF
+ * @return array|bool|null
+ */
public function getLoginInfo() {
return $this->processAPI($this->connection, ['action' => $this->getActionName(__FUNCTION__)], $this->callback);
}
+ /**
+ * 获取陌生人信息
+ * @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_stranger_info-%E8%8E%B7%E5%8F%96%E9%99%8C%E7%94%9F%E4%BA%BA%E4%BF%A1%E6%81%AF
+ * @param $user_id
+ * @param bool $no_cache
+ * @return array|bool|null
+ */
public function getStrangerInfo($user_id, $no_cache = false) {
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
@@ -269,18 +453,24 @@ class ZMRobot
], $this->callback);
}
+ /**
+ * 获取好友列表
+ * @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_friend_list-%E8%8E%B7%E5%8F%96%E5%A5%BD%E5%8F%8B%E5%88%97%E8%A1%A8
+ * @return array|bool|null
+ */
public function getFriendList() {
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__)
], $this->callback);
}
- public function getGroupList() {
- return $this->processAPI($this->connection, [
- 'action' => $this->getActionName(__FUNCTION__)
- ], $this->callback);
- }
-
+ /**
+ * 获取群信息
+ * @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_group_info-%E8%8E%B7%E5%8F%96%E7%BE%A4%E4%BF%A1%E6%81%AF
+ * @param $group_id
+ * @param bool $no_cache
+ * @return array|bool|null
+ */
public function getGroupInfo($group_id, $no_cache = false) {
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
@@ -291,6 +481,25 @@ class ZMRobot
], $this->callback);
}
+ /**
+ * 获取群列表
+ * @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_group_list-%E8%8E%B7%E5%8F%96%E7%BE%A4%E5%88%97%E8%A1%A8
+ * @return array|bool|null
+ */
+ public function getGroupList() {
+ return $this->processAPI($this->connection, [
+ 'action' => $this->getActionName(__FUNCTION__)
+ ], $this->callback);
+ }
+
+ /**
+ * 获取群成员信息
+ * @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_group_member_info-%E8%8E%B7%E5%8F%96%E7%BE%A4%E6%88%90%E5%91%98%E4%BF%A1%E6%81%AF
+ * @param $group_id
+ * @param $user_id
+ * @param bool $no_cache
+ * @return array|bool|null
+ */
public function getGroupMemberInfo($group_id, $user_id, $no_cache = false) {
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
@@ -302,30 +511,55 @@ class ZMRobot
], $this->callback);
}
+ /**
+ * 获取群成员列表
+ * @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_group_member_list-%E8%8E%B7%E5%8F%96%E7%BE%A4%E6%88%90%E5%91%98%E5%88%97%E8%A1%A8
+ * @param $group_id
+ * @return array|bool|null
+ */
public function getGroupMemberList($group_id) {
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
'params' => [
'group_id' => $group_id
]
- ]);
+ ], $this->callback);
}
- public function getCookies($domain = "") {
+ /**
+ * 获取群荣誉信息
+ * @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_group_honor_info-%E8%8E%B7%E5%8F%96%E7%BE%A4%E8%8D%A3%E8%AA%89%E4%BF%A1%E6%81%AF
+ * @param $group_id
+ * @param $type
+ * @return array|bool|null
+ */
+ public function getGroupHonorInfo($group_id, $type) {
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
'params' => [
- 'domain' => $domain
+ 'group_id' => $group_id,
+ 'type' => $type
]
- ]);
+ ], $this->callback);
}
+ /**
+ * 获取 CSRF Token
+ * @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_csrf_token-%E8%8E%B7%E5%8F%96-csrf-token
+ * @return array|bool|null
+ */
public function getCsrfToken() {
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__)
], $this->callback);
}
+ /**
+ * 获取 QQ 相关接口凭证
+ * @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_credentials-%E8%8E%B7%E5%8F%96-qq-%E7%9B%B8%E5%85%B3%E6%8E%A5%E5%8F%A3%E5%87%AD%E8%AF%81
+ * @param string $domain
+ * @return array|bool|null
+ */
public function getCredentials($domain = "") {
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
@@ -335,17 +569,29 @@ class ZMRobot
], $this->callback);
}
- public function getRecord($file, $out_format, $full_path = false) {
+ /**
+ * 获取语音
+ * @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_record-%E8%8E%B7%E5%8F%96%E8%AF%AD%E9%9F%B3
+ * @param $file
+ * @param $out_format
+ * @return array|bool|null
+ */
+ public function getRecord($file, $out_format) {
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
'params' => [
'file' => $file,
- 'out_format' => $out_format,
- 'full_path' => $full_path
+ 'out_format' => $out_format
]
], $this->callback);
}
+ /**
+ * 获取图片
+ * @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_image-%E8%8E%B7%E5%8F%96%E5%9B%BE%E7%89%87
+ * @param $file
+ * @return array|bool|null
+ */
public function getImage($file) {
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
@@ -355,31 +601,57 @@ class ZMRobot
], $this->callback);
}
+ /**
+ * 检查是否可以发送图片
+ * @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#can_send_image-%E6%A3%80%E6%9F%A5%E6%98%AF%E5%90%A6%E5%8F%AF%E4%BB%A5%E5%8F%91%E9%80%81%E5%9B%BE%E7%89%87
+ * @return array|bool|null
+ */
public function canSendImage() {
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__)
], $this->callback);
}
+ /**
+ * 检查是否可以发送语音
+ * @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#can_send_record-%E6%A3%80%E6%9F%A5%E6%98%AF%E5%90%A6%E5%8F%AF%E4%BB%A5%E5%8F%91%E9%80%81%E8%AF%AD%E9%9F%B3
+ * @return array|bool|null
+ */
public function canSendRecord() {
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__)
], $this->callback);
}
+ /**
+ * 获取运行状态
+ * @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_status-%E8%8E%B7%E5%8F%96%E8%BF%90%E8%A1%8C%E7%8A%B6%E6%80%81
+ * @return array|bool|null
+ */
public function getStatus() {
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__)
], $this->callback);
}
+ /**
+ * 获取版本信息
+ * @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_version_info-%E8%8E%B7%E5%8F%96%E7%89%88%E6%9C%AC%E4%BF%A1%E6%81%AF
+ * @return array|bool|null
+ */
public function getVersionInfo() {
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__)
], $this->callback);
}
- public function setRestartPlugin($delay = 0) {
+ /**
+ * 重启 OneBot 实现
+ * @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#set_restart-%E9%87%8D%E5%90%AF-onebot-%E5%AE%9E%E7%8E%B0
+ * @param int $delay
+ * @return array|bool|null
+ */
+ public function setRestart($delay = 0) {
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
'params' => [
@@ -388,23 +660,22 @@ class ZMRobot
], $this->callback);
}
- public function cleanDataDir($data_dir) {
- return $this->processAPI($this->connection, [
- 'action' => $this->getActionName(__FUNCTION__),
- 'params' => [
- 'data_dir' => $data_dir
- ]
- ], $this->callback);
- }
-
- public function cleanPluginLog() {
+ /**
+ * 清理缓存
+ * @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#clean_cache-%E6%B8%85%E7%90%86%E7%BC%93%E5%AD%98
+ * @return array|bool|null
+ */
+ public function cleanCache() {
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__)
], $this->callback);
}
- public function getExperimentAPI() {
- return new ZMRobotExperiment($this->connection, $this->callback, $this->prefix);
+ public function callExtendedAPI($action, $params = []) {
+ return $this->processAPI($this->connection, [
+ 'action' => $action,
+ 'params' => $params
+ ], $this->callback);
}
private function getActionName(string $method) {
diff --git a/src/ZM/API/ZMRobotExperiment.php b/src/ZM/API/ZMRobotExperiment.php
deleted file mode 100644
index f7b6a565..00000000
--- a/src/ZM/API/ZMRobotExperiment.php
+++ /dev/null
@@ -1,102 +0,0 @@
-connection = $connection;
- $this->callback = $callback;
- $this->prefix = $prefix;
- }
-
- public function setCallback($callback = true) {
- $this->callback = $callback;
- return $this;
- }
-
- public function setPrefix($prefix = ZMRobot::API_NORMAL) {
- $this->prefix = $prefix;
- return $this;
- }
-
- public function getFriendList($flat = false) {
- return $this->processAPI($this->connection, [
- 'action' => '_' . $this->getActionName(__FUNCTION__),
- 'params' => [
- 'flat' => $flat
- ]
- ], $this->callback);
- }
-
- public function getGroupInfo($group_id) {
- return $this->processAPI($this->connection, [
- 'action' => '_' . $this->getActionName(__FUNCTION__),
- 'params' => [
- 'group_id' => $group_id
- ]
- ], $this->callback);
- }
-
- public function getVipInfo($user_id) {
- return $this->processAPI($this->connection, [
- 'action' => '_' . $this->getActionName(__FUNCTION__),
- 'params' => [
- 'user_id' => $user_id
- ]
- ], $this->callback);
- }
-
- public function getGroupNotice($group_id) {
- return $this->processAPI($this->connection, [
- 'action' => '_' . $this->getActionName(__FUNCTION__),
- 'params' => [
- 'group_id' => $group_id
- ]
- ], $this->callback);
- }
-
- public function sendGroupNotice($group_id, $title, $content) {
- return $this->processAPI($this->connection, [
- 'action' => '_' . $this->getActionName(__FUNCTION__),
- 'params' => [
- 'group_id' => $group_id,
- 'title' => $title,
- 'content' => $content
- ]
- ], $this->callback);
- }
-
- public function setRestart($clean_log = false, $clean_cache = false, $clean_event = false) {
- return $this->processAPI($this->connection, [
- 'action' => '_' . $this->getActionName(__FUNCTION__),
- 'params' => [
- 'clean_log' => $clean_log,
- 'clean_cache' => $clean_cache,
- 'clean_event' => $clean_event
- ]
- ], $this->callback);
- }
-
- private function getActionName(string $method) {
- $prefix = ($this->prefix == ZMRobot::API_ASYNC ? '_async' : ($this->prefix == ZMRobot::API_RATE_LIMITED ? '_rate_limited' : ''));
- $func_name = strtolower(preg_replace('/(?<=[a-z])([A-Z])/', '_$1', $method));
- return $prefix . $func_name;
- }
-}
diff --git a/src/ZM/Annotation/AnnotationParser.php b/src/ZM/Annotation/AnnotationParser.php
index af175e6f..e5f40070 100644
--- a/src/ZM/Annotation/AnnotationParser.php
+++ b/src/ZM/Annotation/AnnotationParser.php
@@ -4,48 +4,15 @@
namespace ZM\Annotation;
use Doctrine\Common\Annotations\AnnotationReader;
-use Co;
-use ZM\ConnectionManager\ConnectionObject;
+use ZM\Annotation\Interfaces\ErgodicAnnotation;
use ZM\Console\Console;
-use ZM\Store\ZMBuf;
-use Error;
-use Exception;
use ReflectionClass;
use ReflectionException;
use ReflectionMethod;
-use ZM\Annotation\CQ\CQAfter;
-use ZM\Annotation\CQ\CQAPIResponse;
-use ZM\Annotation\CQ\CQAPISend;
-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\Annotation\Http\After;
-use ZM\Annotation\Http\Before;
-use ZM\Annotation\Http\Controller;
-use ZM\Annotation\Http\HandleException;
-use ZM\Annotation\Http\Middleware;
-use ZM\Annotation\Http\MiddlewareClass;
-use ZM\Annotation\Http\RequestMapping;
-use Swoole\Timer;
-use ZM\Annotation\Interfaces\CustomAnnotation;
+use ZM\Annotation\Http\{After, Before, Controller, HandleException, Middleware, MiddlewareClass, RequestMapping};
use ZM\Annotation\Interfaces\Level;
use ZM\Annotation\Module\Closed;
-use ZM\Annotation\Module\InitBuffer;
-use ZM\Annotation\Module\LoadBuffer;
-use ZM\Annotation\Module\SaveBuffer;
-use ZM\Annotation\Swoole\OnSave;
-use ZM\Annotation\Swoole\OnStart;
-use ZM\Annotation\Swoole\OnTick;
-use ZM\Annotation\Swoole\SwooleEventAfter;
-use ZM\Annotation\Swoole\SwooleEventAt;
-use ZM\Annotation\Interfaces\Rule;
-use ZM\Event\EventHandler;
-use ZM\Http\MiddlewareInterface;
use ZM\Utils\DataProvider;
-use ZM\Utils\ZMUtil;
class AnnotationParser
{
@@ -54,12 +21,20 @@ class AnnotationParser
private $start_time;
private $annotation_map = [];
+ private $middleware_map = [];
+ private $middlewares = [];
+ /** @var null|AnnotationReader */
+ private $reader = null;
+ private $req_mapping = [];
+
+ /**
+ * AnnotationParser constructor.
+ */
public function __construct() {
$this->start_time = microtime(true);
$this->loadAnnotationClasses();
- ZMBuf::$req_mapping = [];
- ZMBuf::$req_mapping[0] = [
+ $this->req_mapping[0] = [
'id' => 0,
'pid' => -1,
'name' => '/'
@@ -71,15 +46,17 @@ class AnnotationParser
* @throws ReflectionException
*/
public function registerMods() {
- foreach($this->path_list as $path) {
+ foreach ($this->path_list as $path) {
+ Console::debug("parsing annotation in ".$path[0]);
$all_class = getAllClasses($path[0], $path[1]);
- $reader = new AnnotationReader();
+ $this->reader = new AnnotationReader();
foreach ($all_class as $v) {
Console::debug("正在检索 " . $v);
$reflection_class = new ReflectionClass($v);
$methods = $reflection_class->getMethods(ReflectionMethod::IS_PUBLIC);
- $class_annotations = $reader->getClassAnnotations($reflection_class);
+ $class_annotations = $this->reader->getClassAnnotations($reflection_class);
+ // 这段为新加的:start
//这里将每个类里面所有的类注解、方法注解通通加到一颗大树上,后期解析
/*
$annotation_map: {
@@ -97,222 +74,116 @@ class AnnotationParser
}
}
*/
+
+ // 生成主树
$this->annotation_map[$v]["class_annotations"] = $class_annotations;
$this->annotation_map[$v]["methods"] = $methods;
foreach ($methods as $method) {
- $this->annotation_map[$v]["methods_annotations"][$method->getName()] = $reader->getMethodAnnotations($method);
+ $this->annotation_map[$v]["methods_annotations"][$method->getName()] = $this->reader->getMethodAnnotations($method);
}
- /*
- $middleware_addon = [];
- foreach ($class_annotations as $vs) {
+
+ foreach ($this->annotation_map[$v]["class_annotations"] as $ks => $vs) {
+ $vs->class = $v;
+
+ //预处理1:将适用于每一个函数的注解到类注解重新注解到每个函数下面
+ if ($vs instanceof ErgodicAnnotation) {
+ foreach ($this->annotation_map[$v]["methods"] as $method) {
+ $copy = clone $vs;
+ $copy->method = $method->getName();
+ $this->annotation_map[$v]["methods_annotations"][$method->getName()][] = $copy;
+ }
+ }
+
+ //预处理2:处理 class 下面的注解
if ($vs instanceof Closed) {
+ unset($this->annotation_map[$v]);
continue 2;
- } elseif ($vs instanceof Controller) {
- Console::debug("找到 Controller 中间件: " . $vs->class);
- $class_prefix = $vs->prefix;
- } elseif ($vs instanceof SaveBuffer) {
- Console::debug("注册自动保存的缓存变量: " . $vs->buf_name . " (Dir:" . $vs->sub_folder . ")");
- DataProvider::addSaveBuffer($vs->buf_name, $vs->sub_folder);
- } elseif ($vs instanceof LoadBuffer) {
- Console::debug("注册到内存的缓存变量: " . $vs->buf_name . " (Dir:" . $vs->sub_folder . ")");
- ZMBuf::set($vs->buf_name, DataProvider::getJsonData(($vs->sub_folder ?? "") . "/" . $vs->buf_name . ".json"));
- } elseif ($vs instanceof InitBuffer) {
- ZMBuf::set($vs->buf_name, []);
} elseif ($vs instanceof MiddlewareClass) {
Console::verbose("正在注册中间件 " . $reflection_class->getName());
- $result = [
- "class" => "\\" . $reflection_class->getName()
- ];
- foreach ($methods as $vss) {
- if ($vss->getName() == "getName") {
- /** @var MiddlewareInterface $tmp *
- $tmp = new $v();
- $result["name"] = $tmp->getName();
- continue;
- }
- $method_annotations = $reader->getMethodAnnotations($vss);
- foreach ($method_annotations as $vsss) {
- if ($vss instanceof Rule) $vss = self::registerRuleEvent($vsss, $vss, $reflection_class);
- else $vss = self::registerMethod($vsss, $vss, $reflection_class);
- //echo get_class($vsss) . PHP_EOL;
- if ($vsss instanceof Before) $result["before"] = $vsss->method;
- if ($vsss instanceof After) $result["after"] = $vsss->method;
- if ($vsss instanceof HandleException) {
- $result["exceptions"][$vsss->class_name] = $vsss->method;
- }
- }
- }
- ZMBuf::$events[MiddlewareClass::class][$result["name"]] = $result;
- continue 2;
- } elseif ($vs instanceof Middleware) {
- $middleware_addon[] = $vs;
- } elseif ($vs instanceof CustomAnnotation) {
- $vs->class = $reflection_class->getName();
- ZMBuf::$events[get_class($vs)][] = $vs;
+ $rs = $this->registerMiddleware($vs, $reflection_class);
+ $this->middlewares[$rs["name"]] = $rs;
}
}
- foreach ($methods as $vs) {
- if ($middleware_addon !== []) {
- foreach ($middleware_addon as $value) {
- Console::debug("Added middleware " . $value->middleware . " to $v -> " . $vs->getName());
- ZMBuf::$events[MiddlewareInterface::class][$v][$vs->getName()][] = $value->middleware;
+
+ //预处理3:处理每个函数上面的特殊注解,就是需要操作一些东西的
+ foreach ($this->annotation_map[$v]["methods_annotations"] as $method_name => $methods_annotations) {
+ foreach ($methods_annotations as $method_anno) {
+ /** @var AnnotationBase $method_anno */
+ $method_anno->class = $v;
+ $method_anno->method = $method_name;
+ if ($method_anno instanceof RequestMapping) {
+ $this->registerRequestMapping($method_anno, $method_name, $v, $methods_annotations); //TODO: 用symfony的routing重写
+ } elseif ($method_anno instanceof Middleware) {
+ $this->middleware_map[$method_anno->class][$method_anno->method][] = $method_anno->middleware;
}
}
- $method_annotations = $reader->getMethodAnnotations($vs);
- foreach ($method_annotations as $vss) {
- if ($vss instanceof Rule) $vss = self::registerRuleEvent($vss, $vs, $reflection_class);
- else $vss = self::registerMethod($vss, $vs, $reflection_class);
- Console::debug("寻找 " . $vs->getName() . " -> " . get_class($vss));
-
- if ($vss instanceof SwooleEventAt) ZMBuf::$events[SwooleEventAt::class][] = $vss;
- elseif ($vss instanceof SwooleEventAfter) ZMBuf::$events[SwooleEventAfter::class][] = $vss;
- elseif ($vss instanceof CQMessage) ZMBuf::$events[CQMessage::class][] = $vss;
- elseif ($vss instanceof CQNotice) ZMBuf::$events[CQNotice::class][] = $vss;
- elseif ($vss instanceof CQRequest) ZMBuf::$events[CQRequest::class][] = $vss;
- elseif ($vss instanceof CQMetaEvent) ZMBuf::$events[CQMetaEvent::class][] = $vss;
- elseif ($vss instanceof CQCommand) ZMBuf::$events[CQCommand::class][] = $vss;
- elseif ($vss instanceof RequestMapping) {
- self::registerRequestMapping($vss, $vs, $reflection_class, $class_prefix);
- } elseif ($vss instanceof CustomAnnotation) ZMBuf::$events[get_class($vss)][] = $vss;
- elseif ($vss instanceof CQBefore) ZMBuf::$events[CQBefore::class][$vss->cq_event][] = $vss;
- elseif ($vss instanceof CQAfter) ZMBuf::$events[CQAfter::class][$vss->cq_event][] = $vss;
- elseif ($vss instanceof OnStart) ZMBuf::$events[OnStart::class][] = $vss;
- elseif ($vss instanceof OnSave) ZMBuf::$events[OnSave::class][] = $vss;
- elseif ($vss instanceof Middleware) ZMBuf::$events[MiddlewareInterface::class][$vss->class][$vss->method][] = $vss->middleware;
- elseif ($vss instanceof OnTick) self::addTimerTick($vss);
- elseif ($vss instanceof CQAPISend) ZMBuf::$events[CQAPISend::class][] = $vss;
- elseif ($vss instanceof CQAPIResponse) ZMBuf::$events[CQAPIResponse::class][$vss->retcode] = [$vss->class, $vss->method];
- }
- }*/
+ }
}
}
-
-
- $tree = self::genTree(ZMBuf::$req_mapping);
- ZMBuf::$req_mapping = $tree[0];
- //给支持level的排个序
+ //预处理4:生成路由树(换成symfony后就不需要了)
+ $tree = $this->genTree($this->req_mapping);
+ $this->req_mapping = $tree[0];
Console::debug("解析注解完毕!");
- if (ZMBuf::isset("timer_count")) {
- Console::info("Added " . ZMBuf::get("timer_count") . " timer(s)!");
- ZMBuf::unsetCache("timer_count");
- }
}
- public function sortLevels() {
- foreach (ZMBuf::$events as $class_name => $v) {
- if (is_a($class_name, Level::class, true)) {
- for ($i = 0; $i < count(ZMBuf::$events[$class_name]) - 1; ++$i) {
- for ($j = 0; $j < count(ZMBuf::$events[$class_name]) - $i - 1; ++$j) {
- $l1 = ZMBuf::$events[$class_name][$j]->level;
- $l2 = ZMBuf::$events[$class_name][$j + 1]->level;
- if ($l1 < $l2) {
- $t = ZMBuf::$events[$class_name][$j + 1];
- ZMBuf::$events[$class_name][$j + 1] = ZMBuf::$events[$class_name][$j];
- ZMBuf::$events[$class_name][$j] = $t;
- }
- }
+ /**
+ * @return array
+ */
+ public function generateAnnotationEvents() {
+ $o = [];
+ foreach ($this->annotation_map as $module => $obj) {
+ foreach ($obj["class_annotations"] as $class_annotation) {
+ if ($class_annotation instanceof ErgodicAnnotation) continue;
+ else $o[get_class($class_annotation)][] = $class_annotation;
+ }
+ foreach ($obj["methods_annotations"] as $method_name => $methods_annotations) {
+ foreach ($methods_annotations as $annotation) {
+ $o[get_class($annotation)][] = $annotation;
}
}
+
}
+ foreach ($o as $k => $v) {
+ $this->sortByLevel($o, $k);
+ }
+ return $o;
}
- public static function getRuleCallback($rule_str) {
- $func = null;
- $rule = $rule_str;
- if ($rule != "") {
- $asp = explode(":", $rule);
- $asp_name = array_shift($asp);
- $rest = implode(":", $asp);
- //Swoole 事件时走此switch
- switch ($asp_name) {
- case "connectType": //websocket连接类型
- $func = function (?ConnectionObject $connection) use ($rest) {
- if ($connection === null) return false;
- return $connection->getName() == $rest ? true : false;
- };
- break;
- case "containsGet": //handle http request事件时才能用
- case "containsPost":
- $get_list = explode(",", $rest);
- if ($asp_name == "containsGet")
- $func = function ($request) use ($get_list) {
- foreach ($get_list as $v) if (!isset($request->get[$v])) return false;
- return true;
- };
- else
- $func = function ($request) use ($get_list) {
- foreach ($get_list as $v) if (!isset($request->post[$v])) return false;
- return true;
- };
- /*
- if ($controller_prefix != '') {
- $p = ZMBuf::$req_mapping_node;
- $prefix_exp = explode("/", $controller_prefix);
- foreach ($prefix_exp as $k => $v) {
- if ($v == "" || $v == ".." || $v == ".") {
- unset($prefix_exp[$k]);
- }
- }
- while (($shift = array_shift($prefix_exp)) !== null) {
- $p->addRoute($shift, new MappingNode($shift));
- $p = $p->getRoute($shift);
- }
- if ($p->getNodeName() != "/") {
- $p->setMethod($method->getName());
- $p->setClass($class->getName());
- $p->setRule($func);
- return "mapped";
- }
- }*/
- break;
- case "containsJson": //handle http request事件时才能用
- $json_list = explode(",", $rest);
- $func = function ($json) use ($json_list) {
- foreach ($json_list as $v) if (!isset($json[$v])) return false;
- return true;
- };
- break;
- case "dataEqual": //handle websocket message事件时才能用
- $func = function ($data) use ($rest) {
- return $data == $rest;
- };
- break;
- }
- switch ($asp_name) {
- case "msgMatch": //handle cq message事件时才能用
- $func = function ($msg) use ($rest) {
- return matchPattern($rest, $msg);
- };
- break;
- case "msgEqual": //handle cq message事件时才能用
- $func = function ($msg) use ($rest) {
- return trim($msg) == $rest;
- };
- break;
+ /**
+ * @return array
+ */
+ public function getMiddlewares() { return $this->middlewares; }
+ /**
+ * @return array
+ */
+ public function getMiddlewareMap() { return $this->middleware_map; }
+
+ /**
+ * @return array
+ */
+ public function getReqMapping() { return $this->req_mapping; }
+
+ /**
+ * @param $path
+ * @param $indoor_name
+ */
+ public function addRegisterPath($path, $indoor_name) { $this->path_list[] = [$path, $indoor_name]; }
+
+ //private function below
+
+ private function registerRequestMapping(RequestMapping $vss, $method, $class, $methods_annotations) {
+ $prefix = '';
+ foreach ($methods_annotations as $annotation) {
+ if ($annotation instanceof Controller) {
+ $prefix = $annotation->prefix;
+ break;
}
}
- return $func;
- }
-
- public static function registerRuleEvent(?AnnotationBase $vss, ReflectionMethod $method, ReflectionClass $class) {
- $vss->callback = self::getRuleCallback($vss->getRule());
- $vss->method = $method->getName();
- $vss->class = $class->getName();
- return $vss;
- }
-
- public static function registerMethod(?AnnotationBase $vss, ReflectionMethod $method, ReflectionClass $class) {
- $vss->method = $method->getName();
- $vss->class = $class->getName();
- return $vss;
- }
-
- private static function registerRequestMapping(RequestMapping $vss, ReflectionMethod $method, ReflectionClass $class, string $prefix) {
- $array = ZMBuf::$req_mapping;
+ $array = $this->req_mapping;
$uid = count($array);
$prefix_exp = explode("/", $prefix);
$route_exp = explode("/", $vss->route);
@@ -327,10 +198,10 @@ class AnnotationParser
}
}
if ($prefix_exp == [] && $route_exp == []) {
- $array[0]['method'] = $method->getName();
- $array[0]['class'] = $class->getName();
+ $array[0]['method'] = $method;
+ $array[0]['class'] = $class;
$array[0]['request_method'] = $vss->request_method;
- ZMBuf::$req_mapping = $array;
+ $this->req_mapping = $array;
return;
}
$pid = 0;
@@ -373,16 +244,19 @@ class AnnotationParser
];
$pid = $uid - 1;
}
- $array[$uid - 1]['method'] = $method->getName();
- $array[$uid - 1]['class'] = $class->getName();
+ $array[$uid - 1]['method'] = $method;
+ $array[$uid - 1]['class'] = $class;
$array[$uid - 1]['request_method'] = $vss->request_method;
- ZMBuf::$req_mapping = $array;
+ $array[$uid - 1]['route'] = $vss->route;
+ $this->req_mapping = $array;
}
+ /** @noinspection PhpIncludeInspection */
private function loadAnnotationClasses() {
$class = getAllClasses(WORKING_DIR . "/src/ZM/Annotation/", "ZM\\Annotation");
foreach ($class as $v) {
$s = WORKING_DIR . '/src/' . str_replace("\\", "/", $v) . ".php";
+ //Console::debug("Requiring annotation " . $s);
require_once $s;
}
$class = getAllClasses(DataProvider::getWorkingDir() . "/src/Custom/Annotation/", "Custom\\Annotation");
@@ -393,7 +267,7 @@ class AnnotationParser
}
}
- public static function genTree($items) {
+ private function genTree($items) {
$tree = array();
foreach ($items as $item)
if (isset($items[$item['pid']]))
@@ -403,48 +277,33 @@ class AnnotationParser
return $tree;
}
- private static function addTimerTick(?OnTick $vss) {
- ZMBuf::set("timer_count", ZMBuf::get("timer_count", 0) + 1);
- $class = ZMUtil::getModInstance($vss->class);
- $method = $vss->method;
- $ms = $vss->tick_ms;
- $cid = go(function () use ($class, $method, $ms) {
- Co::suspend();
- $plain_class = get_class($class);
- if (!isset(ZMBuf::$events[MiddlewareInterface::class][$plain_class][$method])) {
- Console::debug("Added timer: " . $plain_class . " -> " . $method);
- Timer::tick($ms, function () use ($class, $method) {
- set_coroutine_params([]);
- try {
- $class->$method();
- } catch (Exception $e) {
- Console::error("Uncaught error from TimerTick: " . $e->getMessage() . " at " . $e->getFile() . "({$e->getLine()})");
- } catch (Error $e) {
- Console::error("Uncaught fatal error from TimerTick: " . $e->getMessage());
- echo Console::setColor($e->getTraceAsString(), "gray");
- Console::error("Please check your code!");
- }
- });
- } else {
- Console::debug("Added Middleware-based timer: " . $plain_class . " -> " . $method);
- Timer::tick($ms, function () use ($class, $method) {
- set_coroutine_params([]);
- try {
- EventHandler::callWithMiddleware($class, $method, [], []);
- } catch (Exception $e) {
- Console::error("Uncaught error from TimerTick: " . $e->getMessage() . " at " . $e->getFile() . "({$e->getLine()})");
- } catch (Error $e) {
- Console::error("Uncaught fatal error from TimerTick: " . $e->getMessage());
- echo Console::setColor($e->getTraceAsString(), "gray");
- Console::error("Please check your code!");
- }
- });
+ private function registerMiddleware(MiddlewareClass $vs, ReflectionClass $reflection_class) {
+ $result = [
+ "class" => "\\" . $reflection_class->getName(),
+ "name" => $vs->name
+ ];
+
+ foreach ($reflection_class->getMethods() as $vss) {
+ $method_annotations = $this->reader->getMethodAnnotations($vss);
+ foreach ($method_annotations as $vsss) {
+ if ($vsss instanceof Before) $result["before"] = $vss->getName();
+ if ($vsss instanceof After) $result["after"] = $vss->getName();
+ if ($vsss instanceof HandleException) {
+ $result["exceptions"][$vsss->class_name] = $vss->getName();
+ }
}
- });
- ZMBuf::append("paused_tick", $cid);
+ }
+ return $result;
}
- public function addRegisterPath($path, $indoor_name) {
- $this->path_list[] = [$path, $indoor_name];
+ private function sortByLevel(&$events, string $class_name, $prefix = "") {
+ if (is_a($class_name, Level::class, true)) {
+ $class_name .= $prefix;
+ usort($events[$class_name], function ($a, $b) {
+ $left = $a->level;
+ $right = $b->level;
+ return $left > $right ? -1 : ($left == $right ? 0 : 1);
+ });
+ }
}
}
diff --git a/src/ZM/Annotation/Http/MiddlewareClass.php b/src/ZM/Annotation/Http/MiddlewareClass.php
index 4d182aae..58f98d1d 100644
--- a/src/ZM/Annotation/Http/MiddlewareClass.php
+++ b/src/ZM/Annotation/Http/MiddlewareClass.php
@@ -4,6 +4,7 @@
namespace ZM\Annotation\Http;
+use Doctrine\Common\Annotations\Annotation\Required;
use Doctrine\Common\Annotations\Annotation\Target;
use ZM\Annotation\AnnotationBase;
@@ -15,5 +16,9 @@ use ZM\Annotation\AnnotationBase;
*/
class MiddlewareClass extends AnnotationBase
{
-
-}
\ No newline at end of file
+ /**
+ * @var string
+ * @Required()
+ */
+ public $name = '';
+}
diff --git a/src/ZM/Annotation/Module/InitBuffer.php b/src/ZM/Annotation/Module/InitBuffer.php
deleted file mode 100644
index a541004e..00000000
--- a/src/ZM/Annotation/Module/InitBuffer.php
+++ /dev/null
@@ -1,19 +0,0 @@
-level = $level;
}
-}
\ No newline at end of file
+}
diff --git a/src/ZM/Annotation/Swoole/SwooleSetup.php b/src/ZM/Annotation/Swoole/SwooleSetup.php
new file mode 100644
index 00000000..d0037379
--- /dev/null
+++ b/src/ZM/Annotation/Swoole/SwooleSetup.php
@@ -0,0 +1,18 @@
+setDescription("Run a simple http server | 启动一个简单的文件 HTTP 服务器");
+ $this->setHelp("直接运行可以启动");
+ $this->addArgument('dir', InputArgument::OPTIONAL, 'Your directory');
+ $this->addOption("host", 'H', InputOption::VALUE_REQUIRED, "启动监听地址");
+ $this->addOption("port", 'P', InputOption::VALUE_REQUIRED, "启动监听地址的端口");
+ // ...
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ $tty_width = explode(" ", trim(exec("stty size")))[1];
+ $global = ZMConfig::get("global");
+ $host = $input->getOption("host") ?? $global["host"];
+ $port = $input->getOption("port") ?? $global["port"];
+ $server = new Server($host, $port);
+ Console::init(2, $server);
+ $index = ["index.html", "index.htm"];
+ $server->on("request", function (Request $request, Response $response) use ($input, $index){
+ HttpUtil::handleStaticPage(
+ $request->server["request_uri"],
+ $response,
+ [
+ "document_root" => realpath($input->getArgument('dir') ?? '.'),
+ "document_index" => $index
+ ]);
+ });
+ $server->on("start", function($server){
+ Process::signal(SIGINT, function () use ($server){
+ Console::warning("Server interrupted by keyboard.");
+ $server->shutdown();
+ $server->stop();
+ });
+ Console::success("Server started. Use Ctrl+C to stop.");
+ });
+ $out = [
+ "host" => $host,
+ "port" => $port,
+ "document_root" => realpath($input->getArgument('dir') ?? '.'),
+ "document_index" => implode(", ", $index)
+ ];
+ Console::printProps($out, $tty_width);
+ $server->start();
+ // return this if there was no problem running the command
+ // (it's equivalent to returning int(0))
+ return Command::SUCCESS;
+
+ // or return this if some error happened during the execution
+ // (it's equivalent to returning int(1))
+ // return Command::FAILURE;
+ }
+}
diff --git a/src/ZM/Command/RunServerCommand.php b/src/ZM/Command/RunServerCommand.php
index 2dac62fe..9ffd6614 100644
--- a/src/ZM/Command/RunServerCommand.php
+++ b/src/ZM/Command/RunServerCommand.php
@@ -3,7 +3,6 @@
namespace ZM\Command;
-use Framework\FrameworkLoader;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
@@ -26,6 +25,8 @@ class RunServerCommand extends Command
$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)");
// ...
}
@@ -34,6 +35,7 @@ class RunServerCommand extends Command
if(($opt = $input->getOption("env")) !== null) {
if(!in_array($opt, ["production", "staging", "development"])) {
$output->writeln(" \"--env\" option only accept production, development and staging ! ");
+ return Command::FAILURE;
}
}
// ... put here the code to run in your command
diff --git a/src/ZM/ConsoleApplication.php b/src/ZM/ConsoleApplication.php
index 9c81b63d..9614b11f 100644
--- a/src/ZM/ConsoleApplication.php
+++ b/src/ZM/ConsoleApplication.php
@@ -5,12 +5,17 @@ namespace ZM;
use Exception;
+use Symfony\Component\Console\Command\Command;
+use TypeError;
use ZM\Command\BuildCommand;
use ZM\Command\InitCommand;
+use ZM\Command\PureHttpCommand;
use ZM\Command\RunServerCommand;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
+use ZM\Config\ZMConfig;
+use ZM\Utils\DataProvider;
class ConsoleApplication extends Application
{
@@ -22,9 +27,42 @@ class ConsoleApplication extends Application
public function initEnv() {
$this->addCommands([
new RunServerCommand(), //运行主服务的指令控制器
- new InitCommand() //初始化用的,用于项目初始化和phar初始化
+ new InitCommand(), //初始化用的,用于项目初始化和phar初始化
+ new PureHttpCommand()
]);
- if (LOAD_MODE === 0) $this->add(new BuildCommand()); //只有在git源码模式才能使用打包指令
+ //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;
+ if (file_exists(DataProvider::getWorkingDir() . "/vendor/autoload.php")) {
+ /** @noinspection PhpIncludeInspection */
+ require_once DataProvider::getWorkingDir() . "/vendor/autoload.php";
+ }
+ if (LOAD_MODE == 2) {
+ // Phar 模式,2.0 不提供哦
+ //require_once FRAMEWORK_DIR . "/vendor/autoload.php";
+ spl_autoload_register('phar_classloader');
+ } elseif (LOAD_MODE == 0) {
+ /** @noinspection PhpIncludeInspection
+ * @noinspection RedundantSuppression
+ */
+ require_once WORKING_DIR . "/vendor/autoload.php";
+ }
+
+ if (!is_dir(DataProvider::getWorkingDir() . '/src/')) {
+ die("Unable to find source directory.\nMaybe you need to run \"init\"?");
+ }
+ ZMConfig::setDirectory(DataProvider::getWorkingDir().'/config');
+ ZMConfig::env($args["env"] ?? "");
+ if(ZMConfig::get("global") === false) die("Global config load failed: ".ZMConfig::$last_error);
+
+ $command_register = ZMConfig::get("global", "command_register_class") ?? [];
+ foreach($command_register as $v) {
+ $obj = new $v();
+ if(!($obj instanceof Command)) throw new TypeError("Command register class must be extended by Symfony\\Component\\Console\\Command\\Command");
+ $this->add($obj);
+ }
}
/**
diff --git a/src/ZM/Context/Context.php b/src/ZM/Context/Context.php
index 503b7526..850a4e43 100644
--- a/src/ZM/Context/Context.php
+++ b/src/ZM/Context/Context.php
@@ -18,6 +18,7 @@ use ZM\Store\ZMBuf;
class Context implements ContextInterface
{
+ public static $context = [];
private $cid;
public function __construct($cid) { $this->cid = $cid; }
@@ -25,31 +26,31 @@ class Context implements ContextInterface
/**
* @return swoole_server|null
*/
- public function getServer() { return ZMBuf::$context[$this->cid]["server"] ?? null; }
+ public function getServer() { return self::$context[$this->cid]["server"] ?? null; }
/**
* @return Frame|null
*/
- public function getFrame() { return ZMBuf::$context[$this->cid]["frame"] ?? null; }
+ public function getFrame() { return self::$context[$this->cid]["frame"] ?? null; }
- public function getFd() { return ZMBuf::$context[$this->cid]["fd"] ?? $this->getFrame()->fd ?? null; }
+ public function getFd() { return self::$context[$this->cid]["fd"] ?? $this->getFrame()->fd ?? null; }
/**
* @return array|null
*/
- public function getData() { return ZMBuf::$context[$this->cid]["data"] ?? null; }
+ public function getData() { return self::$context[$this->cid]["data"] ?? null; }
- public function setData($data) { ZMBuf::$context[$this->cid]["data"] = $data; }
+ public function setData($data) { self::$context[$this->cid]["data"] = $data; }
/**
* @return Request|null
*/
- public function getRequest() { return ZMBuf::$context[$this->cid]["request"] ?? null; }
+ public function getRequest() { return self::$context[$this->cid]["request"] ?? null; }
/**
* @return Response|null
*/
- public function getResponse() { return ZMBuf::$context[$this->cid]["response"] ?? null; }
+ public function getResponse() { return self::$context[$this->cid]["response"] ?? null; }
/** @return ConnectionObject|null */
public function getConnection() { return ManagerGM::get($this->getFd()); }
@@ -67,33 +68,33 @@ class Context implements ContextInterface
return $conn instanceof ConnectionObject ? new ZMRobot($conn) : null;
}
- public function getMessage() { return ZMBuf::$context[$this->cid]["data"]["message"] ?? null; }
+ public function getMessage() { return self::$context[$this->cid]["data"]["message"] ?? null; }
- public function setMessage($msg) { ZMBuf::$context[$this->cid]["data"]["message"] = $msg; }
+ public function setMessage($msg) { self::$context[$this->cid]["data"]["message"] = $msg; }
public function getUserId() { return $this->getData()["user_id"] ?? null; }
- public function setUserId($id) { ZMBuf::$context[$this->cid]["data"]["user_id"] = $id; }
+ public function setUserId($id) { self::$context[$this->cid]["data"]["user_id"] = $id; }
public function getGroupId() { return $this->getData()["group_id"] ?? null; }
- public function setGroupId($id) { ZMBuf::$context[$this->cid]["data"]["group_id"] = $id; }
+ public function setGroupId($id) { self::$context[$this->cid]["data"]["group_id"] = $id; }
public function getDiscussId() { return $this->getData()["discuss_id"] ?? null; }
- public function setDiscussId($id) { ZMBuf::$context[$this->cid]["data"]["discuss_id"] = $id; }
+ public function setDiscussId($id) { self::$context[$this->cid]["data"]["discuss_id"] = $id; }
public function getMessageType() { return $this->getData()["message_type"] ?? null; }
- public function setMessageType($type) { ZMBuf::$context[$this->cid]["data"]["message_type"] = $type; }
+ public function setMessageType($type) { self::$context[$this->cid]["data"]["message_type"] = $type; }
public function getRobotId() { return $this->getData()["self_id"] ?? null; }
- public function getCache($key) { return ZMBuf::$context[$this->cid]["cache"][$key] ?? null; }
+ public function getCache($key) { return self::$context[$this->cid]["cache"][$key] ?? null; }
- public function setCache($key, $value) { ZMBuf::$context[$this->cid]["cache"][$key] = $value; }
+ public function setCache($key, $value) { self::$context[$this->cid]["cache"][$key] = $value; }
- public function getCQResponse() { return ZMBuf::$context[$this->cid]["cq_response"] ?? null; }
+ public function getCQResponse() { return self::$context[$this->cid]["cq_response"] ?? null; }
/**
* only can used by cq->message event function
@@ -114,8 +115,6 @@ class Context implements ContextInterface
return (new ZMRobot($conn))->setCallback($yield)->sendGroupMsg($data["group_id"], $msg);
case "private":
return (new ZMRobot($conn))->setCallback($yield)->sendPrivateMsg($data["user_id"], $msg);
- case "discuss":
- return (new ZMRobot($conn))->setCallback($yield)->sendDiscussMsg($data["discuss_id"], $msg);
}
return null;
}
@@ -123,7 +122,7 @@ class Context implements ContextInterface
}
public function finalReply($msg, $yield = false) {
- ZMBuf::$context[$this->cid]["cache"]["block_continue"] = true;
+ self::$context[$this->cid]["cache"]["block_continue"] = true;
if ($msg == "") return true;
return $this->reply($msg, $yield);
}
@@ -141,8 +140,8 @@ class Context implements ContextInterface
if (!isset($this->getData()["user_id"], $this->getData()["message"], $this->getData()["self_id"]))
throw new InvalidArgumentException("协程等待参数缺失");
$cid = Co::getuid();
- $api_id = ZMBuf::$atomics["wait_msg_id"]->get();
- ZMBuf::$atomics["wait_msg_id"]->add(1);
+ $api_id = ZMBuf::atomic("wait_msg_id")->get();
+ ZMBuf::atomic("wait_msg_id")->add(1);
$hang = [
"coroutine" => $cid,
"user_id" => $this->getData()["user_id"],
@@ -206,9 +205,9 @@ class Context implements ContextInterface
}
public function cloneFromParent() {
- set_coroutine_params(ZMBuf::$context[Co::getPcid()] ?? ZMBuf::$context[$this->cid]);
+ set_coroutine_params(self::$context[Co::getPcid()] ?? self::$context[$this->cid]);
return context();
}
- public function copy() { return ZMBuf::$context[$this->cid]; }
+ public function copy() { return self::$context[$this->cid]; }
}
diff --git a/src/ZM/Event/CQ/MessageEvent.php b/src/ZM/Event/CQ/MessageEvent.php
index ca5d2eb1..734671a5 100644
--- a/src/ZM/Event/CQ/MessageEvent.php
+++ b/src/ZM/Event/CQ/MessageEvent.php
@@ -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());
}
diff --git a/src/ZM/Event/CQ/MetaEvent.php b/src/ZM/Event/CQ/MetaEvent.php
index 52ef7507..78a7671e 100644
--- a/src/ZM/Event/CQ/MetaEvent.php
+++ b/src/ZM/Event/CQ/MetaEvent.php
@@ -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());
}
}
diff --git a/src/ZM/Event/EventDispatcher.php b/src/ZM/Event/EventDispatcher.php
new file mode 100644
index 00000000..3ad6121f
--- /dev/null
+++ b/src/ZM/Event/EventDispatcher.php
@@ -0,0 +1,109 @@
+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);
+ }
+ }
+}
diff --git a/src/ZM/Event/EventHandler.php b/src/ZM/Event/EventHandler.php
index 84631539..c3cbfcac 100644
--- a/src/ZM/Event/EventHandler.php
+++ b/src/ZM/Event/EventHandler.php
@@ -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);
- }
-
- }
}
diff --git a/src/ZM/Event/EventManager.php b/src/ZM/Event/EventManager.php
new file mode 100644
index 00000000..e6546abc
--- /dev/null
+++ b/src/ZM/Event/EventManager.php
@@ -0,0 +1,62 @@
+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!");
+ }
+ });
+ }
+ }
+}
diff --git a/src/ZM/Event/ServerEventHandler.php b/src/ZM/Event/ServerEventHandler.php
index e7f757de..931641be 100644
--- a/src/ZM/Event/ServerEventHandler.php
+++ b/src/ZM/Event/ServerEventHandler.php
@@ -1,80 +1,507 @@
-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);
+ }
+ }
}
}
diff --git a/src/ZM/Event/Swoole/MessageEvent.php b/src/ZM/Event/Swoole/MessageEvent.php
index fc2b8db7..55fde62f 100644
--- a/src/ZM/Event/Swoole/MessageEvent.php
+++ b/src/ZM/Event/Swoole/MessageEvent.php
@@ -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(
diff --git a/src/ZM/Event/Swoole/RequestEvent.php b/src/ZM/Event/Swoole/RequestEvent.php
index 54c1fbeb..f05f7a54 100644
--- a/src/ZM/Event/Swoole/RequestEvent.php
+++ b/src/ZM/Event/Swoole/RequestEvent.php
@@ -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;
}
diff --git a/src/ZM/Event/Swoole/SwooleEvent.php b/src/ZM/Event/Swoole/SwooleEventInterface.php
similarity index 57%
rename from src/ZM/Event/Swoole/SwooleEvent.php
rename to src/ZM/Event/Swoole/SwooleEventInterface.php
index 73b1d54e..2918f3ff 100644
--- a/src/ZM/Event/Swoole/SwooleEvent.php
+++ b/src/ZM/Event/Swoole/SwooleEventInterface.php
@@ -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();
-}
\ No newline at end of file
+}
diff --git a/src/ZM/Event/Swoole/WSCloseEvent.php b/src/ZM/Event/Swoole/WSCloseEvent.php
index 0bd6d214..e06e2086 100644
--- a/src/ZM/Event/Swoole/WSCloseEvent.php
+++ b/src/ZM/Event/Swoole/WSCloseEvent.php
@@ -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], []);
diff --git a/src/ZM/Event/Swoole/WSOpenEvent.php b/src/ZM/Event/Swoole/WSOpenEvent.php
index 1524fa3e..b0117435 100644
--- a/src/ZM/Event/Swoole/WSOpenEvent.php
+++ b/src/ZM/Event/Swoole/WSOpenEvent.php
@@ -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";
- }
+
}
diff --git a/src/ZM/Event/Swoole/WorkerStartEvent.php b/src/ZM/Event/Swoole/WorkerStartEvent.php
deleted file mode 100644
index d5b5911b..00000000
--- a/src/ZM/Event/Swoole/WorkerStartEvent.php
+++ /dev/null
@@ -1,189 +0,0 @@
-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!");
- }
- }
-}
diff --git a/src/ZM/Exception/InterruptException.php b/src/ZM/Exception/InterruptException.php
new file mode 100644
index 00000000..b11016cd
--- /dev/null
+++ b/src/ZM/Exception/InterruptException.php
@@ -0,0 +1,12 @@
+getTtyWidth();
- 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;
- require_once "Utils/DataProvider.php";
- if (file_exists(DataProvider::getWorkingDir() . "/vendor/autoload.php")) {
- /** @noinspection PhpIncludeInspection */
- require_once DataProvider::getWorkingDir() . "/vendor/autoload.php";
- }
- if (LOAD_MODE == 2) {
- // Phar 模式,2.0 不提供哦
- //require_once FRAMEWORK_DIR . "/vendor/autoload.php";
- spl_autoload_register('phar_classloader');
- } elseif (LOAD_MODE == 0) {
- /** @noinspection PhpIncludeInspection
- * @noinspection RedundantSuppression
- */
- require_once WORKING_DIR . "/vendor/autoload.php";
- }
-
- if (!is_dir(DataProvider::getWorkingDir() . '/src/')) {
- die("Unable to find source directory.\nMaybe you need to run \"init\"?");
- }
- ZMConfig::setDirectory(DataProvider::getWorkingDir().'/config');
- ZMConfig::env($args["env"] ?? "");
- if(ZMConfig::get("global") === false) die("Global config load failed: ".ZMConfig::$last_error);
self::$argv = $args;
- $this->defineProperties();
+ //定义常量
+ include_once "global_defines.php";
+
ZMBuf::initAtomic();
- ManagerGM::init(1024, 0.2, [
- [
- "key" => "connect_id",
- "type" => "string",
- "size" => 30
- ]
- ]);
+ try {
+ ManagerGM::init(ZMConfig::get("global", "swoole")["max_connection"] ?? 2048, 0.5, [
+ [
+ "key" => "connect_id",
+ "type" => "string",
+ "size" => 30
+ ],
+ [
+ "key" => "type",
+ "type" => "int"
+ ]
+ ]);
+ } catch (ConnectionManager\TableException $e) {
+ die($e->getMessage());
+ }
//start swoole Framework
$this->selfCheck();
try {
@@ -85,8 +76,6 @@ class Framework
$args["log-theme"] ?? "default",
($o = ZMConfig::get("console_color")) === false ? [] : $o
);
- // 注册 Swoole Server 的事件
- $this->registerServerEvents();
$timezone = ZMConfig::get("global", "timezone") ?? "Asia/Shanghai";
date_default_timezone_set($timezone);
@@ -107,27 +96,13 @@ class Framework
if (($num = ZMConfig::get("global", "swoole")["worker_num"] ?? swoole_cpu_num()) != 1) {
$out["worker_num"] = $num;
}
- $store = "";
- foreach ($out as $k => $v) {
- $line = $k . ": " . $v;
- if (strlen($line) > 19 && $store == "" || $tty_width < 53) {
- Console::log($line);
- } else {
- if ($store === "") $store = str_pad($line, 19, " ", STR_PAD_RIGHT);
- else {
- $store .= (" | " . $line);
- Console::log($store);
- $store = "";
- }
- }
- }
- if ($store != "") Console::log($store);
+ Console::printProps($out, $tty_width);
self::$server->set($this->server_set);
if (file_exists(DataProvider::getWorkingDir() . "/config/motd.txt")) {
$motd = file_get_contents(DataProvider::getWorkingDir() . "/config/motd.txt");
} else {
- $motd = file_get_contents(__DIR__."/../../config/motd.txt");
+ $motd = file_get_contents(__DIR__ . "/../../config/motd.txt");
}
$motd = explode("\n", $motd);
foreach ($motd as $k => $v) {
@@ -137,6 +112,13 @@ class Framework
echo $motd;
global $asd;
$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,
+ ]);
self::$server->start();
} catch (Exception $e) {
Console::error("Framework初始化出现错误,请检查!");
@@ -145,24 +127,6 @@ class Framework
}
}
- private function defineProperties() {
- 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("ZM_MATCH_ALL", 0);
- define("ZM_MATCH_FIRST", 1);
- define("ZM_MATCH_NUMBER", 2);
- define("ZM_MATCH_SECOND", 3);
- define("ZM_BREAKPOINT", 'if(Framework::$argv["debug-mode"]) extract(\Psy\debug(get_defined_vars(), isset($this) ? $this : @get_called_class()));');
- define("BP", ZM_BREAKPOINT);
- define("ZM_DEFAULT_FETCH_MODE", 4);
- }
-
private function selfCheck() {
if (!extension_loaded("swoole")) die("Can not find swoole extension.\n");
if (version_compare(SWOOLE_VERSION, "4.4.13") == -1) die("You must install swoole version >= 4.4.13 !");
@@ -194,20 +158,25 @@ class Framework
$method_annotations = $reader->getMethodAnnotations($vs);
if ($method_annotations != []) {
$annotation = $method_annotations[0];
- if ($annotation instanceof OnEvent) {
+ if ($annotation instanceof HandleEvent) {
$annotation->class = $v;
$annotation->method = $vs->getName();
$event_list[strtolower($annotation->event)] = $annotation;
+ } elseif ($annotation instanceof SwooleSetup) {
+ $annotation->class = $v;
+ $annotation->method = $vs->getName();
+ $c = new $v();
+ $m = $annotation->method;
+ $c->$m();
}
}
}
}
foreach ($event_list as $k => $v) {
self::$server->on($k, function (...$param) use ($v) {
- $c = $v->class;
- //echo $c.PHP_EOL;
- $c = new $c();
- call_user_func_array([$c, $v->method], $param);
+ $c = ZMUtil::getModInstance($v->class);
+ $m = $v->method;
+ $c->$m(...$param);
});
}
}
@@ -232,6 +201,11 @@ class Framework
});
foreach ($args as $x => $y) {
switch ($x) {
+ case 'disable-coroutine':
+ if($y) {
+ $coroutine_mode = false;
+ }
+ break;
case 'debug-mode':
if ($y) {
$coroutine_mode = false;
@@ -273,7 +247,7 @@ class Framework
if ($y) Console::setLevel(4);
break;
case 'log-theme':
- if($y !== null) {
+ if ($y !== null) {
Console::$theme = $y;
}
break;
diff --git a/src/ZM/Http/MiddlewareInterface.php b/src/ZM/Http/MiddlewareInterface.php
index 79c00865..a0ed6969 100644
--- a/src/ZM/Http/MiddlewareInterface.php
+++ b/src/ZM/Http/MiddlewareInterface.php
@@ -6,5 +6,5 @@ namespace ZM\Http;
interface MiddlewareInterface
{
- public function getName();
+
}
diff --git a/src/ZM/Http/StaticFileHandler.php b/src/ZM/Http/StaticFileHandler.php
index f1163405..efc3172e 100644
--- a/src/ZM/Http/StaticFileHandler.php
+++ b/src/ZM/Http/StaticFileHandler.php
@@ -6,6 +6,7 @@ namespace ZM\Http;
use ZM\Console\Console;
use Framework\ZMBuf;
+use ZM\Utils\HttpUtil;
use ZM\Utils\ZMUtil;
class StaticFileHandler
@@ -29,7 +30,7 @@ class StaticFileHandler
}
}
$response->status(404);
- $response->end(ZMUtil::getHttpCodePage(404));
+ $response->end(HttpUtil::getHttpCodePage(404));
return true;
}
}
diff --git a/src/ZM/Module/QQBot.php b/src/ZM/Module/QQBot.php
new file mode 100644
index 00000000..b50e7706
--- /dev/null
+++ b/src/ZM/Module/QQBot.php
@@ -0,0 +1,15 @@
+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);
+ return self::$kv_table->create();
+ }
+
+ /**
+ * @param string $key
+ * @return null|string
+ * @throws Exception
+ */
+ public static function get(string $key) {
+ if (self::$kv_table === null) throw new Exception("not initialized LightCache");
+ self::checkExpire($key);
+ $r = self::$kv_table->get($key);
+ return $r === false ? null : self::parseGet($r);
+ }
+
+ /**
+ * @param string $key
+ * @return mixed|null
+ * @throws Exception
+ */
+ public static function getExpire(string $key) {
+ if (self::$kv_table === null) throw new Exception("not initialized LightCache");
+ self::checkExpire($key);
+ $r = self::$kv_table->get($key, "expire");
+ return $r === false ? null : $r - time();
+ }
+
+ /**
+ * @param string $key
+ * @param string|array|int $value
+ * @param int $expire
+ * @return mixed
+ * @throws Exception
+ */
+ public static function set(string $key, $value, int $expire = -1) {
+ if (self::$kv_table === null) throw new Exception("not initialized LightCache");
+ if (is_array($value) || is_int($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 = "";
+ } 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,
+ "data_type" => $data_type
+ ]);
+ } catch (Exception $e) {
+ return false;
+ }
+ }
+
+ public static function getMemoryUsage() {
+ return self::$kv_table->getMemorySize();
+ }
+
+ /**
+ * @param string $key
+ * @return bool
+ * @throws Exception
+ */
+ public static function isset(string $key) {
+ return self::get($key) !== null;
+ }
+
+ public static function unset(string $key) {
+ return self::$kv_table->del($key);
+ }
+
+ public static function getAll() {
+ $r = [];
+ $del = [];
+ foreach (self::$kv_table as $k => $v) {
+ if ($v["expire"] <= time()) {
+ $del[]=$k;
+ continue;
+ }
+ $r[$k] = self::parseGet($v);
+ }
+ foreach($del as $v) {
+ self::unset($v);
+ }
+ return $r;
+ }
+
+ private static function checkExpire($key) {
+ if (($expire = self::$kv_table->get($key, "expire")) !== -1) {
+ if ($expire <= time()) {
+ self::$kv_table->del($key);
+ }
+ }
+ }
+
+ private static function parseGet($r) {
+ switch ($r["data_type"]) {
+ case "json":
+ return json_decode($r["value"], true);
+ case "":
+ default:
+ return $r["value"];
+ }
+ }
+}
diff --git a/src/ZM/Store/ZMBuf.php b/src/ZM/Store/ZMBuf.php
index 147003a8..60b6e89e 100755
--- a/src/ZM/Store/ZMBuf.php
+++ b/src/ZM/Store/ZMBuf.php
@@ -10,7 +10,6 @@ namespace ZM\Store;
use Swoole\Atomic;
use Swoole\Database\PDOPool;
-use swoole_atomic;
use ZM\Config\ZMConfig;
class ZMBuf
@@ -18,36 +17,19 @@ class ZMBuf
//读写的缓存数据,需要在worker_num = 1下才能正常使用
/** @var mixed[] ZMBuf的data */
private static $cache = [];
- //Scheduler计划任务连接实例,只可以在单worker_num时使用
- static $scheduler = null; //This is stupid warning...
-
//Swoole SQL连接池,多进程下每个进程一个连接池
/** @var PDOPool */
static $sql_pool = null;//保存sql连接池的类
-
- // swoole server操作对象,每个进程均分配
- /** @var \swoole_websocket_server $server */
- static $server;
/** @var array Http请求uri路径根节点 */
public static $req_mapping_node;
- /** @var mixed TimeNLP初始化后的对象,每个进程均可初始化 */
- public static $time_nlp;
- /** @var string[] $custom_connection_class */
- public static $custom_connection_class = [];//保存自定义的ws connection连接类型的
-
- // Atomic:可跨进程读写的原子计数,任何地方均可使用
- /** @var null|swoole_atomic */
- static $info_level = null;//保存log等级的原子计数
+ /** @var array 事件注解的绑定对 */
public static $events = [];
/** @var Atomic[] */
public static $atomics;
- public static $req_mapping = [];
- public static $config = [];
- public static $context = [];
public static $instance = [];
public static $context_class = [];
- public static $server_events = [];
+ public static $terminal = null;
static function get($name, $default = null) {
return self::$cache[$name] ?? $default;
@@ -90,13 +72,8 @@ class ZMBuf
return in_array($val, self::$cache[$name]);
}
- static function config($config_name) {
- return self::$config[$config_name] ?? null;
- }
-
public static function resetCache() {
self::$cache = [];
- self::$time_nlp = null;
self::$instance = [];
}
@@ -107,6 +84,15 @@ class ZMBuf
foreach (ZMConfig::get("global", "init_atomics") as $k => $v) {
self::$atomics[$k] = new Atomic($v);
}
- self::$atomics["show_log_worker"] = new Atomic(999999);
+ self::$atomics["stop_signal"] = new Atomic(0);
+ self::$atomics["wait_msg_id"] = new Atomic(0);
+ }
+
+ /**
+ * @param $name
+ * @return Atomic|null
+ */
+ public static function atomic($name) {
+ return self::$atomics[$name] ?? null;
}
}
diff --git a/src/ZM/Utils/CoroutinePool.php b/src/ZM/Utils/CoroutinePool.php
new file mode 100644
index 00000000..042500b8
--- /dev/null
+++ b/src/ZM/Utils/CoroutinePool.php
@@ -0,0 +1,51 @@
+= (self::$sizes[$name] ?? self::$default_size)) {
+ self::$yields[] = Coroutine::getCid();
+ Coroutine::suspend();
+ }
+ go(function () use ($func, $name) {
+ self::$cids[$name][] = Coroutine::getCid();
+ //Console::debug("正在执行协程,当前协程池中有 " . count(self::$cids[$name]) . " 个正在运行的协程: ".implode(", ", self::$cids[$name]));
+ $func();
+ self::checkCids($name);
+ });
+ }
+
+ public static function defaultSize(int $size) {
+ self::$default_size = $size;
+ }
+
+ public static function setSize($name, int $size) {
+ self::$sizes[$name] = $size;
+ }
+
+ private static function checkCids($name) {
+ if (in_array(Coroutine::getCid(), self::$cids[$name])) {
+ $a = array_search(Coroutine::getCid(), self::$cids[$name]);
+ array_splice(self::$cids[$name], $a, 1);
+ $r = array_shift(self::$yields);
+ if ($r !== null) {
+ Coroutine::resume($r);
+ }
+ }
+ }
+}
diff --git a/src/ZM/Utils/HttpUtil.php b/src/ZM/Utils/HttpUtil.php
new file mode 100644
index 00000000..cc2ab53e
--- /dev/null
+++ b/src/ZM/Utils/HttpUtil.php
@@ -0,0 +1,117 @@
+ $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"]) {
+ HttpUtil::handleStaticPage($request->server["request_uri"], $response);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static function getHttpCodePage(int $http_code) {
+ if (isset(ZMConfig::get("global", "http_default_code_page")[$http_code])) {
+ return Co::readFile(DataProvider::getResourceFolder() . "html/" . ZMConfig::get("global", "http_default_code_page")[$http_code]);
+ } else return null;
+ }
+
+ /**
+ * @param $uri
+ * @param Response|\Swoole\Http\Response $response
+ * @param array $settings
+ * @return bool
+ */
+ public static function handleStaticPage($uri, $response, $settings = []) {
+ $base_dir = $settings["document_root"] ?? ZMConfig::get("global", "static_file_server")["document_root"];
+ $base_index = $settings["document_index"] ?? ZMConfig::get("global", "static_file_server")["document_index"];
+ $path = realpath($base_dir . urldecode($uri));
+ if ($path !== false) {
+ if (is_dir($path)) $path = $path . '/';
+ $work = realpath($base_dir) . '/';
+ if (strpos($path, $work) !== 0) {
+ Console::info("[403] " . $uri);
+ self::responseCodePage($response, 403);
+ return true;
+ }
+ if (is_dir($path)) {
+ if(mb_substr($uri, -1, 1) != "/") {
+ Console::info("[302] " . $uri);
+ $response->redirect($uri."/", 302);
+ return true;
+ }
+ foreach ($base_index as $vp) {
+ if (is_file($path . "/" . $vp)) {
+ Console::info("[200] " . $uri);
+ $exp = strtolower(pathinfo($path . $vp)['extension'] ?? "unknown");
+ $response->setHeader("Content-Type", ZMConfig::get("file_header")[$exp] ?? "application/octet-stream");
+ $response->end(file_get_contents($path . $vp));
+ return true;
+ }
+ }
+ } elseif (is_file($path)) {
+ Console::info("[200] " . $uri);
+ $exp = strtolower(pathinfo($path)['extension'] ?? "unknown");
+ $response->setHeader("Content-Type", ZMConfig::get("file_header")[$exp] ?? "application/octet-stream");
+ $response->end(file_get_contents($path));
+ return true;
+ }
+ }
+ Console::info("[404] " . $uri);
+ self::responseCodePage($response, 404);
+ return true;
+ }
+
+ public static function responseCodePage($response, $code) {
+ $response->status($code);
+ $response->end(self::getHttpCodePage($code));
+ }
+}
diff --git a/src/ZM/Utils/Terminal.php b/src/ZM/Utils/Terminal.php
index 3993e657..70bb7f94 100644
--- a/src/ZM/Utils/Terminal.php
+++ b/src/ZM/Utils/Terminal.php
@@ -5,74 +5,18 @@ namespace ZM\Utils;
use Exception;
-use ZM\Config\ZMConfig;
-use ZM\ConnectionManager\ConnectionObject;
+use Swoole\Event;
use ZM\Console\Console;
-use ZM\Store\ZMBuf;
-use ZM\Annotation\Swoole\SwooleEventAt;
+use ZM\Framework;
class Terminal
{
- /**
- * @var false|resource
- */
- public static $console_proc = null;
- public static $pipes = [];
-
- static function listenConsole($terminal_id) {
- if ($terminal_id === null) {
- if (ZMBuf::$server->worker_id === 0) Console::info("ConsoleCommand disabled.");
- return;
- }
- global $terminal_id;
- global $port;
- $port = ZMConfig::get("global", "port");
- $vss = new SwooleEventAt();
- $vss->type = "open";
- $vss->level = 256;
- $vss->rule = "connectType:terminal";
-
- $vss->callback = function (?ConnectionObject $conn) use ($terminal_id) {
- $req = ctx()->getRequest();
- if ($conn->getName() != "terminal") return false;
- Console::debug("Terminal fd: " . $conn->getFd());
- ZMBuf::set("terminal_fd", $conn->getFd());
- if (($req->header["x-terminal-id"] ?? "") != $terminal_id) {
- ZMBuf::$server->close($conn->getFd());
- return false;
- }
- return false;
- };
- ZMBuf::$events[SwooleEventAt::class][] = $vss;
- $vss2 = new SwooleEventAt();
- $vss2->type = "message";
- $vss2->rule = "connectType:terminal";
- $vss2->callback = function (?ConnectionObject $conn) {
- if ($conn === null) return false;
- if ($conn->getName() != "terminal") return false;
- $cmd = ctx()->getFrame()->data;
- self::executeCommand($cmd);
- return false;
- };
- ZMBuf::$events[SwooleEventAt::class][] = $vss2;
- if (ZMBuf::$server->worker_id === 0) {
- go(function () {
- global $terminal_id, $port;
- $descriptorspec = array(
- 0 => STDIN,
- 1 => STDOUT,
- 2 => STDERR
- );
- self::$console_proc = proc_open('php -r \'$terminal_id = "' . $terminal_id . '";$port = ' . $port . ';require "' . __DIR__ . '/terminal_listener.php";\'', $descriptorspec, self::$pipes);
- });
- }
- }
-
/**
* @param string $cmd
+ * @param $resource
* @return bool
*/
- private static function executeCommand(string $cmd) {
+ public static function executeCommand(string $cmd, $resource) {
$it = explodeMsg($cmd);
switch ($it[0] ?? '') {
case 'logtest':
@@ -88,7 +32,13 @@ class Terminal
$class_name = $it[1];
$function_name = $it[2];
$class = new $class_name([]);
- call_user_func_array([$class, $function_name], []);
+ $class->$function_name();
+ return true;
+ case 'psysh':
+ if (Framework::$argv["disable-coroutine"])
+ eval(\Psy\sh());
+ else
+ Console::error("Only \"--disable-coroutine\" mode can use psysh!!!");
return true;
case 'bc':
$code = base64_decode($it[1] ?? '', true);
@@ -104,18 +54,13 @@ class Terminal
Console::log($it[2], $it[1]);
return true;
case 'stop':
+ Event::del($resource);
ZMUtil::stop();
return false;
case 'reload':
case 'r':
ZMUtil::reload();
return false;
- case 'save':
- //$origin = ZMBuf::$atomics["info_level"]->get();
- //ZMBuf::$atomics["info_level"]->set(3);
- DataProvider::saveBuffer();
- //ZMBuf::$atomics["info_level"]->set($origin);
- return true;
case '':
return true;
default:
diff --git a/src/ZM/Utils/ZMRequest.php b/src/ZM/Utils/ZMRequest.php
deleted file mode 100644
index 2a6b6895..00000000
--- a/src/ZM/Utils/ZMRequest.php
+++ /dev/null
@@ -1,144 +0,0 @@
-set($set == [] ? ['timeout' => 15.0] : $set);
- $cli->setHeaders($headers);
- $cli->get($parse["path"] . (isset($parse["query"]) ? "?" . $parse["query"] : ""));
- if ($return_body) {
- if ($cli->errCode != 0 || $cli->statusCode != 200) return false;
- $a = $cli->body;
- $cli->close();
- return $a;
- } else {
- $cli->close();
- return $cli;
- }
- }
-
- /**
- * 使用Swoole协程客户端发起HTTP POST请求
- * 返回请求后的body
- * 如果请求失败或返回状态不是200,则返回 false
- * @param $url
- * @param array $header
- * @param $data
- * @param array $set
- * @param bool $return_body
- * @return bool|string|Client
- */
- public static function post($url, array $header, $data, $set = [], $return_body = true) {
- /** @var Client $cli */
- list($cli, $parse) = self::getNewClient($url);
- if($cli === null) return false;
- $cli->set($set == [] ? ['timeout' => 15.0] : $set);
- $cli->setHeaders($header);
- $cli->post($parse["path"] . (isset($parse["query"]) ? ("?" . $parse["query"]) : ""), $data);
- if ($return_body) {
- if ($cli->errCode != 0 || $cli->statusCode != 200) return false;
- $a = $cli->body;
- $cli->close();
- return $a;
- } else {
- $cli->close();
- return $cli;
- }
- }
-
- /**
- * @param $url
- * @param array $set
- * @param array $header
- * @return ZMWebSocket
- * @since 1.5
- */
- public static function websocket($url, $set = ['websocket_mask' => true], $header = []) {
- return new ZMWebSocket($url, $set, $header);
- }
-
- /**
- * @param $url
- * @param array $attribute
- * @param bool $return_body
- * @return bool|string|Client
- */
- public static function request($url, $attribute = [], $return_body = true) {
- /** @var Client $cli */
- list($cli, $parse) = self::getNewClient($url);
- if($cli === null) return false;
- $cli->set($attribute["set"] ?? ["timeout" => 15.0]);
- $cli->setMethod($attribute["method"] ?? "GET");
- $cli->setHeaders($attribute["headers"] ?? []);
- if(isset($attribute["data"])) $cli->setData($attribute["data"]);
- if(isset($attribute["file"])) {
- foreach($attribute["file"] as $k => $v) {
- $cli->addFile($v["path"], $v["name"], $v["mime_type"] ?? null, $v["filename"] ?? null, $v["offset"] ?? 0, $v["length"] ?? 0);
- }
- }
- $cli->execute($parse["path"] . (isset($parse["query"]) ? "?" . $parse["query"] : ""));
- if ($return_body) {
- if ($cli->errCode != 0 || $cli->statusCode != 200) return false;
- $a = $cli->body;
- $cli->close();
- return $a;
- } else {
- $cli->close();
- return $cli;
- }
- }
-
- /**
- * @param $url
- * @param null|bool $dst
- * @return bool
- */
- public static function downloadFile($url, $dst = null) {
- /** @var Client $cli */
- list($cli, $parse) = self::getNewClient($url);
- if($cli === null) return false;
- $cli->set(["timeout" => 60.0]);
- $save_path = $dst === null ? "/tmp/_zm_".mt_rand(1000000, 9999999) : $dst;
- $result = $cli->download($parse["path"] . (isset($parse["query"]) ? "?" . $parse["query"] : ""), $save_path);
- if($result === false) return false;
- elseif ($dst === null) return $save_path;
- else return true;
- }
-
- /**
- * @param $url
- * @return bool|array
- */
- private static function getNewClient($url) {
- $parse = parse_url($url);
- if (!isset($parse["host"])) {
- Console::warning("ZMRequest: url must contains scheme such as \"http(s)://\"");
- return false;
- }
- if(!isset($parse["path"])) $parse["path"] = "/";
- $port = $parse["port"] ?? (($parse["scheme"] ?? "http") == "https" ? 443 : 80);
- $cli = new Client($parse["host"], $port, ($parse["scheme"] ?? "http") == "https");
- return [$cli, $parse];
- }
-}
diff --git a/src/ZM/Utils/ZMUtil.php b/src/ZM/Utils/ZMUtil.php
index c4e5515e..31882c37 100644
--- a/src/ZM/Utils/ZMUtil.php
+++ b/src/ZM/Utils/ZMUtil.php
@@ -5,55 +5,45 @@ namespace ZM\Utils;
use Co;
-use ZM\Config\ZMConfig;
+use Exception;
+use Swoole\Event;
+use Swoole\Timer;
use ZM\Console\Console;
-use ZM\API\CQ;
+use ZM\Store\LightCache;
use ZM\Store\ZMBuf;
class ZMUtil
{
- /**
- * 检查workerStart是否运行结束
- */
- public static function checkWait() {
- if (ZMBuf::isset("wait_start")) {
- ZMBuf::append("wait_start", Co::getCid());
- Co::suspend();
+ public static function stop() {
+ Console::warning(Console::setColor("Stopping server...", "red"));
+ if (ZMBuf::$terminal !== null)
+ Event::del(ZMBuf::$terminal);
+ ZMBuf::atomic("stop_signal")->set(1);
+ try {
+ LightCache::set('stop', 'OK');
+ } catch (Exception $e) {
}
+ server()->shutdown();
+ server()->stop();
}
- public static function stop($without_shutdown = false) {
- Console::info(Console::setColor("Stopping server...", "red"));
- foreach ((ZMBuf::$server->connections ?? []) as $v) {
- ZMBuf::$server->close($v);
- }
- DataProvider::saveBuffer();
- if (!$without_shutdown)
- ZMBuf::$server->shutdown();
- ZMBuf::$server->stop();
- }
-
- public static function getHttpCodePage(int $http_code) {
- if (isset(ZMConfig::get("global", "http_default_code_page")[$http_code])) {
- return Co::readFile(DataProvider::getResourceFolder() . "html/" . ZMConfig::get("global", "http_default_code_page")[$http_code]);
- } else return null;
- }
-
- public static function reload() {
+ public static function reload($delay = 800) {
Console::info(Console::setColor("Reloading server...", "gold"));
+ usleep($delay * 1000);
foreach (ZMBuf::get("wait_api", []) as $k => $v) {
if ($v["result"] === null) Co::resume($v["coroutine"]);
}
- foreach (ZMBuf::$server->connections as $v) {
- ZMBuf::$server->close($v);
+ foreach (server()->connections as $v) {
+ server()->close($v);
}
DataProvider::saveBuffer();
- ZMBuf::$server->reload();
+ Timer::clearAll();
+ server()->reload();
}
public static function getModInstance($class) {
if (!isset(ZMBuf::$instance[$class])) {
- Console::debug("Class instance $class not exist, so I created it.");
+ //Console::debug("Class instance $class not exist, so I created it.");
return ZMBuf::$instance[$class] = new $class();
} else {
return ZMBuf::$instance[$class];
diff --git a/src/ZM/Utils/ZMWebSocket.php b/src/ZM/Utils/ZMWebSocket.php
deleted file mode 100644
index cd17b8aa..00000000
--- a/src/ZM/Utils/ZMWebSocket.php
+++ /dev/null
@@ -1,106 +0,0 @@
- true], $header = []) {
- $this->parse = parse_url($url);
- if (!isset($this->parse["host"])) {
- Console::warning("ZMRequest: url must contains scheme such as \"ws(s)://\"");
- return;
- }
- if (!isset($this->parse["path"])) $this->parse["path"] = "/";
- $port = $this->parse["port"] ?? (($this->parse["scheme"] ?? "ws") == "wss" ? 443 : 80);
- $this->client = new Client($this->parse["host"], $port, (($this->parse["scheme"] ?? "ws") == "wss" ? true : false));
- $this->client->set($set);
- if ($header != []) $this->client->setHeaders($header);
- $this->is_available = true;
- }
-
- /**
- * @return bool
- */
- public function upgrade() {
- if (!$this->is_available) return false;
- $r = $this->client->upgrade($this->parse["path"] . (isset($this->parse["query"]) ? ("?" . $this->parse["query"]) : ""));
- if ($r) {
- go(function () {
- while (true) {
- $result = $this->client->recv(60);
- if ($result === false) {
- if ($this->client->connected === false) {
- go(function () {
- call_user_func($this->close_func, $this->client);
- });
- break;
- }
- } elseif ($result instanceof Frame) {
- go(function () use ($result) {
- $this->is_available = false;
- call_user_func($this->message_func, $result, $this->client);
- });
- }
- }
- });
- return true;
- }
- return false;
- }
-
- /**
- * @param callable $callable
- * @return $this
- */
- public function onMessage(callable $callable) {
- $this->message_func = $callable;
- return $this;
- }
-
- /**
- * @param callable $callable
- * @return $this
- */
- public function onClose(callable $callable) {
- $this->close_func = $callable;
- return $this;
- }
-}
-
-if (!debug_backtrace()) {
- go(function () {
- require_once __DIR__ . "/../../Framework/Console.php";
- $cli = new ZMWebSocket("ws://127.0.0.1:20001/");
- if (!$cli->is_available) die("Error!\n");
- $cli->onMessage(function (Frame $frame) {
- var_dump($frame);
- });
- $cli->onClose(function () {
- echo "Connection closed.\n";
- });
- if ($cli->upgrade()) {
- echo "成功连接!\n";
- } else {
- echo "连接失败!\n";
- }
- });
-}
diff --git a/src/ZM/global_defines.php b/src/ZM/global_defines.php
new file mode 100644
index 00000000..eaff3954
--- /dev/null
+++ b/src/ZM/global_defines.php
@@ -0,0 +1,22 @@
+getConnection()->getName() == 'qq';
+}
+
+function connectIsDefault() {
+ return ctx()->getConnection()->getName() == 'default';
+}
+
+function connectIs($type) {
+ return ctx()->getConnection()->getName() == $type;
+}
+
+function getAnnotations() {
+ $s = debug_backtrace()[1];
+ //echo json_encode($s, 128|256);
+ $list = [];
+ foreach(EventManager::$events as $event => $v) {
+ foreach($v as $ks => $vs) {
+ //echo get_class($vs).": ".$vs->class." => ".$vs->method.PHP_EOL;
+ if($vs->class == $s["class"] && $vs->method == $s["function"]) {
+ $list[get_class($vs)][]=$vs;
+ }
+ }
+ }
+ return $list;
+}
+
function set_coroutine_params($array) {
$cid = Co::getCid();
if ($cid == -1) die("Cannot set coroutine params at none coroutine mode.");
- if (isset(ZMBuf::$context[$cid])) ZMBuf::$context[$cid] = array_merge(ZMBuf::$context[$cid], $array);
- else ZMBuf::$context[$cid] = $array;
- foreach (ZMBuf::$context as $c => $v) {
- if (!Co::exists($c)) unset(ZMBuf::$context[$c], ZMBuf::$context_class[$c]);
+ if (isset(Context::$context[$cid])) Context::$context[$cid] = array_merge(Context::$context[$cid], $array);
+ else Context::$context[$cid] = $array;
+ foreach (Context::$context as $c => $v) {
+ if (!Co::exists($c)) unset(Context::$context[$c], ZMBuf::$context_class[$c]);
}
}
@@ -185,13 +214,13 @@ function context() {
function ctx() {
$cid = Co::getCid();
$c_class = ZMConfig::get("global", "context_class");
- if (isset(ZMBuf::$context[$cid])) {
+ if (isset(Context::$context[$cid])) {
return ZMBuf::$context_class[$cid] ?? (ZMBuf::$context_class[$cid] = new $c_class($cid));
} else {
Console::debug("未找到当前协程的上下文($cid),正在找父进程的上下文");
while (($pcid = Co::getPcid($cid)) !== -1) {
$cid = $pcid;
- if (isset(ZMBuf::$context[$cid])) return ZMBuf::$context_class[$cid] ?? (ZMBuf::$context_class[$cid] = new $c_class($cid));
+ if (isset(Context::$context[$cid])) return ZMBuf::$context_class[$cid] ?? (ZMBuf::$context_class[$cid] = new $c_class($cid));
}
return null;
}
@@ -211,21 +240,23 @@ function zm_resume(int $cid) { Co::resume($cid); }
function zm_timer_after($ms, callable $callable) {
go(function () use ($ms, $callable) {
- ZMUtil::checkWait();
Swoole\Timer::after($ms, $callable);
});
}
function zm_timer_tick($ms, callable $callable) {
go(function () use ($ms, $callable) {
- ZMUtil::checkWait();
Console::debug("Adding extra timer tick of " . $ms . " ms");
Swoole\Timer::tick($ms, $callable);
});
}
+function zm_data_hash($v) {
+ return md5($v["user_id"] . "^" . $v["self_id"] . "^" . $v["message_type"] . "^" . ($v[$v["message_type"] . "_id"] ?? $v["user_id"]));
+}
+
function server() {
- return Framework::getServer();
+ return Framework::$server;
}
diff --git a/test/CoroutinePoolTest.php b/test/CoroutinePoolTest.php
new file mode 100644
index 00000000..cefced7b
--- /dev/null
+++ b/test/CoroutinePoolTest.php
@@ -0,0 +1,28 @@
+assertTrue(true);
+ Console::init(4);
+ CoroutinePool::setSize("default", 2);
+ CoroutinePool::defaultSize(50);
+ for ($i = 0; $i < 59; ++$i) {
+ CoroutinePool::go(function () use ($i) {
+ //Console::debug("第 $i 个马上进入睡眠...");
+ ZMRequest::get("http://localhost:9002/test/ping");
+ Console::verbose(strval($i));
+ });
+ }
+ }
+
+ public function testA() {
+ $this->assertTrue(true);
+ }
+}
diff --git a/test/LightCacheTest.php b/test/LightCacheTest.php
new file mode 100644
index 00000000..95f6b3d4
--- /dev/null
+++ b/test/LightCacheTest.php
@@ -0,0 +1,21 @@
+ 2048,
+ "max_strlen" => 4096,
+ "hash_conflict_proportion" => 0.6,
+ ]);
+ //LightCache::set("bool", true);
+ $this->assertEquals(true, LightCache::set("2048", 123, 3));
+ $this->assertArrayHasKey("2048", LightCache::getAll());
+ sleep(3);
+ $this->assertArrayNotHasKey("2048", LightCache::getAll());
+ }
+}
diff --git a/test/ZMTest/Mock/mock.php b/test/ZMTest/Mock/mock.php
new file mode 100644
index 00000000..a4abe2da
--- /dev/null
+++ b/test/ZMTest/Mock/mock.php
@@ -0,0 +1,2 @@
+parser = new AnnotationParser();
+ $this->parser->addRegisterPath(WORKING_DIR . "/src/Module/", "Module");
+ try {
+ $this->parser->registerMods();
+ } catch (ReflectionException $e) {
+ throw $e;
+ }
+ }
+
+ public function testAnnotation() {
+ ob_start();
+ $gen = $this->parser->generateAnnotationEvents();
+ $m = $gen[OnStart::class][0]->method;
+ $class = $gen[OnStart::class][0]->class;
+ $c = new $class();
+ try {
+ $c->$m();
+ } catch (Exception $e) {
+ }
+ $result = ob_get_clean();
+ echo $result;
+ $this->assertStringContainsString("我开始了!", $result);
+ }
+
+ public function testAnnotation2() {
+
+ foreach ($this->parser->generateAnnotationEvents() as $k => $v) {
+ foreach ($v as $vs) {
+ $this->assertTrue($vs->method === null || $vs->method != '');
+ $this->assertTrue(strlen($vs->class) > 0);
+ }
+ }
+ }
+
+ public function testAnnotationMap() {
+ $map = $this->parser->getMiddlewareMap();
+ $this->assertContainsEquals("timer", $map[Hello::class]["timer"]);
+ }
+
+ public function testMiddlewares() {
+ $wares = $this->parser->getMiddlewares();
+ $this->assertArrayHasKey("timer", $wares);
+ }
+
+ public function testReqMapping() {
+ $mapping = $this->parser->getReqMapping();
+ $this->assertEquals("index", $mapping["method"]);
+ }
+}
diff --git a/test/ZMTest/Testing/EventDispatcherTest.php b/test/ZMTest/Testing/EventDispatcherTest.php
new file mode 100644
index 00000000..26984427
--- /dev/null
+++ b/test/ZMTest/Testing/EventDispatcherTest.php
@@ -0,0 +1,51 @@
+addRegisterPath(WORKING_DIR . "/src/Module/", "Module");
+ try {
+ $parser->registerMods();
+ } catch (ReflectionException $e) {
+ throw $e;
+ }
+ EventManager::loadEventByParser($parser);
+ $dispatcher = new EventDispatcher(CQCommand::class);
+ $dispatcher->setReturnFunction(function ($result) {
+ echo $result . PHP_EOL;
+ });
+ //$dispatcher->setRuleFunction(function ($v) { return $v->match == "qwe"; });
+ $dispatcher->setRuleFunction(function ($v) { return $v->match == "你好"; });
+ //$dispatcher->setRuleFunction(fn ($v) => $v->match == "qwe");
+ ob_start();
+ try {
+ $dispatcher->dispatchEvents();
+ } catch (AnnotationException $e) {
+ }
+ $r = ob_get_clean();
+ echo $r;
+ $this->assertStringContainsString("你好啊", $r);
+ }
+}
diff --git a/test/test.php b/test/test.php
new file mode 100644
index 00000000..64918112
--- /dev/null
+++ b/test/test.php
@@ -0,0 +1,56 @@
+ 'MyController'));
+$routes = new RouteCollection();
+$routes->add('route_name', $route);
+
+
+
+$route = new Route(
+ '/archive/{month}', // path
+ array('controller' => 'showArchive', 'asd' => 'feswf'), // default values
+ array('month' => '[0-9]{4}-[0-9]{2}', 'subdomain' => 'www|m'), // requirements
+ array(), // options
+ '', // host
+ array(), // schemes
+ array() // methods
+);
+
+// ...
+
+$routes->add('date', $route);
+
+$route = new Route('/archive/test');
+
+$routes->add('qwerty', $route);
+
+$route = new Route('/');
+
+$routes->add('root', $route);
+
+$context = new RequestContext();
+
+$matcher = new UrlMatcher($routes, $context);
+
+//$parameters = $matcher->match('/test/foo');var_dump($parameters);
+
+$parameters = $matcher->match('/archive/2012-01');
+var_dump($parameters);
+// array(
+// 'controller' => 'showArchive',
+// 'month' => '2012-01',
+// 'subdomain' => 'www',
+// '_route' => ...
+// )
+
+$parameters = $matcher->match('/');
+var_dump($parameters);
+
+$sub = new RouteCollection();