mirror of
https://github.com/zhamao-robot/zhamao-framework.git
synced 2026-07-02 22:35:38 +08:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e77b9d4970 | |||
|
|
456b102c15 | ||
|
|
cc57997abc | ||
|
|
19e61c7cc3 | ||
|
|
f908513dca | ||
|
|
7dc39e6ada | ||
|
|
b0be53554d | ||
|
|
b98048bd39 | ||
|
|
fffd3fdc95 |
@@ -1,5 +0,0 @@
|
||||
#!/bin/bash
|
||||
if [ ! -d "/app/zhamao-framework/bin" ]; then
|
||||
cp -r /app/zhamao-framework-bak/* /app/zhamao-framework/
|
||||
fi
|
||||
php /app/zhamao-framework/bin/start
|
||||
@@ -66,6 +66,11 @@ public function index() {
|
||||
|
||||
如果旧版框架使用过程中无问题且对新功能暂无需求,可以继续使用 v1 版本,后续也将维护安全类更新和修复致命 bug。
|
||||
|
||||
## 下载源码
|
||||
框架源码可直接克隆本仓库进行编辑,如果你在国内,访问 GitHub 和 clone 仓库比较慢,可以将 `github.com` 替换为 `fgit.zhamao.me` 进行加速。
|
||||
|
||||
例如:`git clone https://fgit.zhamao.me/zhamao-robot/zhamao-framework.git`。
|
||||
|
||||
## 贡献和捐赠
|
||||
如果你在使用过程中发现任何问题,可以提交 Issue 或自行 Fork 后修改并提交 Pull Request。
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
/** @noinspection ALL */<?php
|
||||
/**
|
||||
* Copyright: Swlib
|
||||
* Author: Twosee <twose@qq.com>
|
||||
@@ -52,6 +52,7 @@ if (!defined('PHPUNIT_COMPOSER_INSTALL')) {
|
||||
}
|
||||
}
|
||||
}
|
||||
/** @noinspection PhpIncludeInspection */
|
||||
require PHPUNIT_COMPOSER_INSTALL;
|
||||
$starttime = microtime(true);
|
||||
go(function (){
|
||||
|
||||
@@ -21,9 +21,9 @@ function generate($argv) {
|
||||
$s .= "\nGroup=" . exec("groups | awk '{print $1}'");
|
||||
$s .= "\nWorkingDirectory=" . getcwd();
|
||||
if ($argv[0] == "systemd" && !file_exists(getcwd() . '/systemd'))
|
||||
$s .= "\nExecStart=" . getcwd() . "/vendor/bin/start server --disable-console-input";
|
||||
$s .= "\nExecStart=" . getcwd() . "/vendor/bin/start server";
|
||||
else
|
||||
$s .= "\nExecStart=" . getcwd() . "/bin/start server --disable-console-input";
|
||||
$s .= "\nExecStart=" . getcwd() . "/bin/start server";
|
||||
$s .= "\nRestart=always\n\n[Install]\nWantedBy=multi-user.target\n";
|
||||
@mkdir(getcwd() . "/resources/");
|
||||
file_put_contents(getcwd() . "/resources/zhamao.service", $s);
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
"description": "High performance chat robot and web server development framework",
|
||||
"minimum-stability": "stable",
|
||||
"license": "Apache-2.0",
|
||||
"version": "2.2.10",
|
||||
"extra": {
|
||||
"exclude_annotate": [
|
||||
"src/ZM"
|
||||
|
||||
@@ -109,15 +109,25 @@ $config['static_file_server'] = [
|
||||
|
||||
/** 注册 Swoole Server 事件注解的类列表 */
|
||||
$config['server_event_handler_class'] = [
|
||||
\ZM\Event\ServerEventHandler::class,
|
||||
// 这里添加例如 \ZM\Event\ServerEventHandler::class 这样的启动注解类
|
||||
];
|
||||
|
||||
/** 服务器启用的外部第三方和内部插件 */
|
||||
$config['modules'] = [
|
||||
'onebot' => [
|
||||
'onebot' => [ // 机器人解析模块,关闭后无法使用如@CQCommand等注解
|
||||
'status' => true,
|
||||
'single_bot_mode' => false
|
||||
], // QQ机器人事件解析器,如果取消此项则默认为 true 开启状态,否则你手动填写 false 才会关闭
|
||||
],
|
||||
'http_proxy_server' => [ // 一个内置的简单HTTP代理服务器,目前还没有认证功能,预计2.4.0版本完成
|
||||
'status' => false,
|
||||
'host' => '0.0.0.0',
|
||||
'port' => 8083,
|
||||
'swoole_set_override' => [
|
||||
'backlog' => 128,
|
||||
'buffer_output_size' => 1024 * 1024 * 128,
|
||||
'socket_buffer_size' => 1024 * 1024 * 1
|
||||
]
|
||||
],
|
||||
];
|
||||
|
||||
return $config;
|
||||
|
||||
@@ -85,7 +85,6 @@ bin/start server # 通过源码模式启动框架
|
||||
- `--debug-mode`:启用调试模式,调试模式的作用是关闭一键协程化和终端交互,减少 Swoole 本身对代码逻辑的干扰(比如执行 `shell_exec()` 报错的话可以开启这个进行调试)。
|
||||
- `--log-{mode}`:设置 log 等级。支持 `--log-debug`,`--log-verbose`,`--log-info`,`--log-warning`,`--log-error`。
|
||||
- `--log-theme`:设置终端信息的主题。这个选项适用于多种终端信息显示的兼容,例如白色终端和不支持颜色的终端。详见 [Console - 主题设置](/component/console/#_2)。
|
||||
- `--disable-console-input`:关闭终端交互,如果你使用的不是 tmux、screen 而是直接将进程使用 systemd 等方式运行到 init 守护进程下,则需要关闭终端交互输入,关闭后不可以使用 `stop, reload, logtest` 等交互命令。
|
||||
- `--disable-coroutine`:关闭一键协程化。
|
||||
- `--daemon`:以守护进程方式运行框架,此参数将直接在输出 motd 后将进程挂到 init 下运行,后台常驻。
|
||||
- `--watch`:监控 `src/` 目录下的文件变化,有变化则自动重新载入代码。开启监控需要安装 PHP 扩展:inotify。使用 pecl 就可以安装:`pecl install inotify`。
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -128,8 +128,9 @@ $str = CQ::removeCQ("[CQ:at,qq=all]这是带表情的全体消息[CQ:face,id=8]"
|
||||
|
||||
解析 CQ 码。
|
||||
|
||||
- 参数:`getCQ($msg);`:要解析出 CQ 码的消息。
|
||||
- 返回:`数组 | null`,见下表
|
||||
- 定义:`getCQ($msg, $is_object = false)`
|
||||
- 参数 `$is_object` 为 true 时,返回一个 `\ZM\Entity\CQObject` 对象,此对象的属性和下表相同。(2.3.0+ 版本可用)
|
||||
- 返回:`数组 | CQObject | null`,见下表。
|
||||
|
||||
| 键名 | 说明 |
|
||||
| ------ | ------------------------------------------------------------ |
|
||||
@@ -140,6 +141,10 @@ $str = CQ::removeCQ("[CQ:at,qq=all]这是带表情的全体消息[CQ:face,id=8]"
|
||||
|
||||
### CQ::getAllCQ()
|
||||
|
||||
定义:`CQ::getAllCQ($msg, $is_object = false)`
|
||||
|
||||
参数 `$is_object` 为 true 时,返回一个 `\ZM\Entity\CQObject[]` 对象数组,此对象的属性和上面的表格内相同。(2.3.0+ 版本可用)
|
||||
|
||||
解析 CQ 码,和 `getCQ()` 的区别是,这个会将字符串中的所有 CQ 码都解析出来,并以同样上方解析出来的数组格式返回。
|
||||
|
||||
```php
|
||||
@@ -174,7 +179,7 @@ CQ::getAllCQ("[CQ:at,qq=123]你好啊[CQ:at,qq=456]");
|
||||
|
||||
定义:`CQ::face($id)`
|
||||
|
||||
参数:`$id` 为 QQ 表情对应的 ID 号,一些常见的表情 ID 对应的表情样式见 [QQ 对应表情ID表](/assets/face_id.html)。
|
||||
参数:`$id` 为 QQ 表情对应的 ID 号,一些常见的表情 ID 对应的表情样式见 [QQ 对应表情ID表](https://static.zhamao.me/face_id.html)。
|
||||
|
||||
```php
|
||||
/**
|
||||
|
||||
@@ -221,6 +221,27 @@
|
||||
| tick_ms | `int`,**必填**,间隔的毫秒数,例如 1 秒间隔为 `1000`,范围大于 0,小于 86400000。 | | |
|
||||
| worker_id | `int`,要在哪个 Worker 进程上执行,默认为 0,范围是 0~{你设定的 Worker 数量-1},如果是 -1 的话,则会在所有 Worker 进程上触发。 | 限定只执行的 Worker 进程 | |
|
||||
|
||||
## OnTask()
|
||||
|
||||
定义一个在工作进程中运行的任务函数。详情见 [进阶 - 使用 TaskWorker 进程处理密集运算](/advanced/task-worker)。
|
||||
|
||||
### 属性
|
||||
|
||||
| 类型 | 值 |
|
||||
| ---------- | ----------------------------- |
|
||||
| 名称 | `@OnTask` |
|
||||
| 触发前提 | 在框架加载后激活 |
|
||||
| 命名空间 | `ZM\Annotation\Swoole\OnTask` |
|
||||
| 适用位置 | 方法 |
|
||||
| 返回值处理 | 有,返回 Worker 进程的结果 |
|
||||
|
||||
### 注解参数
|
||||
|
||||
| 参数名称 | 参数范围 | 用途 | 默认 |
|
||||
| --------- | ------------------------------------------------------------ | ------------ | ---- |
|
||||
| task_name | `string`,**必填**,任务函数的名称,不建议重复。 | | |
|
||||
| rule | 设置触发前提,PHP 代码,返回 bool 值即可,参考 OnRequestEvent | 限定是否执行 | 空 |
|
||||
|
||||
## OnSetup()
|
||||
|
||||
在框架加载前执行的代码。此部分代码是在主进程执行的,不可在此事件中使用任何协程相关的功能。
|
||||
|
||||
@@ -36,12 +36,14 @@
|
||||
|
||||
### 子表 **swoole**
|
||||
|
||||
| 配置名称 | 说明 | 默认值 |
|
||||
| --------------- | ------------------------------------------------------------ | ----------------------------------- |
|
||||
| `log_file` | Swoole 的日志文件 | `crash_dir` 下的 `swoole_error.log` |
|
||||
| `worker_num` | Worker 工作进程数 | 运行框架的主机 CPU 核心数 |
|
||||
| `dispatch_mode` | 数据包分发策略,见 [文档](https://wiki.swoole.com/#/server/setting?id=dispatch_mode) | 2 |
|
||||
| `max_coroutine` | 最大协程并发数 | 300000 |
|
||||
| 配置名称 | 说明 | 默认值 |
|
||||
| ----------------------- | ------------------------------------------------------------ | ----------------------------------- |
|
||||
| `log_file` | Swoole 的日志文件 | `crash_dir` 下的 `swoole_error.log` |
|
||||
| `worker_num` | Worker 工作进程数 | 运行框架的主机 CPU 核心数 |
|
||||
| `dispatch_mode` | 数据包分发策略,见 [文档](https://wiki.swoole.com/#/server/setting?id=dispatch_mode) | 2 |
|
||||
| `max_coroutine` | 最大协程并发数 | 300000 |
|
||||
| `task_worker_num` | TaskWorker 工作进程数 | 默认不开启(此参数被注释) |
|
||||
| `task_enable_coroutine` | TaskWorker 工作进程启用协程 | 默认不开启(此参数被注释)或 `bool` |
|
||||
|
||||
### 子表 **light_cache**
|
||||
|
||||
|
||||
@@ -1,5 +1,29 @@
|
||||
# 更新日志(v2 版本)
|
||||
|
||||
## 2.3.0
|
||||
|
||||
> 更新时间:2021.3.16
|
||||
|
||||
- 新增:MessageUtil 消息处理工具类
|
||||
- 新增:TaskManager,封装了 TaskWorker 进程的应用
|
||||
- 新增:CQObject,使用 `CQ::getCQ()` 可获取对象形式的 CQ 码解析结果
|
||||
- 新增:`@OnTask` 注解,绑定任务函数
|
||||
- 新增:RouteManager 路由管理类,可快速添加路由
|
||||
- 修复:`ZM_DATA` 和 `DataProvider::getDataFolder()` 返回 false 的问题
|
||||
- 优化:关闭显示停止框架后多余的输出信息
|
||||
|
||||
注:本次升级建议升级后合并全局配置文件,有一些新加的内容。
|
||||
|
||||
## 2.2.11
|
||||
|
||||
> 更新时间:2021.3.13
|
||||
|
||||
- 新增:内部 ID 版本号(ZM_VERSION_ID)
|
||||
- 优化:启动时 log 的等级
|
||||
- 移除:终端输入命令
|
||||
- 修复:纯 HTTP 服务器的启动 bug
|
||||
- 新增:`zm_timer` 的报错处理,防止服务器直接崩掉
|
||||
|
||||
## v2.2.10
|
||||
|
||||
> 更新时间:2021.3.8
|
||||
|
||||
14
mkdocs.yml
14
mkdocs.yml
@@ -72,9 +72,12 @@ nav:
|
||||
- 事件分发器: event/event-dispatcher.md
|
||||
- 框架组件:
|
||||
- 框架组件: component/index.md
|
||||
- 机器人 API: component/robot-api.md
|
||||
- CQ 码(多媒体消息): component/cqcode.md
|
||||
- 上下文: component/context.md
|
||||
- 聊天机器人组件:
|
||||
- 机器人 API: component/robot-api.md
|
||||
- CQ 码(多媒体消息): component/cqcode.md
|
||||
- 机器人消息处理: component/message-util.md
|
||||
- Token 验证: component/access-token.md
|
||||
- 存储:
|
||||
- LightCache 轻量缓存: component/light-cache.md
|
||||
- MySQL 数据库: component/mysql.md
|
||||
@@ -82,13 +85,15 @@ nav:
|
||||
- ZMAtomic 原子计数器: component/atomics.md
|
||||
- SpinLock 自旋锁: component/spin-lock.md
|
||||
- 文件管理: component/data-provider.md
|
||||
- HTTP 服务器工具类:
|
||||
- HTTP 和 WebSocket 客户端: component/zmrequest.md
|
||||
- HTTP 路由管理: component/route-manager.md
|
||||
- 协程池: component/coroutine-pool.md
|
||||
- 单例类: component/singleton-trait.md
|
||||
- ZMUtil 杂项: component/zmutil.md
|
||||
- 全局方法: component/global-functions.md
|
||||
- HTTP 和 WebSocket 客户端: component/zmrequest.md
|
||||
- Console 终端: component/console.md
|
||||
- Token 验证: component/access-token.md
|
||||
- TaskWorker 管理: component/task-worker.md
|
||||
- 进阶开发:
|
||||
- 进阶开发: advanced/index.md
|
||||
- 框架剖析: advanced/framework-structure.md
|
||||
@@ -97,6 +102,7 @@ nav:
|
||||
- 内部类文件手册: advanced/inside-class.md
|
||||
- 接入 WebSocket 客户端: advanced/connect-ws-client.md
|
||||
- 框架多进程: advanced/multi-process.md
|
||||
- TaskWorker 提高并发: advanced/task-worker.md
|
||||
- 开发实战教程:
|
||||
- 编写管理员才能触发的功能: advanced/example/admin.md
|
||||
- FAQ: FAQ.md
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
<?php /** @noinspection PhpFullyQualifiedNameUsageInspection */ #plain
|
||||
|
||||
//这里写你的全局函数
|
||||
/**
|
||||
* @param callable $func
|
||||
* @param string $name
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
function pgo(callable $func, $name = "default") {
|
||||
\ZM\Utils\CoroutinePool::go($func, $name);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?php
|
||||
<?php /** @noinspection PhpMissingReturnTypeInspection */
|
||||
|
||||
namespace Module\Example;
|
||||
|
||||
@@ -11,6 +11,7 @@ use ZM\Console\Console;
|
||||
use ZM\Annotation\CQ\CQCommand;
|
||||
use ZM\Annotation\Http\RequestMapping;
|
||||
use ZM\Event\EventDispatcher;
|
||||
use ZM\Exception\InterruptException;
|
||||
use ZM\Requests\ZMRequest;
|
||||
use ZM\Utils\ZMUtil;
|
||||
|
||||
@@ -21,6 +22,14 @@ use ZM\Utils\ZMUtil;
|
||||
*/
|
||||
class Hello
|
||||
{
|
||||
/*
|
||||
* 默认的图片监听路由对应目录,如需要使用可取消下面的注释,把上面的 /* 换成 /**
|
||||
* @OnStart(-1)
|
||||
*/
|
||||
//public function onStart() {
|
||||
// \ZM\Http\RouteManager::addStaticFileRoute("/images/", \ZM\Utils\DataProvider::getWorkingDir()."/zm_data/images/");
|
||||
//}
|
||||
|
||||
/**
|
||||
* 使用命令 .reload 发给机器人远程重载,注意将 user_id 换成你自己的 QQ
|
||||
* @CQCommand(".reload",user_id=627577391)
|
||||
@@ -126,6 +135,7 @@ class Hello
|
||||
/**
|
||||
* 阻止 Chrome 自动请求 /favicon.ico 导致的多条请求并发和干扰
|
||||
* @OnRequestEvent(rule="ctx()->getRequest()->server['request_uri'] == '/favicon.ico'",level=200)
|
||||
* @throws InterruptException
|
||||
*/
|
||||
public function onRequest() {
|
||||
EventDispatcher::interrupt();
|
||||
|
||||
@@ -24,7 +24,7 @@ class TimerMiddleware implements MiddlewareInterface
|
||||
* @HandleBefore()
|
||||
* @return bool
|
||||
*/
|
||||
public function onBefore() {
|
||||
public function onBefore(): bool {
|
||||
$this->starttime = microtime(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
<?php
|
||||
<?php /** @noinspection PhpUnused */
|
||||
|
||||
/** @noinspection PhpMissingReturnTypeInspection */
|
||||
|
||||
|
||||
namespace ZM\API;
|
||||
|
||||
|
||||
use ZM\Console\Console;
|
||||
use ZM\Entity\CQObject;
|
||||
|
||||
class CQ
|
||||
{
|
||||
@@ -305,9 +308,10 @@ class CQ
|
||||
/**
|
||||
* 获取消息中第一个CQ码
|
||||
* @param $msg
|
||||
* @return array|null
|
||||
* @param bool $is_object
|
||||
* @return array|CQObject|null
|
||||
*/
|
||||
public static function getCQ($msg) {
|
||||
public static function getCQ($msg, $is_object = false) {
|
||||
if (($head = mb_strpos($msg, "[CQ:")) !== false) {
|
||||
$key_offset = mb_substr($msg, $head);
|
||||
$close = mb_strpos($key_offset, "]");
|
||||
@@ -322,7 +326,7 @@ class CQ
|
||||
}
|
||||
$cq["start"] = $head;
|
||||
$cq["end"] = $close + $head;
|
||||
return $cq;
|
||||
return !$is_object ? $cq : CQObject::fromArray($cq);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
@@ -331,9 +335,10 @@ class CQ
|
||||
/**
|
||||
* 获取消息中所有的CQ码
|
||||
* @param $msg
|
||||
* @return array
|
||||
* @param bool $is_object
|
||||
* @return array|CQObject[]
|
||||
*/
|
||||
public static function getAllCQ($msg) {
|
||||
public static function getAllCQ($msg, $is_object = false) {
|
||||
$cqs = [];
|
||||
$offset = 0;
|
||||
while (($head = mb_strpos(($submsg = mb_substr($msg, $offset)), "[CQ:")) !== false) {
|
||||
@@ -352,7 +357,7 @@ class CQ
|
||||
$cq["start"] = $offset + $head;
|
||||
$cq["end"] = $offset + $tmpmsg + $head;
|
||||
$offset += $tmpmsg + 1;
|
||||
$cqs[] = $cq;
|
||||
$cqs[] = (!$is_object ? $cq : CQObject::fromArray($cq));
|
||||
}
|
||||
return $cqs;
|
||||
}
|
||||
|
||||
@@ -75,10 +75,11 @@ trait CQAPI
|
||||
* @return bool
|
||||
* @noinspection PhpUnusedParameterInspection
|
||||
*/
|
||||
public function processHttpAPI($connection, $reply, $function = null) {
|
||||
public function processHttpAPI($connection, $reply, $function = null): bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @noinspection PhpMissingReturnTypeInspection */
|
||||
public function __call($name, $arguments) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<?php /** @noinspection PhpUnused */
|
||||
<?php /** @noinspection PhpMissingReturnTypeInspection */
|
||||
|
||||
/** @noinspection PhpUnused */
|
||||
|
||||
|
||||
namespace ZM\API;
|
||||
|
||||
@@ -12,7 +12,7 @@ abstract class AnnotationBase
|
||||
|
||||
public $class;
|
||||
|
||||
public function __toString() {
|
||||
public function __toString(): string {
|
||||
$str = __CLASS__ . ": ";
|
||||
foreach ($this as $k => $v) {
|
||||
$str .= "\n\t" . $k . " => ";
|
||||
|
||||
@@ -125,10 +125,7 @@ class AnnotationParser
|
||||
Console::debug("解析注解完毕!");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function generateAnnotationEvents() {
|
||||
public function generateAnnotationEvents(): array {
|
||||
$o = [];
|
||||
foreach ($this->annotation_map as $module => $obj) {
|
||||
foreach (($obj["class_annotations"] ?? []) as $class_annotation) {
|
||||
@@ -151,17 +148,17 @@ class AnnotationParser
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getMiddlewares() { return $this->middlewares; }
|
||||
public function getMiddlewares(): array { return $this->middlewares; }
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getMiddlewareMap() { return $this->middleware_map; }
|
||||
public function getMiddlewareMap(): array { return $this->middleware_map; }
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getReqMapping() { return $this->req_mapping; }
|
||||
public function getReqMapping(): array { return $this->req_mapping; }
|
||||
|
||||
/**
|
||||
* @param $path
|
||||
@@ -171,7 +168,7 @@ class AnnotationParser
|
||||
|
||||
//private function below
|
||||
|
||||
private function registerMiddleware(MiddlewareClass $vs, ReflectionClass $reflection_class) {
|
||||
private function registerMiddleware(MiddlewareClass $vs, ReflectionClass $reflection_class): array {
|
||||
$result = [
|
||||
"class" => "\\" . $reflection_class->getName(),
|
||||
"name" => $vs->name
|
||||
|
||||
@@ -26,7 +26,7 @@ class CQAfter extends AnnotationBase implements Level
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getLevel() {
|
||||
public function getLevel(): int {
|
||||
return $this->level;
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ class CQBefore extends AnnotationBase implements Level
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getLevel() {
|
||||
public function getLevel(): int {
|
||||
return $this->level;
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ class CQMessage extends AnnotationBase implements Level
|
||||
/** @var int */
|
||||
public $level = 20;
|
||||
|
||||
public function getLevel() { return $this->level; }
|
||||
public function getLevel(): int { return $this->level; }
|
||||
|
||||
public function setLevel(int $level) {
|
||||
$this->level = $level;
|
||||
|
||||
@@ -27,7 +27,7 @@ class CQMetaEvent extends AnnotationBase implements Level
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getLevel() { return $this->level; }
|
||||
public function getLevel(): int { return $this->level; }
|
||||
|
||||
/**
|
||||
* @param int $level
|
||||
|
||||
36
src/ZM/Annotation/Swoole/OnTask.php
Normal file
36
src/ZM/Annotation/Swoole/OnTask.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Annotation\Swoole;
|
||||
|
||||
use Doctrine\Common\Annotations\Annotation\Required;
|
||||
use Doctrine\Common\Annotations\Annotation\Target;
|
||||
use ZM\Annotation\AnnotationBase;
|
||||
use ZM\Annotation\Interfaces\Rule;
|
||||
|
||||
/**
|
||||
* Class OnTask
|
||||
* @package ZM\Annotation\Swoole
|
||||
* @Annotation
|
||||
* @Target("METHOD")
|
||||
*/
|
||||
class OnTask extends AnnotationBase implements Rule
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
* @Required()
|
||||
*/
|
||||
public $task_name;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $rule = "";
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getRule(): string {
|
||||
return $this->rule;
|
||||
}
|
||||
}
|
||||
16
src/ZM/Annotation/Swoole/OnTaskEvent.php
Normal file
16
src/ZM/Annotation/Swoole/OnTaskEvent.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Annotation\Swoole;
|
||||
|
||||
use Doctrine\Common\Annotations\Annotation\Target;
|
||||
|
||||
/**
|
||||
* Class OnTaskEvent
|
||||
* @package ZM\Annotation\Swoole
|
||||
* @Annotation
|
||||
* @Target("METHOD")
|
||||
*/
|
||||
class OnTaskEvent extends OnSwooleEventBase
|
||||
{
|
||||
}
|
||||
@@ -26,7 +26,7 @@ class BuildCommand extends Command
|
||||
// ...
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output) {
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int {
|
||||
$this->output = $output;
|
||||
$target_dir = $input->getOption("target") ?? (__DIR__ . '/../../../resources/');
|
||||
if (mb_strpos($target_dir, "../")) $target_dir = realpath($target_dir);
|
||||
|
||||
@@ -13,7 +13,7 @@ abstract class DaemonCommand extends Command
|
||||
{
|
||||
protected $daemon_file = null;
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output) {
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int {
|
||||
$pid_path = DataProvider::getWorkingDir() . "/.daemon_pid";
|
||||
if (!file_exists($pid_path)) {
|
||||
$output->writeln("<comment>没有检测到正在运行的守护进程!</comment>");
|
||||
|
||||
@@ -15,7 +15,7 @@ class DaemonReloadCommand extends DaemonCommand
|
||||
$this->setDescription("重载守护进程下的用户代码(仅限--daemon模式可用)");
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output) {
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int {
|
||||
parent::execute($input, $output);
|
||||
system("kill -USR1 " . intval($this->daemon_file["pid"]));
|
||||
$output->writeln("<info>成功重载!</info>");
|
||||
|
||||
@@ -15,7 +15,7 @@ class DaemonStatusCommand extends DaemonCommand
|
||||
$this->setDescription("查看守护进程框架的运行状态(仅限--daemon模式可用)");
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output) {
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int {
|
||||
parent::execute($input, $output);
|
||||
$output->writeln("<info>框架运行中,pid:" . $this->daemon_file["pid"] . "</info>");
|
||||
$output->writeln("<comment>----- 以下是stdout内容 -----</comment>");
|
||||
|
||||
@@ -16,7 +16,7 @@ class DaemonStopCommand extends DaemonCommand
|
||||
$this->setDescription("停止守护进程下运行的框架(仅限--daemon模式可用)");
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output) {
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int {
|
||||
parent::execute($input, $output);
|
||||
system("kill -TERM " . intval($this->daemon_file["pid"]));
|
||||
unlink(DataProvider::getWorkingDir() . "/.daemon_pid");
|
||||
|
||||
@@ -30,7 +30,7 @@ class InitCommand extends Command
|
||||
// ...
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output) {
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int {
|
||||
if (LOAD_MODE === 1) { // 从composer依赖而来的项目模式,最基本的需要初始化的模式
|
||||
$output->writeln("<comment>Initializing files</comment>");
|
||||
$base_path = LOAD_MODE_COMPOSER_PATH;
|
||||
@@ -96,7 +96,7 @@ class InitCommand extends Command
|
||||
return Command::FAILURE;
|
||||
}
|
||||
|
||||
private function getExtractFiles() {
|
||||
private function getExtractFiles(): array {
|
||||
return $this->extract_files;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ namespace ZM\Command;
|
||||
|
||||
|
||||
use Swoole\Atomic;
|
||||
use Swoole\Coroutine;
|
||||
use Swoole\Http\Request;
|
||||
use Swoole\Http\Response;
|
||||
use Swoole\Http\Server;
|
||||
@@ -17,7 +16,9 @@ use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use ZM\Config\ZMConfig;
|
||||
use ZM\Console\Console;
|
||||
use ZM\Framework;
|
||||
use ZM\Store\ZMAtomic;
|
||||
use ZM\Utils\DataProvider;
|
||||
use ZM\Utils\HttpUtil;
|
||||
|
||||
class PureHttpCommand extends Command
|
||||
@@ -34,23 +35,32 @@ class PureHttpCommand extends Command
|
||||
// ...
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output) {
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int {
|
||||
$tty_width = explode(" ", trim(exec("stty size")))[1];
|
||||
if (realpath($input->getArgument('dir') ?? '.') === false) {
|
||||
$output->writeln("<error>Directory error(" . ($input->getArgument('dir') ?? '.') . "): no such file or directory.</error>");
|
||||
return self::FAILURE;
|
||||
}
|
||||
ZMConfig::setDirectory(DataProvider::getWorkingDir() . '/config');
|
||||
$global = ZMConfig::get("global");
|
||||
$host = $input->getOption("host") ?? $global["host"];
|
||||
$port = $input->getOption("port") ?? $global["port"];
|
||||
|
||||
$index = ["index.html", "index.htm"];
|
||||
$out = [
|
||||
"listen" => $host.":".$port,
|
||||
"version" => ZM_VERSION,
|
||||
"web_root" => realpath($input->getArgument('dir') ?? '.'),
|
||||
"index" => implode(",", $index)
|
||||
];
|
||||
Framework::printProps($out, $tty_width);
|
||||
$server = new Server($host, $port);
|
||||
$server->set(ZMConfig::get("global", "swoole"));
|
||||
Console::init(0, $server);
|
||||
Console::init(2, $server);
|
||||
ZMAtomic::$atomics["request"] = [];
|
||||
for ($i = 0; $i < 32; ++$i) {
|
||||
ZMAtomic::$atomics["request"][$i] = new Atomic(0);
|
||||
}
|
||||
$index = ["index.html", "index.htm"];
|
||||
$server->on("request", function (Request $request, Response $response) use ($input, $index, $server) {
|
||||
ZMAtomic::$atomics["request"][$server->worker_id]->add(1);
|
||||
HttpUtil::handleStaticPage(
|
||||
@@ -60,10 +70,11 @@ class PureHttpCommand extends Command
|
||||
"document_root" => realpath($input->getArgument('dir') ?? '.'),
|
||||
"document_index" => $index
|
||||
]);
|
||||
echo "\r" . Coroutine::stats()["coroutine_peak_num"];
|
||||
//echo "\r" . Coroutine::stats()["coroutine_peak_num"];
|
||||
});
|
||||
$server->on("start", function ($server) {
|
||||
Process::signal(SIGINT, function () use ($server) {
|
||||
echo "\r";
|
||||
Console::warning("Server interrupted by keyboard.");
|
||||
for ($i = 0; $i < 32; ++$i) {
|
||||
$num = ZMAtomic::$atomics["request"][$i]->get();
|
||||
@@ -75,13 +86,6 @@ class PureHttpCommand extends Command
|
||||
});
|
||||
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))
|
||||
|
||||
@@ -22,18 +22,18 @@ class RunServerCommand extends Command
|
||||
new InputOption("log-warning", null, null, "调整消息等级到warning (log-level=1)"),
|
||||
new InputOption("log-error", null, null, "调整消息等级到error (log-level=0)"),
|
||||
new InputOption("log-theme", null, InputOption::VALUE_REQUIRED, "改变终端的主题配色"),
|
||||
new InputOption("disable-console-input", null, null, "禁止终端输入内容 (后台服务时需要)"),
|
||||
new InputOption("disable-console-input", null, null, "禁止终端输入内容 (废弃)"),
|
||||
new InputOption("disable-coroutine", null, null, "关闭协程Hook"),
|
||||
new InputOption("daemon", null, null, "以守护进程的方式运行框架"),
|
||||
new InputOption("watch", null, null, "监听 src/ 目录的文件变化并热更新"),
|
||||
new InputOption("show-php-ver", null, null, "启动时显示PHP版本"),
|
||||
new InputOption("show-php-ver", null, null, "启动时显示PHP和Swoole版本"),
|
||||
new InputOption("env", null, InputOption::VALUE_REQUIRED, "设置环境类型 (production, development, staging)"),
|
||||
]);
|
||||
$this->setDescription("Run zhamao-framework | 启动框架");
|
||||
$this->setHelp("直接运行可以启动");
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output) {
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int {
|
||||
if (($opt = $input->getOption("env")) !== null) {
|
||||
if (!in_array($opt, ["production", "staging", "development", ""])) {
|
||||
$output->writeln("<error> \"--env\" option only accept production, development, staging and [empty] ! </error>");
|
||||
|
||||
@@ -13,7 +13,7 @@ class SystemdCommand extends Command
|
||||
// the name of the command (the part after "bin/console")
|
||||
protected static $defaultName = 'systemd:generate';
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output) {
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int {
|
||||
//TODO: 写一个生成systemd配置的功能,给2.0
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
@@ -18,12 +18,16 @@ use ZM\Utils\DataProvider;
|
||||
|
||||
class ConsoleApplication extends Application
|
||||
{
|
||||
const VERSION_ID = 388;
|
||||
const VERSION = "2.3.1";
|
||||
|
||||
public function __construct(string $name = 'UNKNOWN') {
|
||||
$version = json_decode(file_get_contents(__DIR__ . "/../../composer.json"), true)["version"] ?? "UNKNOWN";
|
||||
parent::__construct($name, $version);
|
||||
define("ZM_VERSION_ID", self::VERSION_ID);
|
||||
define("ZM_VERSION", self::VERSION);
|
||||
parent::__construct($name, ZM_VERSION);
|
||||
}
|
||||
|
||||
public function initEnv() {
|
||||
public function initEnv(): ConsoleApplication {
|
||||
$this->selfCheck();
|
||||
|
||||
if (!is_dir(__DIR__ . '/../../vendor')) {
|
||||
@@ -50,7 +54,7 @@ class ConsoleApplication extends Application
|
||||
$composer["autoload"]["psr-4"]["Custom\\"] = "src/Custom";
|
||||
$r = file_put_contents(DataProvider::getWorkingDir() . "/composer.json", json_encode($composer, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
|
||||
if ($r !== false) {
|
||||
echo "成功添加!请重新进行 composer update !\n";
|
||||
echo "成功添加!请运行 composer dump-autoload !\n";
|
||||
exit(1);
|
||||
} else {
|
||||
echo "添加失败!请按任意键继续!";
|
||||
@@ -86,7 +90,7 @@ class ConsoleApplication extends Application
|
||||
* @param OutputInterface|null $output
|
||||
* @return int
|
||||
*/
|
||||
public function run(InputInterface $input = null, OutputInterface $output = null) {
|
||||
public function run(InputInterface $input = null, OutputInterface $output = null): int {
|
||||
try {
|
||||
return parent::run($input, $output);
|
||||
} catch (Exception $e) {
|
||||
@@ -94,7 +98,7 @@ class ConsoleApplication extends Application
|
||||
}
|
||||
}
|
||||
|
||||
private function selfCheck() {
|
||||
private function selfCheck(): bool {
|
||||
if (!extension_loaded("swoole")) die("Can not find swoole extension.\nSee: https://github.com/zhamao-robot/zhamao-framework/issues/19\n");
|
||||
if (version_compare(SWOOLE_VERSION, "4.4.13") == -1) die("You must install swoole version >= 4.4.13 !");
|
||||
if (version_compare(PHP_VERSION, "7.2") == -1) die("PHP >= 7.2 required.");
|
||||
|
||||
@@ -8,11 +8,12 @@ use Co;
|
||||
use Exception;
|
||||
use Swoole\Http\Request;
|
||||
use Swoole\WebSocket\Frame;
|
||||
use swoole_server;
|
||||
use Swoole\WebSocket\Server;
|
||||
use ZM\ConnectionManager\ConnectionObject;
|
||||
use ZM\ConnectionManager\ManagerGM;
|
||||
use ZM\Console\Console;
|
||||
use ZM\Event\EventDispatcher;
|
||||
use ZM\Exception\InterruptException;
|
||||
use ZM\Exception\InvalidArgumentException;
|
||||
use ZM\Exception\WaitTimeoutException;
|
||||
use ZM\Http\Response;
|
||||
@@ -27,19 +28,19 @@ class Context implements ContextInterface
|
||||
public function __construct($cid) { $this->cid = $cid; }
|
||||
|
||||
/**
|
||||
* @return swoole_server|null
|
||||
* @return Server
|
||||
*/
|
||||
public function getServer() { return self::$context[$this->cid]["server"] ?? server(); }
|
||||
public function getServer(): ?Server { return self::$context[$this->cid]["server"] ?? server(); }
|
||||
|
||||
/**
|
||||
* @return Frame|null
|
||||
*/
|
||||
public function getFrame() { return self::$context[$this->cid]["frame"] ?? null; }
|
||||
public function getFrame(): ?Frame { return self::$context[$this->cid]["frame"] ?? null; }
|
||||
|
||||
public function getFd() { return self::$context[$this->cid]["fd"] ?? $this->getFrame()->fd ?? null; }
|
||||
public function getFd(): ?int { return self::$context[$this->cid]["fd"] ?? $this->getFrame()->fd ?? null; }
|
||||
|
||||
/**
|
||||
* @return array|null
|
||||
* @return mixed
|
||||
*/
|
||||
public function getData() { return self::$context[$this->cid]["data"] ?? null; }
|
||||
|
||||
@@ -48,25 +49,25 @@ class Context implements ContextInterface
|
||||
/**
|
||||
* @return Request|null
|
||||
*/
|
||||
public function getRequest() { return self::$context[$this->cid]["request"] ?? null; }
|
||||
public function getRequest(): ?Request { return self::$context[$this->cid]["request"] ?? null; }
|
||||
|
||||
/**
|
||||
* @return Response|null
|
||||
*/
|
||||
public function getResponse() { return self::$context[$this->cid]["response"] ?? null; }
|
||||
public function getResponse(): ?Response { return self::$context[$this->cid]["response"] ?? null; }
|
||||
|
||||
/** @return ConnectionObject|null */
|
||||
/** @return ConnectionObject|null|Response */
|
||||
public function getConnection() { return ManagerGM::get($this->getFd()); }
|
||||
|
||||
/**
|
||||
* @return int|null
|
||||
*/
|
||||
public function getCid() { return $this->cid; }
|
||||
public function getCid(): ?int { return $this->cid; }
|
||||
|
||||
/**
|
||||
* @return ZMRobot|null
|
||||
*/
|
||||
public function getRobot() {
|
||||
public function getRobot(): ?ZMRobot {
|
||||
$conn = ManagerGM::get($this->getFrame()->fd);
|
||||
return $conn instanceof ConnectionObject ? new ZMRobot($conn) : null;
|
||||
}
|
||||
@@ -87,7 +88,7 @@ class Context implements ContextInterface
|
||||
|
||||
public function setDiscussId($id) { self::$context[$this->cid]["data"]["discuss_id"] = $id; }
|
||||
|
||||
public function getMessageType() { return $this->getData()["message_type"] ?? null; }
|
||||
public function getMessageType(): ?string { return $this->getData()["message_type"] ?? null; }
|
||||
|
||||
public function setMessageType($type) { self::$context[$this->cid]["data"]["message_type"] = $type; }
|
||||
|
||||
@@ -104,6 +105,7 @@ class Context implements ContextInterface
|
||||
* @param $msg
|
||||
* @param bool $yield
|
||||
* @return mixed
|
||||
* @noinspection PhpMissingReturnTypeInspection
|
||||
*/
|
||||
public function reply($msg, $yield = false) {
|
||||
$data = $this->getData();
|
||||
@@ -131,6 +133,12 @@ class Context implements ContextInterface
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $msg
|
||||
* @param false $yield
|
||||
* @return mixed|void
|
||||
* @throws InterruptException
|
||||
*/
|
||||
public function finalReply($msg, $yield = false) {
|
||||
self::$context[$this->cid]["cache"]["block_continue"] = true;
|
||||
if ($msg != "") $this->reply($msg, $yield);
|
||||
@@ -144,6 +152,7 @@ class Context implements ContextInterface
|
||||
* @return string
|
||||
* @throws InvalidArgumentException
|
||||
* @throws WaitTimeoutException
|
||||
* @noinspection PhpMissingReturnTypeInspection
|
||||
*/
|
||||
public function waitMessage($prompt = "", $timeout = 600, $timeout_prompt = "") {
|
||||
if (!isset($this->getData()["user_id"], $this->getData()["message"], $this->getData()["self_id"]))
|
||||
@@ -258,6 +267,7 @@ class Context implements ContextInterface
|
||||
*/
|
||||
public function getNumArg($prompt_msg = "") { return $this->getArgs(ZM_MATCH_NUMBER, $prompt_msg); }
|
||||
|
||||
/** @noinspection PhpMissingReturnTypeInspection */
|
||||
public function cloneFromParent() {
|
||||
set_coroutine_params(self::$context[Co::getPcid()] ?? self::$context[$this->cid]);
|
||||
return context();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?php
|
||||
<?php /** @noinspection PhpMissingReturnTypeInspection */
|
||||
|
||||
|
||||
namespace ZM\Context;
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<?php /** @noinspection PhpComposerExtensionStubsInspection */
|
||||
<?php /** @noinspection PhpUnused */
|
||||
|
||||
/** @noinspection PhpComposerExtensionStubsInspection */
|
||||
|
||||
|
||||
namespace ZM\DB;
|
||||
@@ -34,7 +36,7 @@ class DB
|
||||
* @return Table
|
||||
* @throws DbException
|
||||
*/
|
||||
public static function table($table_name) {
|
||||
public static function table($table_name): Table {
|
||||
if (Table::getTableInstance($table_name) === null) {
|
||||
if (in_array($table_name, self::$table_list))
|
||||
return new Table($table_name);
|
||||
@@ -60,7 +62,7 @@ class DB
|
||||
* @return bool
|
||||
* @throws DbException
|
||||
*/
|
||||
public static function unprepared($line) {
|
||||
public static function unprepared($line): bool {
|
||||
try {
|
||||
$conn = SqlPoolStorage::$sql_pool->get();
|
||||
if ($conn === false) {
|
||||
@@ -134,7 +136,7 @@ class DB
|
||||
}
|
||||
}
|
||||
|
||||
public static function isTableExists($table) {
|
||||
public static function isTableExists($table): bool {
|
||||
return in_array($table, self::$table_list);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ class SelectBody
|
||||
/**
|
||||
* @throws DbException
|
||||
*/
|
||||
public function count() {
|
||||
public function count(): int {
|
||||
$this->select_thing = ["count(*)"];
|
||||
$str = $this->queryPrepare();
|
||||
$this->result = DB::rawQuery($str[0], $str[1]);
|
||||
@@ -81,7 +81,7 @@ class SelectBody
|
||||
|
||||
public function getResult() { return $this->result; }
|
||||
|
||||
public function equals(SelectBody $body) {
|
||||
public function equals(SelectBody $body): bool {
|
||||
if ($this->select_thing != $body->getSelectThing()) return false;
|
||||
elseif ($this->where_thing == $body->getWhereThing()) return false;
|
||||
else return true;
|
||||
@@ -95,9 +95,9 @@ class SelectBody
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getWhereThing() { return $this->where_thing; }
|
||||
public function getWhereThing(): array { return $this->where_thing; }
|
||||
|
||||
private function queryPrepare() {
|
||||
private function queryPrepare(): array {
|
||||
$msg = "SELECT " . implode(", ", $this->select_thing) . " FROM " . $this->table->getTableName();
|
||||
$sql = $this->table->paintWhereSQL($this->where_thing['='] ?? [], '=');
|
||||
if ($sql[0] != '') {
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<?php
|
||||
<?php /** @noinspection PhpUnused */
|
||||
|
||||
/** @noinspection PhpMissingReturnTypeInspection */
|
||||
|
||||
|
||||
namespace ZM\DB;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?php
|
||||
<?php /** @noinspection PhpMissingReturnTypeInspection */
|
||||
|
||||
|
||||
namespace ZM\DB;
|
||||
|
||||
26
src/ZM/Entity/CQObject.php
Normal file
26
src/ZM/Entity/CQObject.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Entity;
|
||||
|
||||
|
||||
class CQObject
|
||||
{
|
||||
public $type;
|
||||
public $params;
|
||||
public $start;
|
||||
public $end;
|
||||
|
||||
public function __construct($type = "", $params = [], $start = 0, $end = 0) {
|
||||
if ($type !== "") {
|
||||
$this->type = $type;
|
||||
$this->params = $params;
|
||||
$this->start = $start;
|
||||
$this->end = $end;
|
||||
}
|
||||
}
|
||||
|
||||
public static function fromArray($arr): CQObject {
|
||||
return new CQObject($arr["type"], $arr["params"] ?? [], $arr["start"], $arr["end"]);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
<?php
|
||||
<?php /** @noinspection PhpUnused */
|
||||
|
||||
|
||||
namespace ZM\Event;
|
||||
@@ -9,7 +9,6 @@ use Error;
|
||||
use Exception;
|
||||
use ZM\Console\Console;
|
||||
use ZM\Exception\InterruptException;
|
||||
use ZM\Exception\ZMException;
|
||||
use ZM\Store\LightCacheInside;
|
||||
use ZM\Store\Lock\SpinLock;
|
||||
use ZM\Store\ZMAtomic;
|
||||
@@ -32,7 +31,7 @@ class EventDispatcher
|
||||
/** @var bool */
|
||||
private $log = false;
|
||||
/** @var int */
|
||||
private $eid = 0;
|
||||
private $eid;
|
||||
/** @var int */
|
||||
public $status = self::STATUS_NORMAL;
|
||||
/** @var mixed */
|
||||
@@ -64,22 +63,18 @@ class EventDispatcher
|
||||
|
||||
public function __construct(string $class = '') {
|
||||
$this->class = $class;
|
||||
try {
|
||||
$this->eid = ZMAtomic::get("_event_id")->add(1);
|
||||
$list = LightCacheInside::get("wait_api", "event_trace");
|
||||
} catch (ZMException $e) {
|
||||
$list = [];
|
||||
}
|
||||
$this->eid = ZMAtomic::get("_event_id")->add(1);
|
||||
$list = LightCacheInside::get("wait_api", "event_trace");
|
||||
if (isset($list[$class])) $this->log = true;
|
||||
if ($this->log) Console::verbose("[事件分发{$this->eid}] 开始分发事件: " . $class);
|
||||
}
|
||||
|
||||
public function setRuleFunction(callable $rule = null) {
|
||||
public function setRuleFunction(callable $rule = null): EventDispatcher {
|
||||
$this->rule = $rule;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setReturnFunction(callable $return_func) {
|
||||
public function setReturnFunction(callable $return_func): EventDispatcher {
|
||||
$this->return_func = $return_func;
|
||||
return $this;
|
||||
}
|
||||
@@ -100,6 +95,7 @@ class EventDispatcher
|
||||
}
|
||||
}
|
||||
if ($this->status === self::STATUS_RULE_FAILED) $this->status = self::STATUS_NORMAL;
|
||||
//TODO:没有过滤before的false,可能会导致一些问题,先观望一下
|
||||
} catch (InterruptException $e) {
|
||||
$this->store = $e->return_var;
|
||||
$this->status = self::STATUS_INTERRUPTED;
|
||||
@@ -116,6 +112,7 @@ class EventDispatcher
|
||||
* @return bool
|
||||
* @throws InterruptException
|
||||
* @throws AnnotationException
|
||||
* @noinspection PhpMissingReturnTypeInspection
|
||||
*/
|
||||
public function dispatchEvent($v, $rule_func = null, ...$params) {
|
||||
$q_c = $v->class;
|
||||
|
||||
@@ -9,7 +9,6 @@ use Exception;
|
||||
use Swoole\Timer;
|
||||
use ZM\Annotation\AnnotationBase;
|
||||
use ZM\Annotation\AnnotationParser;
|
||||
use ZM\Annotation\Swoole\OnSave;
|
||||
use ZM\Annotation\Swoole\OnTick;
|
||||
use ZM\Config\ZMConfig;
|
||||
use ZM\Console\Console;
|
||||
@@ -37,6 +36,7 @@ class EventManager
|
||||
|
||||
/**
|
||||
* 注册所有计时器给每个进程
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function registerTimerTick() {
|
||||
$dispatcher = new EventDispatcher(OnTick::class);
|
||||
|
||||
@@ -17,6 +17,7 @@ use Swoole\Database\PDOConfig;
|
||||
use Swoole\Database\PDOPool;
|
||||
use Swoole\Event;
|
||||
use Swoole\Process;
|
||||
use Throwable;
|
||||
use ZM\Annotation\AnnotationParser;
|
||||
use ZM\Annotation\Http\RequestMapping;
|
||||
use ZM\Annotation\Swoole\OnCloseEvent;
|
||||
@@ -26,6 +27,8 @@ use ZM\Annotation\Swoole\OnPipeMessageEvent;
|
||||
use ZM\Annotation\Swoole\OnRequestEvent;
|
||||
use ZM\Annotation\Swoole\OnStart;
|
||||
use ZM\Annotation\Swoole\OnSwooleEvent;
|
||||
use ZM\Annotation\Swoole\OnTask;
|
||||
use ZM\Annotation\Swoole\OnTaskEvent;
|
||||
use ZM\Config\ZMConfig;
|
||||
use ZM\ConnectionManager\ManagerGM;
|
||||
use ZM\Console\Console;
|
||||
@@ -47,10 +50,8 @@ use ZM\Store\LightCacheInside;
|
||||
use ZM\Store\MySQL\SqlPoolStorage;
|
||||
use ZM\Store\Redis\ZMRedisPool;
|
||||
use ZM\Store\WorkerCache;
|
||||
use ZM\Store\ZMBuf;
|
||||
use ZM\Utils\DataProvider;
|
||||
use ZM\Utils\HttpUtil;
|
||||
use ZM\Utils\Terminal;
|
||||
use ZM\Utils\ZMUtil;
|
||||
|
||||
class ServerEventHandler
|
||||
@@ -59,9 +60,9 @@ class ServerEventHandler
|
||||
* @SwooleHandler("start")
|
||||
*/
|
||||
public function onStart() {
|
||||
global $terminal_id;
|
||||
//global $terminal_id;
|
||||
$r = null;
|
||||
if ($terminal_id !== null) {
|
||||
/*if ($terminal_id !== null) {
|
||||
ZMBuf::$terminal = $r = STDIN;
|
||||
Event::add($r, function () use ($r) {
|
||||
$fget = fgets($r);
|
||||
@@ -78,7 +79,7 @@ class ServerEventHandler
|
||||
Console::error("Uncaught error " . get_class($e) . ": " . $e->getMessage() . " at " . $e->getFile() . "(" . $e->getLine() . ")");
|
||||
}
|
||||
});
|
||||
}
|
||||
}*/
|
||||
Process::signal(SIGINT, function () use ($r) {
|
||||
if (zm_atomic("_int_is_reload")->get() === 1) {
|
||||
zm_atomic("_int_is_reload")->set(0);
|
||||
@@ -126,6 +127,7 @@ class ServerEventHandler
|
||||
* @SwooleHandler("WorkerStop")
|
||||
* @param $server
|
||||
* @param $worker_id
|
||||
* @throws Exception
|
||||
*/
|
||||
public function onWorkerStop(Server $server, $worker_id) {
|
||||
if ($worker_id == (ZMConfig::get("worker_cache")["worker"] ?? 0)) {
|
||||
@@ -159,7 +161,7 @@ class ServerEventHandler
|
||||
else server()->shutdown();
|
||||
});
|
||||
|
||||
Console::info("Worker #{$server->worker_id} 启动中");
|
||||
Console::verbose("Worker #{$server->worker_id} 启动中");
|
||||
Framework::$server = $server;
|
||||
//ZMBuf::resetCache(); //清空变量缓存
|
||||
//ZMBuf::set("wait_start", []); //添加队列,在workerStart运行完成前先让其他协程等待执行
|
||||
@@ -233,7 +235,7 @@ class ServerEventHandler
|
||||
$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]);
|
||||
@@ -244,6 +246,7 @@ class ServerEventHandler
|
||||
$dispatcher->dispatchEvents($server, $worker_id);
|
||||
if ($dispatcher->status === EventDispatcher::STATUS_NORMAL) Console::debug("@OnStart 执行完毕");
|
||||
else Console::warning("@OnStart 执行异常!");
|
||||
Console::success("Worker #" . $worker_id . " 已启动");
|
||||
} catch (Exception $e) {
|
||||
Console::error("Worker加载出错!停止服务!");
|
||||
Console::error($e->getMessage() . "\n" . $e->getTraceAsString());
|
||||
@@ -260,7 +263,7 @@ class ServerEventHandler
|
||||
try {
|
||||
Framework::$server = $server;
|
||||
$this->loadAnnotations();
|
||||
Console::debug("TaskWorker #" . $server->worker_id . " 已启动");
|
||||
Console::success("TaskWorker #" . $server->worker_id . " 已启动");
|
||||
} catch (Exception $e) {
|
||||
Console::error("Worker加载出错!停止服务!");
|
||||
Console::error($e->getMessage() . "\n" . $e->getTraceAsString());
|
||||
@@ -316,7 +319,7 @@ class ServerEventHandler
|
||||
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::error("Uncaught " . get_class($e) . " when calling \"message\": " . $error_msg);
|
||||
Console::trace();
|
||||
}
|
||||
|
||||
@@ -466,7 +469,7 @@ class ServerEventHandler
|
||||
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::error("Uncaught " . get_class($e) . " when calling \"open\": " . $error_msg);
|
||||
Console::trace();
|
||||
}
|
||||
//EventHandler::callSwooleEvent("open", $server, $request);
|
||||
@@ -513,7 +516,7 @@ class ServerEventHandler
|
||||
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::error("Uncaught " . get_class($e) . " when calling \"close\": " . $error_msg);
|
||||
Console::trace();
|
||||
}
|
||||
ManagerGM::popConnect($fd);
|
||||
@@ -595,20 +598,53 @@ class ServerEventHandler
|
||||
* @SwooleHandler("task")
|
||||
* @param Server|null $server
|
||||
* @param Server\Task $task
|
||||
* @return mixed
|
||||
* @noinspection PhpUnusedParameterInspection
|
||||
*/
|
||||
public function onTask(?Server $server, Server\Task $task) {
|
||||
$data = $task->data;
|
||||
switch ($data["action"]) {
|
||||
case "runMethod":
|
||||
$c = $data["class"];
|
||||
$ss = new $c();
|
||||
$method = $data["method"];
|
||||
$ps = $data["params"];
|
||||
$task->finish($ss->$method(...$ps));
|
||||
if (isset($task->data["task"])) {
|
||||
$dispatcher = new EventDispatcher(OnTask::class);
|
||||
$dispatcher->setRuleFunction(function ($v) use ($task) {
|
||||
/** @var OnTask $v */
|
||||
return $v->task_name == $task->data["task"];
|
||||
});
|
||||
$dispatcher->setReturnFunction(function ($return) {
|
||||
EventDispatcher::interrupt($return);
|
||||
});
|
||||
$params = $task->data["params"];
|
||||
try {
|
||||
$dispatcher->dispatchEvents(...$params);
|
||||
} catch (Throwable $e) {
|
||||
$finish["throw"] = $e;
|
||||
}
|
||||
if ($dispatcher->status === EventDispatcher::STATUS_EXCEPTION) {
|
||||
$finish["result"] = null;
|
||||
$finish["retcode"] = -1;
|
||||
} else {
|
||||
$finish["result"] = $dispatcher->store;
|
||||
$finish["retcode"] = 0;
|
||||
}
|
||||
if (zm_atomic("server_is_stopped")->get() === 1) {
|
||||
return;
|
||||
}
|
||||
$task->finish($finish);
|
||||
} else {
|
||||
try {
|
||||
$dispatcher = new EventDispatcher(OnTaskEvent::class);
|
||||
$dispatcher->setRuleFunction(function ($v) {
|
||||
/** @var OnTaskEvent $v */
|
||||
return eval("return " . $v->getRule() . ";");
|
||||
});
|
||||
$dispatcher->dispatchEvents();
|
||||
} catch (Exception $e) {
|
||||
$error_msg = $e->getMessage() . " at " . $e->getFile() . "(" . $e->getLine() . ")";
|
||||
Console::error("Uncaught exception " . get_class($e) . " when calling \"task\": " . $error_msg);
|
||||
Console::trace();
|
||||
} catch (Error $e) {
|
||||
$error_msg = $e->getMessage() . " at " . $e->getFile() . "(" . $e->getLine() . ")";
|
||||
Console::error("Uncaught " . get_class($e) . " when calling \"task\": " . $error_msg);
|
||||
Console::trace();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -641,7 +677,8 @@ class ServerEventHandler
|
||||
$composer = json_decode(file_get_contents(DataProvider::getWorkingDir() . "/composer.json"), true);
|
||||
foreach ($dir as $v) {
|
||||
if (is_dir($path . "/" . $v) && isset($composer["autoload"]["psr-4"][$v . "\\"]) && !in_array($composer["autoload"]["psr-4"][$v . "\\"], $composer["extra"]["exclude_annotate"] ?? [])) {
|
||||
Console::verbose("Add " . $v . " to register path");
|
||||
if (\server()->worker_id == 0)
|
||||
Console::verbose("Add " . $v . " to register path");
|
||||
$parser->addRegisterPath(DataProvider::getWorkingDir() . "/src/" . $v . "/", $v);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,14 +45,15 @@ class Framework
|
||||
|
||||
self::$argv = $args;
|
||||
|
||||
//定义常量
|
||||
include_once "global_defines.php";
|
||||
|
||||
ZMConfig::setDirectory(DataProvider::getWorkingDir() . '/config');
|
||||
ZMConfig::setEnv($args["env"] ?? "");
|
||||
if (ZMConfig::get("global") === false) {
|
||||
die ("Global config load failed: " . ZMConfig::$last_error . "\nPlease init first!\n");
|
||||
}
|
||||
|
||||
//定义常量
|
||||
include_once "global_defines.php";
|
||||
|
||||
ZMAtomic::init();
|
||||
try {
|
||||
$sw = ZMConfig::get("global");
|
||||
@@ -74,7 +75,6 @@ class Framework
|
||||
die($e->getMessage());
|
||||
}
|
||||
try {
|
||||
|
||||
Console::init(
|
||||
ZMConfig::get("global", "info_level") ?? 2,
|
||||
self::$server,
|
||||
@@ -94,7 +94,7 @@ class Framework
|
||||
else $out["worker"] = ZMConfig::get("global", "swoole")["worker_num"];
|
||||
$out["environment"] = $args["env"] === null ? "default" : $args["env"];
|
||||
$out["log_level"] = Console::getLevel();
|
||||
$out["version"] = ZM_VERSION;
|
||||
$out["version"] = ZM_VERSION . (LOAD_MODE == 0 ? (" (build " . ZM_VERSION_ID . ")") : "");
|
||||
if (APP_VERSION !== "unknown") $out["app_version"] = APP_VERSION;
|
||||
if (isset(ZMConfig::get("global", "swoole")["task_worker_num"])) {
|
||||
$out["task_worker"] = ZMConfig::get("global", "swoole")["task_worker_num"];
|
||||
@@ -112,6 +112,7 @@ class Framework
|
||||
}
|
||||
if (self::$argv["show-php-ver"] !== false) {
|
||||
$out["php_version"] = PHP_VERSION;
|
||||
$out["swoole_version"] = SWOOLE_VERSION;
|
||||
}
|
||||
|
||||
$out["working_dir"] = DataProvider::getWorkingDir();
|
||||
@@ -120,7 +121,7 @@ class Framework
|
||||
self::$server = new Server(ZMConfig::get("global", "host"), ZMConfig::get("global", "port"));
|
||||
|
||||
self::$server->set($this->server_set);
|
||||
|
||||
Console::setServer(self::$server);
|
||||
self::printMotd($tty_width);
|
||||
|
||||
global $asd;
|
||||
@@ -195,6 +196,8 @@ class Framework
|
||||
|
||||
public function start() {
|
||||
self::$server->start();
|
||||
zm_atomic("server_is_stopped")->set(1);
|
||||
Console::setLevel(0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -337,7 +340,7 @@ class Framework
|
||||
} else { // 输出很小,写到前面并分片
|
||||
//Console::info("输出很小,写到前面并分片");
|
||||
$space = intval($max_border / 2) - 2 - strlen($tmp_line);
|
||||
$line_data[$current_line] .= str_pad("", $space, " ");
|
||||
$line_data[$current_line] .= str_pad("", $space);
|
||||
$line_data[$current_line] .= "| "; // 添加分片
|
||||
$line_width[$current_line] -= (strlen($tmp_line) + 3 + $space);
|
||||
}
|
||||
@@ -389,7 +392,7 @@ class Framework
|
||||
echo str_pad("", $max_border, "=") . PHP_EOL;
|
||||
}
|
||||
|
||||
public static function getTtyWidth() {
|
||||
public static function getTtyWidth(): string {
|
||||
return explode(" ", trim(exec("stty size")))[1];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<?php
|
||||
<?php /** @noinspection PhpUnused */
|
||||
|
||||
/** @noinspection PhpMissingReturnTypeInspection */
|
||||
|
||||
|
||||
namespace ZM\Http;
|
||||
|
||||
@@ -9,6 +9,7 @@ use Symfony\Component\Routing\RouteCollection;
|
||||
use ZM\Annotation\Http\Controller;
|
||||
use ZM\Annotation\Http\RequestMapping;
|
||||
use ZM\Console\Console;
|
||||
use ZM\Store\LightCacheInside;
|
||||
|
||||
class RouteManager
|
||||
{
|
||||
@@ -34,4 +35,26 @@ class RouteManager
|
||||
|
||||
self::$routes->add(md5($route_name), $route);
|
||||
}
|
||||
|
||||
public static function addStaticFileRoute($route, $path) {
|
||||
$tail = trim($route, "/");
|
||||
$route_name = ($tail === "" ? "" : "/") . $tail . "/{filename}";
|
||||
Console::debug("添加静态文件路由:" . $route_name);
|
||||
$route = new Route($route_name, ['_class' => RouteManager::class, '_method' => "onStaticRoute"]);
|
||||
//echo $path.PHP_EOL;
|
||||
LightCacheInside::set("static_route", $route->getPath(), $path);
|
||||
|
||||
self::$routes->add(md5($route_name), $route);
|
||||
}
|
||||
|
||||
public function onStaticRoute($params) {
|
||||
$route_path = self::$routes->get($params["_route"])->getPath();
|
||||
if(($path = LightCacheInside::get("static_route", $route_path)) === null) {
|
||||
ctx()->getResponse()->endWithStatus(404);
|
||||
return false;
|
||||
}
|
||||
unset($params["_route"]);
|
||||
$obj = array_shift($params);
|
||||
return new StaticFileHandler($obj, $path);
|
||||
}
|
||||
}
|
||||
@@ -55,7 +55,7 @@ class QQBot
|
||||
* @return EventDispatcher
|
||||
* @throws Exception
|
||||
*/
|
||||
public function dispatchBeforeEvents($data) {
|
||||
public function dispatchBeforeEvents($data): EventDispatcher {
|
||||
$before = new EventDispatcher(CQBefore::class);
|
||||
$before->setRuleFunction(function ($v) use ($data) {
|
||||
return $v->cq_event == $data["post_type"];
|
||||
@@ -70,6 +70,7 @@ class QQBot
|
||||
/**
|
||||
* @param $data
|
||||
* @throws InterruptException
|
||||
* @throws Exception
|
||||
*/
|
||||
private function dispatchEvents($data) {
|
||||
//Console::warning("最xia数据包:".json_encode($data));
|
||||
|
||||
@@ -27,6 +27,7 @@ class LightCache
|
||||
* @param $config
|
||||
* @return bool|mixed
|
||||
* @throws Exception
|
||||
* @noinspection PhpMissingReturnTypeInspection
|
||||
*/
|
||||
public static function init($config) {
|
||||
self::$config = $config;
|
||||
@@ -87,6 +88,7 @@ class LightCache
|
||||
* @param int $expire
|
||||
* @return mixed
|
||||
* @throws ZMException
|
||||
* @noinspection PhpMissingReturnTypeInspection
|
||||
*/
|
||||
public static function set(string $key, $value, int $expire = -1) {
|
||||
if (self::$kv_table === null) throw new ZMException("not initialized LightCache");
|
||||
@@ -120,6 +122,7 @@ class LightCache
|
||||
* @param $value
|
||||
* @return bool|mixed
|
||||
* @throws ZMException
|
||||
* @noinspection PhpMissingReturnTypeInspection
|
||||
*/
|
||||
public static function update(string $key, $value) {
|
||||
if (self::$kv_table === null) throw new ZMException("not initialized LightCache.");
|
||||
@@ -154,7 +157,7 @@ class LightCache
|
||||
* @return bool
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function isset(string $key) {
|
||||
public static function isset(string $key): bool {
|
||||
return self::get($key) !== null;
|
||||
}
|
||||
|
||||
@@ -162,7 +165,7 @@ class LightCache
|
||||
return self::$kv_table->del($key);
|
||||
}
|
||||
|
||||
public static function getAll() {
|
||||
public static function getAll(): array {
|
||||
$r = [];
|
||||
$del = [];
|
||||
foreach (self::$kv_table as $k => $v) {
|
||||
@@ -178,8 +181,11 @@ class LightCache
|
||||
return $r;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param false $only_worker
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function savePersistence($only_worker = false) {
|
||||
|
||||
// 下面将OnSave激活一下
|
||||
if (server()->worker_id == (ZMConfig::get("global", "worker_cache")["worker"] ?? 0)) {
|
||||
$dispatcher = new EventDispatcher(OnSave::class);
|
||||
|
||||
@@ -13,30 +13,24 @@ class LightCacheInside
|
||||
/** @var Table[]|null */
|
||||
private static $kv_table = [];
|
||||
|
||||
public static $last_error = '';
|
||||
|
||||
public static function init() {
|
||||
self::$kv_table["wait_api"] = new Table(3, 0);
|
||||
self::$kv_table["wait_api"]->column("value", Table::TYPE_STRING, 65536);
|
||||
self::$kv_table["connect"] = new Table(8, 0);
|
||||
self::$kv_table["connect"]->column("value", Table::TYPE_STRING, 256);
|
||||
$result = self::$kv_table["wait_api"]->create() && self::$kv_table["connect"]->create();
|
||||
if ($result === false) {
|
||||
self::$last_error = '系统内存不足,申请失败';
|
||||
public static function init(): bool {
|
||||
try {
|
||||
self::createTable("wait_api", 3, 65536);
|
||||
self::createTable("connect", 3, 64); //用于存单机器人模式下的机器人fd的
|
||||
self::createTable("static_route", 64, 256);//用于存储
|
||||
} catch (ZMException $e) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
} //用于存协程等待的状态内容的
|
||||
//self::createTable("worker_start", 2, 1024);//用于存启动服务器时的状态的
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $table
|
||||
* @param string $key
|
||||
* @return mixed|null
|
||||
* @throws ZMException
|
||||
*/
|
||||
public static function get(string $table, string $key) {
|
||||
if (!isset(self::$kv_table[$table])) throw new ZMException("not initialized LightCache");
|
||||
$r = self::$kv_table[$table]->get($key);
|
||||
return $r === false ? null : json_decode($r["value"], true);
|
||||
}
|
||||
@@ -46,10 +40,8 @@ class LightCacheInside
|
||||
* @param string $key
|
||||
* @param string|array|int $value
|
||||
* @return mixed
|
||||
* @throws ZMException
|
||||
*/
|
||||
public static function set(string $table, string $key, $value) {
|
||||
if (self::$kv_table === null) throw new ZMException("not initialized LightCache");
|
||||
public static function set(string $table, string $key, $value): bool {
|
||||
try {
|
||||
return self::$kv_table[$table]->set($key, [
|
||||
"value" => json_encode($value, 256)
|
||||
@@ -62,4 +54,17 @@ class LightCacheInside
|
||||
public static function unset(string $table, string $key) {
|
||||
return self::$kv_table[$table]->del($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
* @param $size
|
||||
* @param $str_size
|
||||
* @throws ZMException
|
||||
*/
|
||||
private static function createTable($name, $size, $str_size) {
|
||||
self::$kv_table[$name] = new Table($size, 0);
|
||||
self::$kv_table[$name]->column("value", Table::TYPE_STRING, $str_size);
|
||||
$r = self::$kv_table[$name]->create();
|
||||
if ($r === false) throw new ZMException("内存不足,创建静态表失败!");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?php
|
||||
<?php /** @noinspection PhpUnused */
|
||||
|
||||
|
||||
namespace ZM\Store\Lock;
|
||||
@@ -29,7 +29,7 @@ class SpinLock
|
||||
}
|
||||
}
|
||||
|
||||
public static function tryLock(string $key) {
|
||||
public static function tryLock(string $key): bool {
|
||||
if (($r = self::$kv_lock->incr($key, 'lock_num')) > 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<?php /** @noinspection PhpComposerExtensionStubsInspection */
|
||||
<?php /** @noinspection PhpUnused */
|
||||
|
||||
/** @noinspection PhpComposerExtensionStubsInspection */
|
||||
|
||||
|
||||
namespace ZM\Store\Redis;
|
||||
@@ -36,7 +38,7 @@ class ZMRedis
|
||||
/**
|
||||
* @return Redis
|
||||
*/
|
||||
public function get() {
|
||||
public function get(): Redis {
|
||||
return $this->conn;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?php
|
||||
<?php /** @noinspection PhpMissingReturnTypeInspection */
|
||||
|
||||
|
||||
namespace ZM\Store;
|
||||
|
||||
@@ -16,7 +16,7 @@ class ZMAtomic
|
||||
* @param $name
|
||||
* @return Atomic|null
|
||||
*/
|
||||
public static function get($name) {
|
||||
public static function get($name): ?Atomic {
|
||||
return self::$atomics[$name] ?? null;
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ class ZMAtomic
|
||||
self::$atomics["_int_is_reload"] = new Atomic(0);
|
||||
self::$atomics["wait_msg_id"] = new Atomic(0);
|
||||
self::$atomics["_event_id"] = new Atomic(0);
|
||||
self::$atomics["server_is_stopped"] = new Atomic(0);
|
||||
for ($i = 0; $i < 10; ++$i) {
|
||||
self::$atomics["_tmp_" . $i] = new Atomic(0);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ namespace ZM\Utils;
|
||||
|
||||
|
||||
use Co;
|
||||
use Exception;
|
||||
use ZM\Store\LightCacheInside;
|
||||
use ZM\Store\Lock\SpinLock;
|
||||
use ZM\Store\ZMAtomic;
|
||||
@@ -17,7 +16,7 @@ class CoMessage
|
||||
* @param array $compare
|
||||
* @param int $timeout
|
||||
* @return mixed
|
||||
* @throws Exception
|
||||
* @noinspection PhpMissingReturnTypeInspection
|
||||
*/
|
||||
public static function yieldByWS(array $hang, array $compare, $timeout = 600) {
|
||||
$cid = Co::getuid();
|
||||
@@ -40,7 +39,7 @@ class CoMessage
|
||||
Co::suspend();
|
||||
SpinLock::lock("wait_api");
|
||||
$sess = LightCacheInside::get("wait_api", "wait_api");
|
||||
$result = $sess[$api_id]["result"];
|
||||
$result = $sess[$api_id]["result"] ?? null;
|
||||
unset($sess[$api_id]);
|
||||
LightCacheInside::set("wait_api", "wait_api", $sess);
|
||||
SpinLock::unlock("wait_api");
|
||||
@@ -49,7 +48,7 @@ class CoMessage
|
||||
return $result;
|
||||
}
|
||||
|
||||
public static function resumeByWS() {
|
||||
public static function resumeByWS(): bool {
|
||||
$dat = ctx()->getData();
|
||||
$last = null;
|
||||
SpinLock::lock("wait_api");
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?php
|
||||
<?php /** @noinspection PhpUnused */
|
||||
|
||||
|
||||
namespace ZM\Utils;
|
||||
@@ -11,7 +11,7 @@ class DataProvider
|
||||
{
|
||||
public static $buffer_list = [];
|
||||
|
||||
public static function getResourceFolder() {
|
||||
public static function getResourceFolder(): string {
|
||||
return self::getWorkingDir() . '/resources/';
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?php
|
||||
<?php /** @noinspection PhpMissingReturnTypeInspection */
|
||||
|
||||
|
||||
namespace ZM\Utils;
|
||||
@@ -17,6 +17,7 @@ use ZM\Http\RouteManager;
|
||||
|
||||
class HttpUtil
|
||||
{
|
||||
/** @noinspection PhpMissingReturnTypeInspection */
|
||||
public static function parseUri($request, $response, $uri, &$node, &$params) {
|
||||
$context = new RequestContext();
|
||||
$context->setMethod($request->server['request_method']);
|
||||
|
||||
79
src/ZM/Utils/MessageUtil.php
Normal file
79
src/ZM/Utils/MessageUtil.php
Normal file
@@ -0,0 +1,79 @@
|
||||
<?php /** @noinspection PhpUnused */
|
||||
|
||||
|
||||
namespace ZM\Utils;
|
||||
|
||||
|
||||
use ZM\API\CQ;
|
||||
use ZM\Config\ZMConfig;
|
||||
use ZM\Console\Console;
|
||||
use ZM\Requests\ZMRequest;
|
||||
|
||||
class MessageUtil
|
||||
{
|
||||
/**
|
||||
* 下载消息中 CQ 码的所有图片,通过 url
|
||||
* @param $msg
|
||||
* @param null $path
|
||||
* @return array|false
|
||||
*/
|
||||
public static function downloadCQImage($msg, $path = null) {
|
||||
$path = $path ?? DataProvider::getDataFolder() . "images/";
|
||||
if (!is_dir($path)) mkdir($path);
|
||||
$path = realpath($path);
|
||||
if ($path === false) {
|
||||
Console::warning("指定的路径错误不存在!");
|
||||
return false;
|
||||
}
|
||||
$files = [];
|
||||
$cq = CQ::getAllCQ($msg, true);
|
||||
foreach ($cq as $v) {
|
||||
if ($v->type == "image") {
|
||||
$result = ZMRequest::downloadFile($v->params["url"], $path . "/" . $v->params["file"]);
|
||||
if ($result === false) {
|
||||
Console::warning("图片 " . $v->params["url"] . " 下载失败!");
|
||||
return false;
|
||||
}
|
||||
$files[] = $path . "/" . $v->params["file"];
|
||||
}
|
||||
}
|
||||
return $files;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查消息中是否含有图片 CQ 码
|
||||
* @param $msg
|
||||
* @return bool
|
||||
*/
|
||||
public static function containsImage($msg): bool {
|
||||
$cq = CQ::getAllCQ($msg, true);
|
||||
foreach ($cq as $v) {
|
||||
if ($v->type == "image") {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过本地地址返回图片的 CQ 码
|
||||
* type == 0 : 返回图片的 base64 CQ 码
|
||||
* type == 1 : 返回图片的 file://路径 CQ 码(路径必须为绝对路径)
|
||||
* type == 2 : 返回图片的 http://xxx CQ 码(默认为 /images/ 路径就是文件对应所在的目录)
|
||||
* @param $file
|
||||
* @param int $type
|
||||
* @return string
|
||||
*/
|
||||
public static function getImageCQFromLocal($file, $type = 0): string {
|
||||
switch ($type) {
|
||||
case 0:
|
||||
return CQ::image("base64://" . base64_encode(file_get_contents($file)));
|
||||
case 1:
|
||||
return CQ::image("file://" . $file);
|
||||
case 2:
|
||||
$info = pathinfo($file);
|
||||
return CQ::image(ZMConfig::get("global", "http_reverse_link") . "/images/" . $info["basename"]);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
<?php
|
||||
<?php /** @noinspection PhpUnused */
|
||||
|
||||
|
||||
namespace ZM\Utils;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?php
|
||||
<?php /** @noinspection PhpUnused */
|
||||
|
||||
|
||||
namespace ZM\Utils;
|
||||
@@ -15,6 +15,7 @@ trait SingletonTrait
|
||||
|
||||
/**
|
||||
* @return self
|
||||
* @noinspection PhpMissingReturnTypeInspection
|
||||
*/
|
||||
public static function getInstance() {
|
||||
if (null === self::$instance) {
|
||||
|
||||
26
src/ZM/Utils/TaskManager.php
Normal file
26
src/ZM/Utils/TaskManager.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php /** @noinspection PhpUnused */
|
||||
|
||||
|
||||
namespace ZM\Utils;
|
||||
|
||||
|
||||
use ZM\Console\Console;
|
||||
|
||||
class TaskManager
|
||||
{
|
||||
/**
|
||||
* @noinspection PhpMissingReturnTypeInspection
|
||||
* @param $task_name
|
||||
* @param int $timeout
|
||||
* @param mixed ...$params
|
||||
* @return false|mixed
|
||||
*/
|
||||
public static function runTask($task_name, $timeout = -1, ...$params) {
|
||||
if (!isset(server()->setting["task_worker_num"])) {
|
||||
Console::warning("未开启 TaskWorker 进程,请先修改 global 配置文件启用!");
|
||||
return false;
|
||||
}
|
||||
$r = server()->taskwait(["task" => $task_name, "params" => $params], $timeout);
|
||||
return $r === false ? false : $r["result"];
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
<?php
|
||||
<?php /** @noinspection PhpUnused */
|
||||
|
||||
|
||||
namespace ZM\Utils;
|
||||
@@ -16,6 +16,8 @@ class Terminal
|
||||
* @param string $cmd
|
||||
* @param $resource
|
||||
* @return bool
|
||||
* @noinspection PhpMissingReturnTypeInspection
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
public static function executeCommand(string $cmd, $resource) {
|
||||
$it = explodeMsg($cmd);
|
||||
|
||||
@@ -17,6 +17,9 @@ use ZM\Store\ZMBuf;
|
||||
|
||||
class ZMUtil
|
||||
{
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function stop() {
|
||||
if (SpinLock::tryLock("_stop_signal") === false) return;
|
||||
Console::warning(Console::setColor("Stopping server...", "red"));
|
||||
@@ -33,6 +36,10 @@ class ZMUtil
|
||||
server()->stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $delay
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function reload($delay = 800) {
|
||||
if (server()->worker_id !== -1) {
|
||||
Console::info(server()->worker_id);
|
||||
|
||||
@@ -5,7 +5,6 @@ use ZM\Utils\DataProvider;
|
||||
|
||||
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("APP_VERSION", LOAD_MODE == 1 ? (json_decode(file_get_contents(DataProvider::getWorkingDir() . "/composer.json"), true)["version"] ?? "unknown") : "unknown");
|
||||
define("CRASH_DIR", ZMConfig::get("global", "crash_dir"));
|
||||
@mkdir(ZM_DATA);
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<?php #plain
|
||||
<?php /** @noinspection PhpUnused */ #plain
|
||||
|
||||
use Swoole\Atomic;
|
||||
use Swoole\Coroutine;
|
||||
use Swoole\WebSocket\Server;
|
||||
use ZM\API\ZMRobot;
|
||||
use ZM\Config\ZMConfig;
|
||||
use ZM\ConnectionManager\ManagerGM;
|
||||
@@ -11,6 +13,7 @@ use ZM\Exception\RobotNotFoundException;
|
||||
use ZM\Exception\ZMException;
|
||||
use ZM\Framework;
|
||||
use ZM\Store\LightCacheInside;
|
||||
use ZM\Store\ZMAtomic;
|
||||
use ZM\Store\ZMBuf;
|
||||
use ZM\Utils\DataProvider;
|
||||
use Swoole\Coroutine\System;
|
||||
@@ -52,7 +55,7 @@ function getClassPath($class_name) {
|
||||
* @param bool $ban_comma
|
||||
* @return array
|
||||
*/
|
||||
function explodeMsg($msg, $ban_comma = false) {
|
||||
function explodeMsg($msg, $ban_comma = false): array {
|
||||
$msg = str_replace(" ", "\n", trim($msg));
|
||||
if (!$ban_comma) {
|
||||
//$msg = str_replace(",", "\n", $msg);
|
||||
@@ -79,7 +82,7 @@ function unicode_decode($str) {
|
||||
* @param $indoor_name
|
||||
* @return array
|
||||
*/
|
||||
function getAllClasses($dir, $indoor_name) {
|
||||
function getAllClasses($dir, $indoor_name): array {
|
||||
if (!is_dir($dir)) return [];
|
||||
$list = scandir($dir);
|
||||
$classes = [];
|
||||
@@ -104,7 +107,7 @@ function getAllClasses($dir, $indoor_name) {
|
||||
return $classes;
|
||||
}
|
||||
|
||||
function matchPattern($pattern, $context) {
|
||||
function matchPattern($pattern, $context): bool {
|
||||
if (mb_substr($pattern, 0, 1) == "" && mb_substr($context, 0, 1) == "")
|
||||
return true;
|
||||
if ('*' == mb_substr($pattern, 0, 1) && "" != mb_substr($pattern, 1, 1) && "" == mb_substr($context, 0, 1))
|
||||
@@ -116,7 +119,7 @@ function matchPattern($pattern, $context) {
|
||||
return false;
|
||||
}
|
||||
|
||||
function split_explode($del, $str, $divide_en = false) {
|
||||
function split_explode($del, $str, $divide_en = false): array {
|
||||
$str = explode($del, $str);
|
||||
for ($i = 0; $i < mb_strlen($str[0]); $i++) {
|
||||
if (
|
||||
@@ -178,19 +181,19 @@ function matchArgs($pattern, $context) {
|
||||
} else return false;
|
||||
}
|
||||
|
||||
function connectIsQQ() {
|
||||
function connectIsQQ(): bool {
|
||||
return ctx()->getConnection()->getName() == 'qq';
|
||||
}
|
||||
|
||||
function connectIsDefault() {
|
||||
function connectIsDefault(): bool {
|
||||
return ctx()->getConnection()->getName() == 'default';
|
||||
}
|
||||
|
||||
function connectIs($type) {
|
||||
function connectIs($type): bool {
|
||||
return ctx()->getConnection()->getName() == $type;
|
||||
}
|
||||
|
||||
function getAnnotations() {
|
||||
function getAnnotations(): array {
|
||||
$s = debug_backtrace()[1];
|
||||
//echo json_encode($s, 128|256);
|
||||
$list = [];
|
||||
@@ -218,14 +221,14 @@ function set_coroutine_params($array) {
|
||||
/**
|
||||
* @return ContextInterface|null
|
||||
*/
|
||||
function context() {
|
||||
function context(): ?ContextInterface {
|
||||
return ctx();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ContextInterface|null
|
||||
*/
|
||||
function ctx() {
|
||||
function ctx(): ?ContextInterface {
|
||||
$cid = Co::getCid();
|
||||
$c_class = ZMConfig::get("global", "context_class");
|
||||
if (isset(Context::$context[$cid])) {
|
||||
@@ -242,11 +245,11 @@ function ctx() {
|
||||
|
||||
function zm_debug($msg) { Console::debug($msg); }
|
||||
|
||||
function onebot_target_id_name($message_type) {
|
||||
function onebot_target_id_name($message_type): string {
|
||||
return ($message_type == "group" ? "group_id" : "user_id");
|
||||
}
|
||||
|
||||
function zm_sleep($s = 1) {
|
||||
function zm_sleep($s = 1): bool {
|
||||
if (Coroutine::getCid() != -1) System::sleep($s);
|
||||
else usleep($s * 1000 * 1000);
|
||||
return true;
|
||||
@@ -261,27 +264,41 @@ function zm_yield() { Co::yield(); }
|
||||
function zm_resume(int $cid) { Co::resume($cid); }
|
||||
|
||||
function zm_timer_after($ms, callable $callable) {
|
||||
go(function () use ($ms, $callable) {
|
||||
Swoole\Timer::after($ms, $callable);
|
||||
Swoole\Timer::after($ms, function () use ($callable) {
|
||||
call_with_catch($callable);
|
||||
});
|
||||
}
|
||||
|
||||
function call_with_catch($callable) {
|
||||
try {
|
||||
$callable();
|
||||
} 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 " . get_class($e) . " when calling \"message\": " . $error_msg);
|
||||
Console::trace();
|
||||
}
|
||||
}
|
||||
|
||||
function zm_timer_tick($ms, callable $callable) {
|
||||
if (zm_cid() === -1) {
|
||||
return go(function () use ($ms, $callable) {
|
||||
Console::debug("Adding extra timer tick of " . $ms . " ms");
|
||||
Swoole\Timer::tick($ms, $callable);
|
||||
Swoole\Timer::tick($ms, function () use ($callable) {call_with_catch($callable);});
|
||||
});
|
||||
} else {
|
||||
return Swoole\Timer::tick($ms, $callable);
|
||||
return Swoole\Timer::tick($ms, function () use ($callable) {call_with_catch($callable);});
|
||||
}
|
||||
}
|
||||
|
||||
function zm_data_hash($v) {
|
||||
function zm_data_hash($v): string {
|
||||
return md5($v["user_id"] . "^" . $v["self_id"] . "^" . $v["message_type"] . "^" . ($v[$v["message_type"] . "_id"] ?? $v["user_id"]));
|
||||
}
|
||||
|
||||
function server() {
|
||||
function server(): ?Server {
|
||||
return Framework::$server;
|
||||
}
|
||||
|
||||
@@ -290,7 +307,7 @@ function server() {
|
||||
* @throws RobotNotFoundException
|
||||
* @throws ZMException
|
||||
*/
|
||||
function bot() {
|
||||
function bot(): ZMRobot {
|
||||
if (($conn = LightCacheInside::get("connect", "conn_fd")) == -2) {
|
||||
return ZMRobot::getRandom();
|
||||
} elseif ($conn != -1) {
|
||||
@@ -315,11 +332,11 @@ function getAllFdByConnectType(string $type = 'default'): array {
|
||||
return $fds;
|
||||
}
|
||||
|
||||
function zm_atomic($name) {
|
||||
return \ZM\Store\ZMAtomic::get($name);
|
||||
function zm_atomic($name): ?Atomic {
|
||||
return ZMAtomic::get($name);
|
||||
}
|
||||
|
||||
function uuidgen($uppercase = false) {
|
||||
function uuidgen($uppercase = false): string {
|
||||
try {
|
||||
$data = random_bytes(16);
|
||||
} catch (Exception $e) {
|
||||
|
||||
@@ -1,256 +0,0 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZMTest\Mock;
|
||||
|
||||
|
||||
use Swoole\Http\Request;
|
||||
use Swoole\WebSocket\Frame;
|
||||
use Swoole\WebSocket\Server;
|
||||
use ZM\API\ZMRobot;
|
||||
use ZM\ConnectionManager\ConnectionObject;
|
||||
use ZM\Context\ContextInterface;
|
||||
use ZM\Http\Response;
|
||||
|
||||
class Context implements ContextInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Context constructor.
|
||||
* @param $cid
|
||||
*/
|
||||
public function __construct($cid) { }
|
||||
|
||||
/**
|
||||
* @return Server
|
||||
*/
|
||||
public function getServer() {
|
||||
// TODO: Implement getServer() method.
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Frame
|
||||
*/
|
||||
public function getFrame() {
|
||||
// TODO: Implement getFrame() method.
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getData() {
|
||||
// TODO: Implement getData() method.
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $data
|
||||
* @return mixed
|
||||
*/
|
||||
public function setData($data) {
|
||||
// TODO: Implement setData() method.
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ConnectionObject
|
||||
*/
|
||||
public function getConnection() {
|
||||
// TODO: Implement getConnection() method.
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int|null
|
||||
*/
|
||||
public function getFd() {
|
||||
// TODO: Implement getFd() method.
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getCid() {
|
||||
// TODO: Implement getCid() method.
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Response
|
||||
*/
|
||||
public function getResponse() {
|
||||
// TODO: Implement getResponse() method.
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Request
|
||||
*/
|
||||
public function getRequest() {
|
||||
// TODO: Implement getRequest() method.
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ZMRobot
|
||||
*/
|
||||
public function getRobot() {
|
||||
// TODO: Implement getRobot() method.
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getUserId() {
|
||||
// TODO: Implement getUserId() method.
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getGroupId() {
|
||||
// TODO: Implement getGroupId() method.
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getDiscussId() {
|
||||
// TODO: Implement getDiscussId() method.
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getMessageType() {
|
||||
// TODO: Implement getMessageType() method.
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getRobotId() {
|
||||
// TODO: Implement getRobotId() method.
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getMessage() {
|
||||
// TODO: Implement getMessage() method.
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $msg
|
||||
* @return mixed
|
||||
*/
|
||||
public function setMessage($msg) {
|
||||
// TODO: Implement setMessage() method.
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $id
|
||||
* @return mixed
|
||||
*/
|
||||
public function setUserId($id) {
|
||||
// TODO: Implement setUserId() method.
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $id
|
||||
* @return mixed
|
||||
*/
|
||||
public function setGroupId($id) {
|
||||
// TODO: Implement setGroupId() method.
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $id
|
||||
* @return mixed
|
||||
*/
|
||||
public function setDiscussId($id) {
|
||||
// TODO: Implement setDiscussId() method.
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $type
|
||||
* @return mixed
|
||||
*/
|
||||
public function setMessageType($type) {
|
||||
// TODO: Implement setMessageType() method.
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getCQResponse() {
|
||||
// TODO: Implement getCQResponse() method.
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $msg
|
||||
* @param bool $yield
|
||||
* @return mixed
|
||||
*/
|
||||
public function reply($msg, $yield = false) {
|
||||
echo $msg.PHP_EOL;
|
||||
// TODO: Implement reply() method.
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $msg
|
||||
* @param bool $yield
|
||||
* @return mixed
|
||||
*/
|
||||
public function finalReply($msg, $yield = false) {
|
||||
// TODO: Implement finalReply() method.
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $prompt
|
||||
* @param int $timeout
|
||||
* @param string $timeout_prompt
|
||||
* @return mixed
|
||||
*/
|
||||
public function waitMessage($prompt = "", $timeout = 600, $timeout_prompt = "") {
|
||||
// TODO: Implement waitMessage() method.
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $arg
|
||||
* @param $mode
|
||||
* @param $prompt_msg
|
||||
* @return mixed
|
||||
*/
|
||||
public function getArgs(&$arg, $mode, $prompt_msg) {
|
||||
$r = $arg;
|
||||
array_shift($r);
|
||||
return array_shift($r);
|
||||
// TODO: Implement getArgs() method.
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $key
|
||||
* @param $value
|
||||
* @return mixed
|
||||
*/
|
||||
public function setCache($key, $value) {
|
||||
// TODO: Implement setCache() method.
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $key
|
||||
* @return mixed
|
||||
*/
|
||||
public function getCache($key) {
|
||||
// TODO: Implement getCache() method.
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function cloneFromParent() {
|
||||
// TODO: Implement cloneFromParent() method.
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function copy() {
|
||||
// TODO: Implement copy() method.
|
||||
}
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
<?php
|
||||
/** @noinspection PhpFullyQualifiedNameUsageInspection */
|
||||
/** @noinspection PhpComposerExtensionStubsInspection */
|
||||
global $config;
|
||||
|
||||
/** bind host */
|
||||
$config['host'] = '0.0.0.0';
|
||||
|
||||
/** bind port */
|
||||
$config['port'] = 20001;
|
||||
|
||||
/** 框架开到公网或外部的HTTP访问链接,通过 DataProvider::getFrameworkLink() 获取 */
|
||||
$config['http_reverse_link'] = "http://127.0.0.1:" . $config['port'];
|
||||
|
||||
/** 框架是否启动debug模式 */
|
||||
$config['debug_mode'] = false;
|
||||
|
||||
/** 存放框架内文件数据的目录 */
|
||||
$config['zm_data'] = realpath(__DIR__ . "/../") . '/zm_data/';
|
||||
|
||||
/** 存放崩溃和运行日志的目录 */
|
||||
$config['crash_dir'] = $config['zm_data'] . 'crash/';
|
||||
|
||||
/** 对应swoole的server->set参数 */
|
||||
$config['swoole'] = [
|
||||
'log_file' => $config['crash_dir'] . 'swoole_error.log',
|
||||
'worker_num' => 8,
|
||||
'dispatch_mode' => 2,
|
||||
'max_coroutine' => 30000,
|
||||
//'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' => '',
|
||||
'sql_port' => 3306,
|
||||
'sql_username' => 'name',
|
||||
'sql_database' => 'db_name',
|
||||
'sql_password' => '',
|
||||
'sql_enable_cache' => true,
|
||||
'sql_reset_cache' => '0300',
|
||||
'sql_options' => [
|
||||
PDO::ATTR_STRINGIFY_FETCHES => false,
|
||||
PDO::ATTR_EMULATE_PREPARES => false
|
||||
],
|
||||
'sql_no_exception' => false,
|
||||
'sql_default_fetch_mode' => PDO::FETCH_ASSOC // added in 1.5.6
|
||||
];
|
||||
|
||||
/** CQHTTP连接约定的token */
|
||||
$config["access_token"] = "";
|
||||
|
||||
/** HTTP服务器固定请求头的返回 */
|
||||
$config['http_header'] = [
|
||||
'X-Powered-By' => 'zhamao-framework',
|
||||
'Content-Type' => 'text/html; charset=utf-8'
|
||||
];
|
||||
|
||||
/** HTTP服务器在指定状态码下回复的页面(默认) */
|
||||
$config['http_default_code_page'] = [
|
||||
'404' => '404.html'
|
||||
];
|
||||
|
||||
/** zhamao-framework在框架启动时初始化的atomic们 */
|
||||
$config['init_atomics'] = [
|
||||
//'custom_atomic_name' => 0, //自定义添加的Atomic
|
||||
];
|
||||
|
||||
/** 终端日志显示等级(0-4) */
|
||||
$config["info_level"] = 2;
|
||||
|
||||
/** 自动保存计时器的缓存保存时间(秒) */
|
||||
$config['auto_save_interval'] = 900;
|
||||
|
||||
/** 上下文接口类 implemented from ContextInterface */
|
||||
$config['context_class'] = \ZMTest\Mock\Context::class;
|
||||
|
||||
/** 静态文件访问 */
|
||||
$config['static_file_server'] = [
|
||||
'status' => false,
|
||||
'document_root' => realpath(__DIR__ . "/../") . '/resources/html',
|
||||
'document_index' => [
|
||||
'index.html'
|
||||
]
|
||||
];
|
||||
|
||||
/** 注册 Swoole Server 事件注解的类列表 */
|
||||
$config['server_event_handler_class'] = [
|
||||
\ZM\Event\ServerEventHandler::class,
|
||||
];
|
||||
|
||||
/** 注册自定义指令的类 */
|
||||
$config['command_register_class'] = [
|
||||
//\Custom\Command\CustomCommand::class
|
||||
];
|
||||
|
||||
/** 服务器启用的外部第三方和内部插件 */
|
||||
$config['modules'] = [
|
||||
'onebot' => true, // QQ机器人事件解析器,如果取消此项则默认为 true 开启状态,否则你手动填写 false 才会关闭
|
||||
];
|
||||
|
||||
return $config;
|
||||
@@ -40,10 +40,7 @@ class EventDispatcherTest extends TestCase
|
||||
$dispatcher->setRuleFunction(function ($v) { return $v->match == "你好"; });
|
||||
//$dispatcher->setRuleFunction(fn ($v) => $v->match == "qwe");
|
||||
ob_start();
|
||||
try {
|
||||
$dispatcher->dispatchEvents();
|
||||
} catch (AnnotationException $e) {
|
||||
}
|
||||
$dispatcher->dispatchEvents();
|
||||
$r = ob_get_clean();
|
||||
echo $r;
|
||||
$this->assertStringContainsString("你好啊", $r);
|
||||
|
||||
Reference in New Issue
Block a user