mirror of
https://github.com/zhamao-robot/zhamao-framework.git
synced 2026-07-03 23:05:37 +08:00
Compare commits
60 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c8938b7a4b | ||
|
|
34db1626a5 | ||
|
|
7f0c97c5b9 | ||
|
|
74050c46e7 | ||
| 3ed1cb665a | |||
|
|
a6f33ba69d | ||
|
|
176b690417 | ||
|
|
e22b1b90ec | ||
|
|
a3c560790c | ||
| 570e2108dd | |||
|
|
59c0d95e5d | ||
|
|
9ceaecdc02 | ||
|
|
19d50898ef | ||
|
|
5b62ca62ae | ||
| fb528d30ce | |||
| 5f2d5ed334 | |||
| c20e459900 | |||
| 09220825cf | |||
|
|
3d4db23d27 | ||
|
|
4496b67dcc | ||
|
|
2a13298384 | ||
|
|
d6ec404d76 | ||
|
|
3235fd4dc1 | ||
|
|
293740fee2 | ||
|
|
d3c420ec84 | ||
|
|
85ef09d43c | ||
|
|
e4561d69c4 | ||
|
|
6b4d206099 | ||
|
|
50843edf6a | ||
|
|
3a1686f8da | ||
|
|
66dd91bb97 | ||
|
|
e020e5d593 | ||
|
|
3d62663281 | ||
|
|
8d9485c02e | ||
|
|
9fb45dd683 | ||
|
|
8a4924dba9 | ||
|
|
beaf7be606 | ||
|
|
7dbd21bdf4 | ||
|
|
9ce3056203 | ||
|
|
880b4e847c | ||
|
|
71e83d5bc8 | ||
|
|
432fd92cca | ||
|
|
907a9cea25 | ||
| 07391810ff | |||
| 5063421364 | |||
| 2b4d308783 | |||
| e2f49968b3 | |||
| 229778ebf9 | |||
|
|
d2c0972c93 | ||
|
|
d300b6e518 | ||
|
|
56cb7b2223 | ||
|
|
2fc42d5d60 | ||
|
|
999e90f709 | ||
|
|
45c6cd7d2a | ||
|
|
5aa0858021 | ||
|
|
e981da3932 | ||
|
|
2685be7306 | ||
|
|
a13c4628f5 | ||
|
|
b36417454c | ||
|
|
a6f5952dee |
38
.github/ISSUE_TEMPLATE/1_Bug_report.yaml
vendored
Normal file
38
.github/ISSUE_TEMPLATE/1_Bug_report.yaml
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
name: 🐛 漏洞(BUG)报告
|
||||
description: ⚠️ 请不要直接在此提交安全漏洞
|
||||
labels: bug
|
||||
|
||||
body:
|
||||
- type: input
|
||||
id: affected-versions
|
||||
attributes:
|
||||
label: 受影响版本
|
||||
placeholder: x.y.z
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: 描述
|
||||
description: 请详细地描述您的问题
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: reproduce-steps
|
||||
attributes:
|
||||
label: 复现步骤
|
||||
description: |
|
||||
请尽可能地提供可以复现此步骤的漏洞。
|
||||
如果步骤过长或难以描述,您可以自行建立一个用于复现漏洞的仓库。
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: possible-solution
|
||||
attributes:
|
||||
label: 解决方案
|
||||
description: 如果您对这个漏洞的成因或修复有任何意见的话,请在此提出
|
||||
- type: textarea
|
||||
id: additional-context
|
||||
attributes:
|
||||
label: 附加信息
|
||||
description: 其他可能有帮助的信息,如日志、截图等
|
||||
19
.github/ISSUE_TEMPLATE/2_Feature_request.yaml
vendored
Normal file
19
.github/ISSUE_TEMPLATE/2_Feature_request.yaml
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
name: 🚀 功能建议
|
||||
description: 新功能、改进的意见、草案
|
||||
labels: enhancement
|
||||
|
||||
body:
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: 描述
|
||||
description: 请提供简洁清楚的描述
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: example
|
||||
attributes:
|
||||
label: 例子
|
||||
description: |
|
||||
一个简单的例子,展示该功能将如何被使用(包括代码、配置文件等)
|
||||
如果这是针对已有功能的改进,请展示改进前后使用方式(或效能)的对比
|
||||
33
.github/workflows/main.yml
vendored
Normal file
33
.github/workflows/main.yml
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
name: Docs Build
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Deploy docs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout master
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Deploy docs to GitHub Pages
|
||||
uses: mhausenblas/mkdocs-deploy-gh-pages@master
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
CUSTOM_DOMAIN: framework.zhamao.me
|
||||
CONFIG_FILE: mkdocs.yml
|
||||
EXTRA_PACKAGES: build-base
|
||||
- name: Copy deployment to current folder
|
||||
run: |
|
||||
cp -r "${GITHUB_WORKSPACE}/site" "./deploy"
|
||||
- name: Deploy to Zhamao Server
|
||||
uses: easingthemes/ssh-deploy@main
|
||||
env:
|
||||
SSH_PRIVATE_KEY: ${{ secrets.ZHAMAO_XIN_PRIVATE_KEY }}
|
||||
ARGS: "-rltgoDzvO --delete"
|
||||
SOURCE: "deploy/"
|
||||
REMOTE_HOST: ${{ secrets.ZHAMAO_XIN_HOST }}
|
||||
REMOTE_USER: ${{ secrets.ZHAMAO_XIN_USER }}
|
||||
TARGET: ${{ secrets.ZHAMAO_XIN_TARGET }}
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -12,6 +12,7 @@ composer.lock
|
||||
.daemon_pid
|
||||
/runtime/
|
||||
/tmp/
|
||||
/temp/
|
||||
/ext/go-cqhttp/data/
|
||||
/ext/go-cqhttp/logs/
|
||||
/ext/go-cqhttp/config.hjson
|
||||
|
||||
17
.run/Reload Zhamao Server.run.xml
Normal file
17
.run/Reload Zhamao Server.run.xml
Normal file
@@ -0,0 +1,17 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Reload Zhamao Server" type="ShConfigurationType">
|
||||
<option name="SCRIPT_TEXT" value="./zhamao server:reload && exit" />
|
||||
<option name="INDEPENDENT_SCRIPT_PATH" value="true" />
|
||||
<option name="SCRIPT_PATH" value="" />
|
||||
<option name="SCRIPT_OPTIONS" value="" />
|
||||
<option name="INDEPENDENT_SCRIPT_WORKING_DIRECTORY" value="true" />
|
||||
<option name="SCRIPT_WORKING_DIRECTORY" value="$PROJECT_DIR$" />
|
||||
<option name="INDEPENDENT_INTERPRETER_PATH" value="true" />
|
||||
<option name="INTERPRETER_PATH" value="" />
|
||||
<option name="INTERPRETER_OPTIONS" value="" />
|
||||
<option name="EXECUTE_IN_TERMINAL" value="true" />
|
||||
<option name="EXECUTE_SCRIPT_FILE" value="false" />
|
||||
<envs />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
17
.run/Run Zhamao Server.run.xml
Normal file
17
.run/Run Zhamao Server.run.xml
Normal file
@@ -0,0 +1,17 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Run Zhamao Server" type="ShConfigurationType">
|
||||
<option name="SCRIPT_TEXT" value="./zhamao server" />
|
||||
<option name="INDEPENDENT_SCRIPT_PATH" value="true" />
|
||||
<option name="SCRIPT_PATH" value="./zhamao" />
|
||||
<option name="SCRIPT_OPTIONS" value="server" />
|
||||
<option name="INDEPENDENT_SCRIPT_WORKING_DIRECTORY" value="true" />
|
||||
<option name="SCRIPT_WORKING_DIRECTORY" value="$PROJECT_DIR$" />
|
||||
<option name="INDEPENDENT_INTERPRETER_PATH" value="true" />
|
||||
<option name="INTERPRETER_PATH" value="" />
|
||||
<option name="INTERPRETER_OPTIONS" value="" />
|
||||
<option name="EXECUTE_IN_TERMINAL" value="true" />
|
||||
<option name="EXECUTE_SCRIPT_FILE" value="false" />
|
||||
<envs />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
17
.run/Stop Zhamao Server.run.xml
Normal file
17
.run/Stop Zhamao Server.run.xml
Normal file
@@ -0,0 +1,17 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Stop Zhamao Server" type="ShConfigurationType">
|
||||
<option name="SCRIPT_TEXT" value="./zhamao server:stop && exit" />
|
||||
<option name="INDEPENDENT_SCRIPT_PATH" value="true" />
|
||||
<option name="SCRIPT_PATH" value="" />
|
||||
<option name="SCRIPT_OPTIONS" value="" />
|
||||
<option name="INDEPENDENT_SCRIPT_WORKING_DIRECTORY" value="true" />
|
||||
<option name="SCRIPT_WORKING_DIRECTORY" value="$PROJECT_DIR$" />
|
||||
<option name="INDEPENDENT_INTERPRETER_PATH" value="true" />
|
||||
<option name="INTERPRETER_PATH" value="" />
|
||||
<option name="INTERPRETER_OPTIONS" value="" />
|
||||
<option name="EXECUTE_IN_TERMINAL" value="true" />
|
||||
<option name="EXECUTE_SCRIPT_FILE" value="false" />
|
||||
<envs />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
19
README.md
19
README.md
@@ -7,6 +7,7 @@
|
||||
[](https://github.com/zhamao-robot/zhamao-framework/blob/master/LICENSE)
|
||||
[](https://packagist.org/packages/zhamao/framework)
|
||||
[](https://github.com/howmanybots/onebot)
|
||||

|
||||
|
||||
[](https://github.com/zhamao-robot/zhamao-framework/search?q=AnnotationBase)
|
||||
[](https://github.com/zhamao-robot/zhamao-framework/search?q=TODO)
|
||||
@@ -42,19 +43,19 @@ public function index() {
|
||||
## 开始
|
||||
如果你是初学者,可以直接使用以下脚本部署 PHP 环境和安装框架的脚手架:
|
||||
```bash
|
||||
# 新建一个自己喜欢名字的文件夹,运行一键安装脚本
|
||||
# 新建一个自己喜欢名字的文件夹,运行一键安装脚本 (仅限 x86_64 和 aarch64 平台)
|
||||
mkdir zhamao-app/
|
||||
cd zhamao-app/
|
||||
bash -c "$(curl -fsSL https://api.zhamao.xin/go.sh)"
|
||||
|
||||
# 启动
|
||||
runtime/php vendor/bin/start server
|
||||
vendor/bin/start server
|
||||
```
|
||||
|
||||
## 文档(v2 版本)
|
||||
查看文档(国内自建):<https://docs-v2.zhamao.xin/>
|
||||
查看文档(国内自建):<https://framework.zhamao.xin/>
|
||||
|
||||
备用链接(国外托管):<https://docs-v2.zhamao.me/>
|
||||
备用链接(国外托管):<https://framework.zhamao.me/>
|
||||
|
||||
自行构建文档:`mkdocs build -d distribute`
|
||||
|
||||
@@ -97,7 +98,7 @@ runtime/php vendor/bin/start server
|
||||
## 关于
|
||||
框架和 SDK 是 炸毛机器人 项目的核心框架开源部分。炸毛机器人是作者写的一个高性能机器人,曾获全国计算机设计大赛一等奖。
|
||||
|
||||
作者的炸毛机器人已从2018年初起稳定运行了**三年**,并且持续迭代。
|
||||
作者的炸毛机器人已从2018年初起稳定运行了**四年**,并且持续迭代。
|
||||
|
||||
欢迎随时在 HTTP-API 插件群里提问,当然更好的话可以加作者 QQ([627577391](http://wpa.qq.com/msgrd?v=3&uin=627577391&site=qq&menu=yes))或提交 Issue 进行疑难解答。
|
||||
|
||||
@@ -109,4 +110,10 @@ runtime/php vendor/bin/start server
|
||||
|
||||
在贡献代码时,请保管好自己的全局配置文件中的敏感信息,请勿将带有个人信息的配置文件上传 GitHub 等网站。
|
||||
|
||||

|
||||
感谢 JetBrains 为此开源项目提供 PhpStorm 开发工具支持:
|
||||
|
||||
<img src="https://resources.jetbrains.com/storage/products/company/brand/logos/PhpStorm.svg" width="300">
|
||||
|
||||
感谢 [php-libonebot](https://github.com/botuniverse/php-libonebot) 开发者 @sunxyw 中为项目开发规范化提出的一些建议。
|
||||
|
||||
<!--  -->
|
||||
|
||||
1
bin/phpunit-swoole
Executable file → Normal file
1
bin/phpunit-swoole
Executable file → Normal file
@@ -59,6 +59,7 @@ require PHPUNIT_COMPOSER_INSTALL;
|
||||
$starttime = microtime(true);
|
||||
go(function () {
|
||||
try {
|
||||
require_once __DIR__.'/../test/bootstrap.php';
|
||||
PHPUnit\TextUI\Command::main(false);
|
||||
} catch (Exception $e) {
|
||||
echo $e->getMessage() . PHP_EOL;
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
"jelix/version": "^2.0",
|
||||
"league/climate": "^3.6",
|
||||
"psy/psysh": "@stable",
|
||||
"doctrine/orm": "^2.9"
|
||||
"doctrine/dbal": "^2.13.1"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-ctype": "Use C/C++ extension instead of polyfill will be more efficient",
|
||||
|
||||
@@ -34,10 +34,15 @@ $config['swoole'] = [
|
||||
//'task_enable_coroutine' => true
|
||||
];
|
||||
|
||||
/** 一些框架与Swoole运行时设置的调整 */
|
||||
|
||||
/** 一些框架与框架运行时设置的调整 */
|
||||
$config['runtime'] = [
|
||||
'swoole_coroutine_hook_flags' => SWOOLE_HOOK_ALL & (~SWOOLE_HOOK_CURL),
|
||||
'swoole_server_mode' => SWOOLE_PROCESS
|
||||
'swoole_server_mode' => SWOOLE_PROCESS,
|
||||
'middleware_error_policy' => 1,
|
||||
'reload_delay_time' => 800,
|
||||
'global_middleware_binding' => [],
|
||||
'save_console_log_file' => false, // 改为目标路径,则将 Console 输出的日志保存到文件
|
||||
];
|
||||
|
||||
/** 轻量字符串缓存,默认开启 */
|
||||
@@ -55,25 +60,10 @@ $config['worker_cache'] = [
|
||||
'transaction_timeout' => 30000
|
||||
];
|
||||
|
||||
/** @deprecated 放弃使用,旧版数据库,请使用 mysql_config 和 doctrine/dbal 搭配使用 */
|
||||
$config['sql_config'] = [
|
||||
'sql_host' => '',
|
||||
'sql_port' => 3306,
|
||||
'sql_username' => 'name',
|
||||
'sql_database' => 'db_name',
|
||||
'sql_password' => '',
|
||||
'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
|
||||
];
|
||||
|
||||
/** MySQL数据库连接信息,host留空则启动时不创建sql连接池 */
|
||||
$config['mysql_config'] = [
|
||||
'host' => '',
|
||||
'port' => 33306,
|
||||
'port' => 3306,
|
||||
'unix_socket' => null,
|
||||
'username' => 'root',
|
||||
'password' => '123456',
|
||||
@@ -133,7 +123,9 @@ $config['static_file_server'] = [
|
||||
$config['onebot'] = [
|
||||
'status' => true,
|
||||
'single_bot_mode' => false,
|
||||
'message_level' => 99999
|
||||
'message_level' => 99,
|
||||
'message_convert_string' => true,
|
||||
'message_command_policy' => 'interrupt',
|
||||
];
|
||||
|
||||
/** 一个远程简易终端,使用nc直接连接即可,但是不建议开放host为0.0.0.0(远程连接) */
|
||||
|
||||
@@ -42,6 +42,21 @@ MessageUtil::containsImage("[CQ:image,file=a.jpg,url=http://xxx]"); // true
|
||||
MessageUtil::containsImage("[CQ:face,id=140] 咦,这是一条带表情的消息"); // false
|
||||
```
|
||||
|
||||
### isAtMe()
|
||||
|
||||
检查消息中是否含有@bot的消息。
|
||||
|
||||
定义:`isAtMe($msg, $me_id)`
|
||||
|
||||
参数 `$me_id` 为Bot的QQ号。
|
||||
|
||||
返回:`bool`,true 就是有,false 就没有。
|
||||
|
||||
```php
|
||||
MessageUtil::isAtMe("[CQ:at,qq=123456]炸毛你好","123456"); // true
|
||||
MessageUtil::isAtMe("[CQ:at,qq=123456789]另一个朋友你好","123456"); // false
|
||||
```
|
||||
|
||||
### getImageCQFromLocal()
|
||||
|
||||
通过文件路径获取图片的发送 CQ 码。
|
||||
@@ -113,3 +128,52 @@ public function onStart() {
|
||||
( 其实还是很聪明的!
|
||||
</chat-box>
|
||||
|
||||
### strToArray()
|
||||
|
||||
将 `string` 类型的消息文本转换为 `array` 格式。
|
||||
|
||||
定义:`strToArray($msg, bool $ignore_space = true, bool $trim_text = false)`
|
||||
|
||||
参数 `$msg` 为带 OB/CQ 码的字符串消息,如 `你好啊,[CQ:at,qq=123]`。
|
||||
|
||||
参数 `$ignore_space` 在 `false` 时,转换的数组内会包含空 `text` 段。
|
||||
|
||||
参数 `$trim_text` 为 `true` 时,会自动去除 `text` 段消息头尾的换行符和空格。
|
||||
|
||||
这个命令转换的数组格式符合 OneBot 11/12 标准,但细节上可能会与不同 OneBot 实现有所差异。
|
||||
|
||||
```php
|
||||
$str = "你好啊,[CQ:at,qq=123]";
|
||||
$arr = \ZM\Utils\MessageUtil::strToArray($str);
|
||||
```
|
||||
|
||||
转换结果参考如下:
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"type": "text",
|
||||
"data": {
|
||||
"text": "你好啊,"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "at",
|
||||
"data": {
|
||||
"qq": "123"
|
||||
}
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### arrayToStr()
|
||||
|
||||
将 `array` 格式的消息内容转换为字符串 + CQ 码的形式。
|
||||
|
||||
定义:`arrayToStr(array $array)`
|
||||
|
||||
```php
|
||||
// 我们使用上边的 $arr 作为传入值。
|
||||
$new_str = \ZM\Utils\MessageUtil::arrayToStr($arr);
|
||||
// 结果:"你好啊,[CQ:at,qq=123]"
|
||||
```
|
||||
9
docs/component/bot/robot-api-12.md
Normal file
9
docs/component/bot/robot-api-12.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# 机器人 API(OneBotV12)(待发布)
|
||||
|
||||
!!! tip "提示"
|
||||
|
||||
目前由于 OneBot 12 标准还没有定稿,处于草案阶段,故框架暂不更新。
|
||||
|
||||
在未来升级到 OneBot 12 标准后,框架会提供转换及兼容措施以及 12 版本的 API 方法。
|
||||
|
||||
见 [机器人动作(OneBot 11)](../robot-api)。
|
||||
@@ -1,26 +1,29 @@
|
||||
# 机器人 API(ZMRobot)
|
||||
# 机器人 API(OneBotV11)
|
||||
|
||||
ZMRobot 类是封装好的 OneBot 标准的 API 接口调用类,可以在机器人连接后通过连接或者机器人 QQ 号获取对象并调用接口(如发送群消息、获取群列表等操作)。
|
||||
OneBotV11 类是封装好的 OneBot 标准的 API 接口调用类,可以在机器人连接后通过连接或者机器人 QQ 号获取对象并调用接口(如发送群消息、获取群列表等操作)。
|
||||
|
||||
| 属性项 | 属性值 | 备注 |
|
||||
| -------- | ---------------- | ------------------------------ |
|
||||
| 名称 | ZMRobot | |
|
||||
| 类型 | 实例化类 | `$r = new ZMRobot($conn)` |
|
||||
| 命名空间 | `ZM\API\ZMRobot` | 使用前先 `use ZM\API\ZMRobot;` |
|
||||
| 属性项 | 属性值 | 备注 |
|
||||
| -------- | ------------------ | -------------------------------- |
|
||||
| 名称 | OneBotV11 | |
|
||||
| 类型 | 实例化类 | `$r = new OneBotV11($conn)` |
|
||||
| 命名空间 | `ZM\API\OneBotV11` | 使用前先 `use ZM\API\OneBotV11;` |
|
||||
| 别名 | `ZM\API\ZMRobot` | 此类目前是 `extends OneBotV11` |
|
||||
|
||||
> 你也可以继续使用 2.5 版本之前的别名类 `ZMRobot`,但未来框架将会优先兼容 OneBot V12 版本的协议,可能会造成更新问题,建议切换为 OneBotV11 类。
|
||||
|
||||
## 属性
|
||||
|
||||
对象属性方法是对 API 的调整,例如是否以 `_async`、`_rate_limited` 后缀发送 API、设置协程返回还是异步返回结果等。
|
||||
|
||||
### ZMRobot::API_NORMAL
|
||||
### OneBotV11::API_NORMAL
|
||||
|
||||
以默认(无后缀)方式请求 API。
|
||||
|
||||
### ZMRobot::API_ASYNC
|
||||
### OneBotV11::API_ASYNC
|
||||
|
||||
以后缀 `_async` 方式异步请求 API。
|
||||
|
||||
### ZMRobot::API_RATE_LIMITED
|
||||
### OneBotV11::API_RATE_LIMITED
|
||||
|
||||
以后缀 `_rate_limited` 方式请求 API。
|
||||
|
||||
@@ -30,7 +33,7 @@ ZMRobot 类是封装好的 OneBot 标准的 API 接口调用类,可以在机
|
||||
|
||||
设置后缀。目前支持 `_async`、`_rate_limited`。
|
||||
|
||||
- **prefix**: `int` `默认:API_NORMAL`,可选 `ZMRobot::API_NORMAL`、`ZMRobot::API_ASYNC`、`ZMRobot::API_RATE_LIMITED`
|
||||
- **prefix**: `int` `默认:API_NORMAL`,可选 `OneBotV11::API_NORMAL`、`OneBotV11::API_ASYNC`、`OneBotV11::API_RATE_LIMITED`
|
||||
|
||||
设置后缀后,请求的 API 会发生变化。例如发送私聊消息:`sendPrivateMsg()`,请求的 API 为 `send_private_msg_async`,详见 [OneBot 文档](https://github.com/howmanybots/onebot/blob/master/v11/specs/api/README.md)。
|
||||
|
||||
@@ -43,22 +46,22 @@ ZMRobot 类是封装好的 OneBot 标准的 API 接口调用类,可以在机
|
||||
获取当前对象的机器人 QQ 或 OneBot 实例的 ID。
|
||||
|
||||
```php
|
||||
$bot = ZMRobot::get(123456);
|
||||
$bot = OneBotV11::get(123456);
|
||||
echo $bot->getSelfId(); //123456
|
||||
```
|
||||
|
||||
### ZMRobot::get()
|
||||
### OneBotV11::get()
|
||||
|
||||
静态方法,用来通过机器人 QQ 或 OneBot 实例的 ID 获取 ZMRobot 对象。
|
||||
静态方法,用来通过机器人 QQ 或 OneBot 实例的 ID 获取 OneBotV11对象。
|
||||
|
||||
参数:`$robot_id`,必填。
|
||||
|
||||
```php
|
||||
$r = ZMRobot::get(123456);
|
||||
$r = OneBotV11::get(123456);
|
||||
$r->sendPrivateMsg(55555, "hello");
|
||||
```
|
||||
|
||||
### ZMRobot::getRandom()
|
||||
### OneBotV11::getRandom()
|
||||
|
||||
静态方法,随机获取一个连接到框架的机器人(多个机器人实例连接到框架时适用)。
|
||||
|
||||
@@ -66,21 +69,21 @@ $r->sendPrivateMsg(55555, "hello");
|
||||
|
||||
```php
|
||||
try {
|
||||
$bot = ZMRobot::getRandom();
|
||||
$bot = OneBotV11::getRandom();
|
||||
$bot->sendPrivateMsg(55555, "foo");
|
||||
} catch (\ZM\Exception\RobotNotFoundException $e) {
|
||||
echo "还没有机器人连接到框架!\n";
|
||||
}
|
||||
```
|
||||
|
||||
### ZMRobot::getAllRobot()
|
||||
### OneBotV11::getAllRobot()
|
||||
|
||||
获取所有连接到框架的机器人的 ZMRobot 对象。
|
||||
获取所有连接到框架的机器人的 OneBotV11 对象。
|
||||
|
||||
返回值:`ZMRobot[]`。
|
||||
返回值:`OneBotV11[]`。
|
||||
|
||||
```php
|
||||
$all = ZMRobot::getAllRobot();
|
||||
$all = OneBotV11::getAllRobot();
|
||||
foreach($all as $v) {
|
||||
$v->sendPrivateMsg(55555, "机器人轮流给一个人发消息啦!");
|
||||
}
|
||||
@@ -95,7 +98,7 @@ foreach($all as $v) {
|
||||
```php
|
||||
//从上下文获取 Websocket 连接对象
|
||||
$conn = ctx()->getConnection();
|
||||
$bot = new ZMRobot($conn);
|
||||
$bot = new OneBotV11($conn);
|
||||
```
|
||||
|
||||
## 返回结果处理
|
||||
@@ -103,7 +106,7 @@ $bot = new ZMRobot($conn);
|
||||
因为框架的机器人是兼容 OneBot 标准的(原 CQHTTP),所以每次接收发送 API 请求的结果都是大体一样的结构。我们以 `sendPrivateMsg()` 为例,因为发送出去的每一条消息都会在 OneBot 实例(如 CQHTTP 插件、go-cqhttp 等)中对应一个消息 ID,以供我们核查消息和后续撤回等操作需要。
|
||||
|
||||
```php
|
||||
$bot = ZMRobot::get("123456"); // 机器人QQ号
|
||||
$bot = OneBotV11::get("123456"); // 机器人QQ号
|
||||
$obj = $bot->sendGroupMsg("234567", "你好");
|
||||
echo json_encode($obj, 128|256);
|
||||
```
|
||||
@@ -163,7 +166,7 @@ vardump($result["retcode"]); //如果成功撤回,输出 int(0)
|
||||
=== "代码"
|
||||
|
||||
```php
|
||||
$bot = ZMRobot::get(123456); // 123456是你的机器人QQ
|
||||
$bot = OneBotV11::get(123456); // 123456是你的机器人QQ
|
||||
$bot->sendPrivateMsg("627577391", "你好啊!你好你好!");
|
||||
```
|
||||
|
||||
@@ -766,6 +769,21 @@ vardump($result["retcode"]); //如果成功撤回,输出 int(0)
|
||||
|
||||
响应数据:无
|
||||
|
||||
### getExtendedAPI()
|
||||
|
||||
用来调用 OneBot 标准之外扩展出来的自定义 API。与下方 `callExtendedAPI` 不同的是,为了方便用户使用,炸毛框架内置了热门使用并且相对稳定的机器人客户端的专有 API。
|
||||
|
||||
目前内置了 `go-cqhttp` 频道相关的扩充 API。
|
||||
|
||||
使用示例:`getExtendedAPI('go-cqhttp')->getGuildList()`
|
||||
使用示例2:`getExtendedAPI()->sendGuildChannelMsg($guild_id, $channel_id, '频道的消息')`
|
||||
|
||||
唯一一个参数做保留,用于选择不同客户端,目前仅支持 `go-cqhttp`,所以缺省也默认为 `go-cqhttp`。
|
||||
|
||||
!!! warning "注意"
|
||||
|
||||
由于不同版本的扩展 API 变化可能会很大,改动较多,炸毛框架不会将对应扩展方法写入文档,具体调用情况可根据 IDE 自动补全中的文档或对应类的注释查看。
|
||||
|
||||
### callExtendedAPI() (扩充 API)
|
||||
|
||||
用来调用 OneBot 标准之外扩展出来的自定义 API。
|
||||
|
||||
@@ -124,7 +124,7 @@ public function ping() {
|
||||
|
||||
## getRobot() - 获取机器人 API 对象
|
||||
|
||||
返回当前上下文关联的机器人 API 调用对象 [ZMRobot](bot/robot-api.md)。
|
||||
返回当前上下文关联的机器人 API 调用对象 [ZMRobot](../bot/robot-api.md)。
|
||||
|
||||
可以使用的事件:所有 HTTP API 发来的事件:`@CQCommand()`,`@CQMessage()` 等。
|
||||
|
||||
104
docs/component/common/event-tracer.md
Normal file
104
docs/component/common/event-tracer.md
Normal file
@@ -0,0 +1,104 @@
|
||||
# 事件跟踪器及调试
|
||||
|
||||
众所周知,炸毛框架中的事件由内置的事件分发器(EventDispatcher)负责分发,但调试事件分发在之前的版本比较困难,例如不能获取到事件如何被调用,以及事件如何被捕获。
|
||||
|
||||
EventTracer 的作用是记录事件的调用顺序,以便于调试。
|
||||
|
||||
命名空间使用指南:`use ZM\Event\EventTracer;`
|
||||
|
||||
## EventTracer::getCurrentEvent() - 获取当前注解事件对象
|
||||
|
||||
```php
|
||||
/**
|
||||
* @OnStart()
|
||||
*/
|
||||
public function onStart() {
|
||||
zm_dump(EventTracer::getCurrentEvent());
|
||||
}
|
||||
/*
|
||||
^ ZM\Annotation\Swoole\OnStart^ {#192
|
||||
+worker_id: 0
|
||||
+method: "onStart"
|
||||
+class: "Module\Example\Hello"
|
||||
}
|
||||
*/
|
||||
```
|
||||
|
||||
这里这个方法必须在注解事件内执行,如果在注解事件外执行,将会返回 `null`。
|
||||
|
||||
## EventTracer::getCurrentEventMiddlewares() - 获取当前注解事件的中间件们
|
||||
|
||||
```php
|
||||
/**
|
||||
* @OnStart()
|
||||
* @Middleware("timer")
|
||||
*/
|
||||
public function onStart() {
|
||||
zm_dump(EventTracer::getCurrentEventMiddlewares());
|
||||
}
|
||||
/*
|
||||
^ array:1 [
|
||||
0 => ZM\Annotation\Http\Middleware^ {#194
|
||||
+middleware: "timer"
|
||||
+params: []
|
||||
+method: "onStart"
|
||||
+class: "Module\Example\Hello"
|
||||
}
|
||||
]
|
||||
*/
|
||||
```
|
||||
|
||||
返回值为当前注解事件的中间件们,如果没有注解中间件,返回 `[]`。
|
||||
|
||||
## EventTracer::getEventTraceList() - 获取注解事件的列表
|
||||
|
||||
此处返回的是 `getCurrentEvent()` 相同的对象,但是返回的是一个数组,数组中的元素是注解事件。
|
||||
|
||||
```php
|
||||
/**
|
||||
* 一个简单随机数的功能demo
|
||||
* 问法1:随机数 1 20
|
||||
* 问法2:从1到20的随机数
|
||||
* @CQCommand("随机数")
|
||||
* @Middleware("timer")
|
||||
* @CQCommand(pattern="*从*到*的随机数")
|
||||
* @return string
|
||||
*/
|
||||
public function randNum() {
|
||||
// 此处为随机数代码
|
||||
zm_dump(EventTracer::getEventTraceList());
|
||||
return "随机数:" . rand(1, 20);
|
||||
}
|
||||
|
||||
/*
|
||||
^ array:2 [
|
||||
0 => ZM\Annotation\CQ\CQCommand^ {#193
|
||||
+match: ""
|
||||
+pattern: "*从*到*的随机数"
|
||||
+regex: ""
|
||||
+start_with: ""
|
||||
+end_with: ""
|
||||
+keyword: ""
|
||||
+alias: []
|
||||
+message_type: ""
|
||||
+user_id: 0
|
||||
+group_id: 0
|
||||
+discuss_id: 0
|
||||
+level: 20
|
||||
+method: "randNum"
|
||||
+class: "Module\Example\Hello"
|
||||
}
|
||||
1 => ZM\Annotation\Swoole\OnMessageEvent^ {#165
|
||||
+connect_type: "default"
|
||||
+rule: "connectIsQQ()"
|
||||
+level: 99
|
||||
+method: "handleByEvent"
|
||||
+class: "ZM\Module\QQBot"
|
||||
}
|
||||
]
|
||||
*/
|
||||
```
|
||||
|
||||
## EventDispatcher::enableEventTrace() - 启用事件跟踪器
|
||||
|
||||
还没写完,不着急。
|
||||
@@ -14,7 +14,6 @@ src/
|
||||
│ └── zm.json
|
||||
└── Middleware/
|
||||
└── TimerMiddleware.php
|
||||
|
||||
```
|
||||
|
||||
我们在 Example 目录下创建一个 `zm.json` 的文件,编写配置,即代表 `src/Module/Example/` 文件夹及里面的用户模块源码为一个模块包,也就可以被框架识别并打包处理。
|
||||
@@ -100,14 +99,144 @@ src/
|
||||
}
|
||||
```
|
||||
|
||||
#### - global-config-override
|
||||
|
||||
- 类型:string | false。
|
||||
- 含义:解包时是否需要手动编辑全局配置(`global.php`)。
|
||||
|
||||
这里如果写 string 类型的,那么就是相当于在解包时会提示此处的内容,内容推荐填写要求解包模块用户需要编辑的项目,比如 「请将 static_file_server 的 status 改为 true,以便使用静态文本功能」。
|
||||
|
||||
如果是 false,那么和不指定此参数效果是一样的,无需用户修改 global.php。
|
||||
|
||||
??? note "点我查看编写实例:"
|
||||
```json
|
||||
{
|
||||
"name": "foo",
|
||||
"description": "这个是一个示例模块打包教程",
|
||||
"global-config-override": "请将 static_file_server 的 status 改为 true"
|
||||
}
|
||||
```
|
||||
|
||||
#### - allow-hotload
|
||||
|
||||
- 类型:bool。
|
||||
- 含义:是否允许用户无需解压直接加载模块包文件(phar)。
|
||||
|
||||
当此项为 true 时,可以将模块包直接放入 `zm_data/modules` 文件夹下,然后将 `global.php` 中的 `module_loader` 项中的 `enable_hotload` 改为 true,启动框架即可加载。
|
||||
|
||||
??? note "点我查看编写实例:"
|
||||
```json
|
||||
{
|
||||
"name": "foo",
|
||||
"description": "这个是一个示例模块打包教程",
|
||||
"allow-hotload": true
|
||||
}
|
||||
```
|
||||
|
||||
!!! warning "注意"
|
||||
如果使用允许热加载,那么模块包中的配置最好不要有 `global-config-override` 和 `light-cache-store`,以此来达到最正确的效果,一般热加载更适合 Library(库)类型的模块。
|
||||
|
||||
#### - zm-data-store
|
||||
|
||||
- 类型:array of string(例如 `["foo","bar"]`)。
|
||||
- 含义:打包时要添加到模块包内的 `zm_data` 目录下的子目录或文件。
|
||||
|
||||
其中项目必须是相对路径,不能是绝对路径,且必须是在配置项 `zm_data` 指定的目录(默认会在框架项目的根目录下的 `zm_data/` 目录。
|
||||
|
||||
我们假设要打包一个 `{zm_data 目录}/config/` 目录及其目录下的文件,和一个 `main.png` 文件,下方是实例。
|
||||
|
||||
??? note "点我查看编写实例:"
|
||||
```json
|
||||
{
|
||||
"name": "foo",
|
||||
"description": "这个是一个示例模块打包教程",
|
||||
"zm-data-store": [
|
||||
"config/",
|
||||
"main.png"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
在打包时框架会自动添加这些文件到 phar 插件包内,到解包时,会自动将这些文件释放到对应框架的 `zm_data` 目录下。
|
||||
|
||||
## 打包模块命令
|
||||
|
||||
编写配置文件 `zm.json` 后,就可以被框架正常识别为模块形式,你也可以使用对无需打包的模块进行配置以进行分类管理。
|
||||
|
||||
### module:list
|
||||
|
||||
使用 list 命令可以列出炸毛框架检测到配置文件或打包好的模块。
|
||||
|
||||
```
|
||||
$ ./zhamao module:list
|
||||
[foo]
|
||||
类型: source
|
||||
版本: 1.0.0
|
||||
描述: 示例模块打包文件
|
||||
目录: src/Module/Example
|
||||
没有发现已打包且装载的模块!
|
||||
```
|
||||
|
||||
其中 `[ ]` 内为识别出来的模块名称,由上方用户编写的 `zm.json` 定义,类型为 `source` 是源码形式,也就是指定了 `zm.json` 形式的模块,目录为模块所在子目录。
|
||||
|
||||
我们假设打包上方定义的 `foo` 模块,使用下方命令 `module:pack` 即可。
|
||||
|
||||
### module:pack
|
||||
|
||||
使用 pack 命令可以将配置好的模块打包为 `xxx.phar` 文件并转移或发布给他人。
|
||||
|
||||
我们假设打包模块脚手架的默认模块 `src/Module/Example` 下面的模块源码和附带一个 `zm_data` 目录下的文件(我们就随便打包一下 Swoole 的输出日志吧)。`zm.json` 文件内容如下:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "foo",
|
||||
"description": "示例模块打包文件",
|
||||
"version": "1.0.0",
|
||||
"allow-hotload": true,
|
||||
"zm-data-store": [
|
||||
"crash/swoole_error.log"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
然后输入命令:
|
||||
|
||||
```
|
||||
$ ./zhamao module:pack foo
|
||||
[15:07:11] [I] 模块输出文件:/root/zhamao-framework/zm_data/output/foo_1.0.0.phar
|
||||
[15:07:11] [S] 打包完成!
|
||||
```
|
||||
|
||||
如果提示文件夹不存在,请先手动创建文件夹:`mkdir /path/to/your/zm_data/output`
|
||||
|
||||
在打包后,你将获得一个 `foo_1.0.0.phar` 的文件。
|
||||
|
||||
> 如果你没有在 `zm.json` 中指定 `version`,那么输出的 phar 文件是不会带版本号的。
|
||||
|
||||
打包后的 phar 内将包含:
|
||||
|
||||
- Hello.php
|
||||
- zm.json
|
||||
- crash/swoole_error.log
|
||||
- 必要的框架热加载以及解包需要的配置信息
|
||||
|
||||
|
||||
| 字段名 | 类型 | 含义 |
|
||||
| ---------------------- | ---------------------------------- | ---------------------------------------- |
|
||||
| description | string | 模块的描述 |
|
||||
| version | string | 模块的版本(建议使用 x.y.z 三段式) |
|
||||
| depends | array of string(例如`{"a":"b"}`) | 模块依赖关系声明 |
|
||||
| light-cache-store | string[] | 打包模块时要储存的持久化 LightCache 键名 |
|
||||
| global-config-override | string \| false | 是否需要手动编辑全局配置(`global.php`) |
|
||||
| | | |
|
||||
## 打包命令
|
||||
|
||||
```bash
|
||||
# ./zhamao 和原先的 vendor/bin/start 是完全一致的
|
||||
./zhamao module:pack <module-name>
|
||||
```
|
||||
|
||||
例如,打包上面的名叫 foo 的模块:`./zhamao module:pack foo`。
|
||||
|
||||
打包命令执行后,将会在 `zm_data` 下的 `output` 目录输出一个 phar 文件。如果你指定了 `version` 参数,那么文件名将会是 `${name}_${version}.phar`,如果没有指定版本,那么只会有 `${name}.phar`,同时如果文件已经存在,将覆盖写入。
|
||||
|
||||
## 查看模块信息命令
|
||||
|
||||
```bash
|
||||
./zhamao module:list
|
||||
```
|
||||
|
||||
通过此命令可以查看模块相关的信息,如未打包但已配置的模块信息等。
|
||||
|
||||
|
||||
84
docs/component/module/module-unpack.md
Normal file
84
docs/component/module/module-unpack.md
Normal file
@@ -0,0 +1,84 @@
|
||||
# 模块解包
|
||||
|
||||
从 2.5 版本起,炸毛框架的模块源码支持了打包和分发,分发后必不可少的一步就是将其解包。
|
||||
|
||||
解包过程大致为:
|
||||
|
||||
1. 检查模块的配置文件是否正常。
|
||||
2. 检查模块的依赖问题,如果有依赖但未安装,则抛出异常。
|
||||
3. 检查 LightCache 轻量缓存是否需要写入。
|
||||
4. 检查 `zm_data` 是否有需要存入的数据。
|
||||
5. 合并 `composer.json` 文件。
|
||||
6. 拷贝 `zm_data` 相关的文件。
|
||||
7. 写入 LightCache 相关数据。
|
||||
8. 提示用户手动合并 `global.php` 全局配置文件。
|
||||
9. 拷贝模块 PHP 源文件。
|
||||
|
||||
## 解包命令
|
||||
|
||||
```bash
|
||||
./zhamao module:unpack <module-name>
|
||||
```
|
||||
|
||||
首先将待解包的 phar 文件放入 `zm_data` 目录下的 `modules` 文件夹(如果不存在需要手动创建),如果你手动修改过 `global.php` 下面的 `module_loader.load_path` 项,需要放入对应的目录。
|
||||
|
||||
放入后,结构如下:
|
||||
|
||||
```
|
||||
zm_data/
|
||||
zm_data/modules/
|
||||
zm_data/modules/foo.phar
|
||||
```
|
||||
|
||||
接下来,需要知道模块的名称。当然一般情况下,phar 的名称可以获取到模块的实际名称,如 `foo`,但最好用 `./zhamao module:list` 列出模块的信息来获取真实的模块名称。
|
||||
|
||||
```
|
||||
./zhamao module:list
|
||||
# 下面是输出
|
||||
[foo]
|
||||
类型: 模块包(phar)
|
||||
位置: zm_data/modules/我是假的名字.phar
|
||||
```
|
||||
|
||||
解包过程十分简单,只需要执行一次命令即可。
|
||||
|
||||
```
|
||||
./zhamao module:unpack foo
|
||||
# 下面是输出
|
||||
[10:05:40] [I] Releasing source file: src/Module/Example/Hello.php
|
||||
[10:05:40] [I] Releasing source file: src/Module/Example/zm.json
|
||||
[10:05:40] [S] 解压完成!
|
||||
```
|
||||
|
||||
### 命令参数
|
||||
|
||||
在解包时会遇到各种复杂的情况,如源码文件已存在、数据已存在、依赖问题等,通过增加参数可以控制解包时的行为。
|
||||
|
||||
#### --overwrite-light-cache
|
||||
|
||||
含义:覆盖现有的 LightCache 键值(如果存在的话)。
|
||||
|
||||
#### --overwrite-zm-data
|
||||
|
||||
含义:覆盖现有的 `zm_data` 下的文件(如果存在的话)。
|
||||
|
||||
#### --overwrite-source
|
||||
|
||||
含义:覆盖现有的 PHP 模块源文件(如果存在的话)。
|
||||
|
||||
#### --ignore-depends
|
||||
|
||||
含义:解包时忽略检查依赖。
|
||||
|
||||
### 常见问题
|
||||
|
||||
如果你解包的模块包要求修改 `global.php`,则会出现类似这样的提示:
|
||||
|
||||
```
|
||||
# ./zhamao module:unpack foo
|
||||
[14:47:39] [W] 模块作者要求用户手动修改 global.php 配置文件中的项目:
|
||||
[14:47:39] [W] *请把全局配置文件的light_cache项目中max_strlen选项调整为至少65536
|
||||
请输入修改模式,y(使用vim修改)/e(自行使用其他编辑器修改后确认)/N(默认暂不修改):[y/e/N]
|
||||
```
|
||||
|
||||
一般这种情况,根据第二条提示(第二条提示为打包时填入的 `global-config-override`)。如果输入 y,则会自动执行命令 `vim config/global.php`,如果输入的是 e,则会等待你手动修改完成文件,最后按回车完成修改。默认情况直接回车的话,会跳过此步骤,如果模块要求了修改但跳过修改,安装后可能会有功能缺失等问题。
|
||||
@@ -125,9 +125,9 @@ dump(LightCache::getExpire("test")); // 返回 10
|
||||
```php
|
||||
$s = LightCache::set("test", "hello", 20); //假设这条代码执行时时间戳是 1616838482
|
||||
zm_sleep(10);
|
||||
dump(LightCache::getExpire("test")); // 返回 1616838502
|
||||
dump(LightCache::getExpireTS("test")); // 返回 1616838502
|
||||
zm_sleep(10);
|
||||
dump(LightCache::getExpire("test")); // 返回 null
|
||||
dump(LightCache::getExpireTS("test")); // 返回 null
|
||||
```
|
||||
|
||||
### LightCache::getMemoryUsage()
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
# MySQL 数据库
|
||||
# MySQL 数据库(旧版组件)
|
||||
|
||||
!!! warning "注意"
|
||||
|
||||
此 MySQL 组件为旧版 MySQL 查询器组件,为了统一和提升对未来独立组件的兼容性,现转变为使用 `doctrine/dbal` 和 `doctrine/orm` 库来实现查询器,请转到 [MySQL 查询器]()。
|
||||
|
||||
## 配置
|
||||
|
||||
184
docs/component/store/mysql/common-query.md
Normal file
184
docs/component/store/mysql/common-query.md
Normal file
@@ -0,0 +1,184 @@
|
||||
# 执行 SQL 语句
|
||||
|
||||
在一开始,无论你做什么数据库操作,均需要获取一个 `\ZM\MySQL\MySQLWrapper` 作为你的操作对象。
|
||||
|
||||
```php
|
||||
/** @var \ZM\MySQL\MySQLWrapper $wrapper */
|
||||
$wrapper = \ZM\MySQL\MySQLManager::getWrapper();
|
||||
```
|
||||
|
||||
!!! tip "提示"
|
||||
|
||||
这部分内容部分直接取自 [DBAL - Data Retrieval And Manipulation](https://www.doctrine-project.org/projects/doctrine-dbal/en/2.13/reference/data-retrieval-and-manipulation.html) 原文并直接翻译,如有实际不同请提交 Issue 反馈。
|
||||
|
||||
## 执行预处理 SQL 语句
|
||||
|
||||
预处理查询很巧妙地解决了 SQL 注入问题,并且可以方便地绑定参数进行查询。
|
||||
|
||||
预处理一般是指使用 `?` 占位符或 `:xxx` 命名标签进行参数留空,先处理 SQL 语句再填入数据。
|
||||
|
||||
一般 `?` 具有前后位置性,例如如下的查询:
|
||||
|
||||
```php
|
||||
$sql = "SELECT * FROM users WHERE id = ? AND username = ?";
|
||||
$stmt = $wrapper->getConnection()->prepare($sql);
|
||||
$stmt->bindValue(1, "1");
|
||||
$stmt->bindValue(2, "jack");
|
||||
$resultSet = $stmt->executeQuery();
|
||||
```
|
||||
|
||||
其中 `$resultSet` 与 `Statement` 方法相似,此处的对象可能是 [数据库语句对象](../mysql-statement) 或 数据库结果对象(结果对象与语句对象的 `fetchXXX()` 部分一致)。
|
||||
|
||||
这里也可以使用命名标签,使用标签可以给相同参数处使用同一个标签:
|
||||
|
||||
```php
|
||||
$sql = "SELECT * FROM users WHERE gender = :name OR username = :name";
|
||||
$stmt = $wrapper->getConnection()->prepare($sql);
|
||||
$stmt->bindValue("name", "jack");
|
||||
$resultSet = $stmt->executeQuery();
|
||||
```
|
||||
|
||||
## 执行常规语句
|
||||
|
||||
执行常规语句为 `statement` 方式执行,此方法执行后只返回影响的行数,而不返回结果,适用于 `UPDATE` 等语句。
|
||||
|
||||
```php
|
||||
<?php
|
||||
$count = $wrapper->executeStatement('UPDATE users SET username = ? WHERE id = ?', array('jwage', 1));
|
||||
echo $count; // 1
|
||||
```
|
||||
|
||||
## 执行查询语句
|
||||
|
||||
为给定的 SQL 创建一个准备好的语句并将参数传递给 executeQuery 方法,然后返回结果集。此方法为上述的「预处理查询语句」的简化版,可直接在第二个参数使用 array 插入绑定参数执行。
|
||||
|
||||
```php
|
||||
$resultSet = $wrapper->executeQuery('SELECT * FROM user WHERE username = ?', array('jack'));
|
||||
$user = $resultSet->fetchAssociative();
|
||||
|
||||
/* $user 值
|
||||
array(
|
||||
0 => array(
|
||||
'id' => 1,
|
||||
'username' => 'jack',
|
||||
'gender' => 'man',
|
||||
'update_time' => '2021-10-12'
|
||||
)
|
||||
)
|
||||
*/
|
||||
```
|
||||
|
||||
### fetchAllAssociative()
|
||||
|
||||
执行查询并将所有结果返回一个数组中。
|
||||
|
||||
因此,上面的查询语句还可以直接被简化为一次方法调用:
|
||||
|
||||
```php
|
||||
$resultSet = $wrapper->fetchAllAssociative('SELECT * FROM user WHERE username = ?', array('jack'));
|
||||
// 结果同 executeQuery()->fetchAllAssociative() 中 $user 的值。
|
||||
```
|
||||
|
||||
### fetchAllKeyValue()
|
||||
|
||||
执行查询并将前两列分别作为键和值提取到关联数组中。
|
||||
|
||||
```php
|
||||
$resultSet = $wrapper->fetchAllKeyValue('SELECT username, gender FROM user WHERE username = ?', array('jack'));
|
||||
|
||||
/* $resultSet 值
|
||||
array(
|
||||
'jack' => 'man'
|
||||
)
|
||||
*/
|
||||
```
|
||||
|
||||
### fetchAllAssociativeIndexed()
|
||||
|
||||
执行查询并将数据作为关联数组获取,其中键代表第一列,值是其余列及其值的关联数组。
|
||||
|
||||
```php
|
||||
$users = $wrapper->fetchAllAssociativeIndexed('SELECT id, username, gender FROM users');
|
||||
|
||||
/*
|
||||
array(
|
||||
1 => array(
|
||||
'username' => 'jack',
|
||||
'gender' => 'man',
|
||||
'update_time' => '2021-10-12'
|
||||
)
|
||||
)
|
||||
*/
|
||||
```
|
||||
|
||||
### fetchNumeric()
|
||||
|
||||
查询并返回第一行数据,形式以数字索引方式返回每一列。
|
||||
|
||||
```php
|
||||
$user = $wrapper->fetchNumeric('SELECT * FROM users WHERE username = ?', array('jack'));
|
||||
|
||||
/*
|
||||
array(
|
||||
0 => 'jwage',
|
||||
1 => 'man',
|
||||
2 => '2021-10-12'
|
||||
)
|
||||
*/
|
||||
```
|
||||
|
||||
### fetchOne()
|
||||
|
||||
仅返回查询结果的第一行第一列的值。
|
||||
|
||||
```php
|
||||
$username = $wrapper->fetchOne('SELECT username FROM users WHERE id = ?', array(1));
|
||||
echo $username; // jack
|
||||
```
|
||||
|
||||
### fetchAssociative()
|
||||
|
||||
返回结果内第一行的关联数组形式的数据。
|
||||
|
||||
```php
|
||||
$users = $wrapper->fetchAssociative('SELECT * FROM users');
|
||||
|
||||
/*
|
||||
array(
|
||||
'id' => 1,
|
||||
'username' => 'jack',
|
||||
'gender' => 'man',
|
||||
'update_time' => '2021-10-12'
|
||||
)
|
||||
*/
|
||||
```
|
||||
|
||||
### delete()
|
||||
|
||||
删除查询操作,第一个参数为表名,第二个参数为 `['列名' => '列值']`。
|
||||
|
||||
```php
|
||||
<?php
|
||||
$wrapper->delete('users', array('username' => 'jack'));
|
||||
// 等同于执行DELETE FROM user WHERE username = ? ,参数列表为('jack')
|
||||
```
|
||||
|
||||
### insert()
|
||||
|
||||
插入数据库一行,第一个参数为表名,第二个参数为对应数据。
|
||||
|
||||
```php
|
||||
$wrapper->insert('users', array('id' => 0, 'username' => 'jwage', 'gender' => 'woman', 'update_time' => '2021-10-17'));
|
||||
// INSERT INTO user (id, username, gender, update_time) VALUES (?,?,?,?) (0,jwage,woman,2021-10-17)
|
||||
```
|
||||
|
||||
### update()
|
||||
|
||||
更新数据库,使用给定数据更新匹配键值标识符的所有行。
|
||||
|
||||
```php
|
||||
<?php
|
||||
$wrapper->update('user', array('username' => 'jwage'), array('id' => 1));
|
||||
// UPDATE user (username) VALUES (?) WHERE id = ? (jwage, 1)
|
||||
```
|
||||
|
||||
7
docs/component/store/mysql/config.md
Normal file
7
docs/component/store/mysql/config.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# 配置
|
||||
|
||||
炸毛框架的数据库组件支持原生 SQL、查询构造器,去掉了复杂的对象模型关联,同时默认为数据库连接池,使开发变得简单。
|
||||
|
||||
数据库的配置位于 `config/global.php` 文件的 `mysql_config` 段,见 [全局配置](../../../../guide/basic-config#mysql_config)。
|
||||
|
||||
如果 `mysql_config.host` 字段为空,则不创建数据库连接池,填写后将创建,且默认保持长连接。
|
||||
1
docs/component/store/mysql/mysql-statement.md
Normal file
1
docs/component/store/mysql/mysql-statement.md
Normal file
@@ -0,0 +1 @@
|
||||
你好啊,这里是 Statement。
|
||||
14
docs/component/store/mysql/mysql.md
Normal file
14
docs/component/store/mysql/mysql.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# MySQL 数据库简介
|
||||
|
||||
炸毛框架的数据库组件对接了 MySQL 连接池,在使用过程中无需配置即可实现 MySQL 查询,同时拥有高并发。
|
||||
|
||||
目前 2.5 版本后炸毛框架底层采用了 `doctrine/dbal` 组件,可以方便地构建 SQL 语句。
|
||||
|
||||
本章大体查询内容均以下表 `users` 为基础:
|
||||
|
||||
| id | username | gender | update_time |
|
||||
| -- | -------- | ------ | ----------- |
|
||||
| 1 | jack | man | 2021-10-12 |
|
||||
| 2 | rose | woman | 2021-10-11 |
|
||||
|
||||
#
|
||||
@@ -2,6 +2,10 @@
|
||||
|
||||
前面讲到 LightCache 轻量缓存在特定的情况下为了保证数据不被多进程的因素导致丢失或覆盖,在高并发情况下修改数据需要加锁,所以炸毛框架内置了 SpinLock 自旋锁。
|
||||
|
||||
!!! tip "提示"
|
||||
|
||||
框架单进程运行的模式下不需要任何自旋锁。
|
||||
|
||||
## 配置
|
||||
|
||||
自旋锁使用无需配置,和 LightCache 同源。
|
||||
|
||||
@@ -165,3 +165,31 @@ public function onThrowing(?Exception $e) {
|
||||
|
||||
`ctx()` 为获取当前协程空间绑定的 `request` 和 `response` 对象。
|
||||
|
||||
## 中间件加载错误处理策略
|
||||
|
||||
中间件在某些情况下可能会产生普通 PHP 异常以外的异常,不能被框架的正常错误流程捕获,所以这里额外说明了中间件异常处理的几种策略。
|
||||
|
||||
中间件异常处理策略可以在 2.5.2 版本之后通过 `global.php` 中的 `runtime` 下 `middleware_error_policy` 设置。
|
||||
|
||||
- **0**: 无论被运行事件中间件是否存在,都不抛出异常,继续执行事件。
|
||||
- **1**: 在框架启动时如果某事件被注解了一个不存在的中间件,则不抛出异常,在执行期间才检测是否存在此中间件,并抛出异常。
|
||||
- **2**: 严格的中间件检查,在框架启动时就检测所有被注解了中间件的注解事件。
|
||||
|
||||
假设我们有一个路由注解 `@RequestMapping("/test")`,同时注解了一个不存在的中间件,如下:
|
||||
|
||||
```php
|
||||
/**
|
||||
* @RequestMapping("/test")
|
||||
* @Middleware("foo")
|
||||
*/
|
||||
public function testRoute() {
|
||||
return "I am testing middleware";
|
||||
}
|
||||
```
|
||||
|
||||
配置项为 0,此中间件类不存在的话,则会报告 warning,并直接执行此函数。
|
||||
|
||||
配置项为 1,在访问此路由执行此函数时会抛出异常,中断此次事件。
|
||||
|
||||
配置项为 2,在框架启动时抛出致命异常。
|
||||
|
||||
|
||||
@@ -20,9 +20,10 @@
|
||||
| `crash_dir` | 存放崩溃和运行日志的目录 | `zm_data` 下的 `crash/` |
|
||||
| `config_dir` | 存放 saveToJson() 方法保存的数据的目录 | `zm_data` 下的 `config/` |
|
||||
| `swoole` | 对应 Swoole server 中 set 的参数,参考Swoole文档 | 见子表 `swoole` |
|
||||
| `runtime` | 一些框架运行时调整的设置 | 见子表 `runtime` |
|
||||
| `light_cache` | 轻量内置 key-value 缓存 | 见字表 `light_cache` |
|
||||
| `worker_cache` | 跨进程变量级缓存 | 见子表 `worker_cache` |
|
||||
| `sql_config` | MySQL 数据库连接信息 | 见子表 `sql_config` |
|
||||
| `mysql_config` | MySQL 数据库连接信息 | 见子表 `mysql_config` |
|
||||
| `redis_config` | Redis 连接信息 | 见子表 `redis_config` |
|
||||
| `access_token` | OneBot 客户端连接约定的token,留空则无,相关设置见 [组件 - Access Token 验证](component/access-token) | 空 |
|
||||
| `http_header` | HTTP 请求自定义返回的header | 见配置文件 |
|
||||
@@ -34,6 +35,7 @@
|
||||
| `server_event_handler_class` | 注册 Swoole Server 事件注解的类列表,在 Swoole 服务器启动前就被加载 | 空 |
|
||||
| `onebot` | OneBot 协议相关配置 | 见子表 `onebot` |
|
||||
| `remote_terminal` | 远程终端相关配置 | 见子表 `remote_terminal` |
|
||||
| `module_loader` | 模块/插件 加载相关配置 | 见子表 `module_loader` |
|
||||
|
||||
### 子表 **swoole**
|
||||
|
||||
@@ -47,6 +49,16 @@
|
||||
| `task_worker_num` | TaskWorker 工作进程数 | 默认不开启(此参数被注释) |
|
||||
| `task_enable_coroutine` | TaskWorker 工作进程启用协程 | 默认不开启(此参数被注释)或 `bool` |
|
||||
|
||||
### 子表 runtime
|
||||
|
||||
| 配置名称 | 说明 | 默认值 |
|
||||
| ----------------------------- | ------------------------------------------------------------ | --------------------------------------- |
|
||||
| `swoole_coroutine_hook_flags` | Swoole 启动时一键协程化 Hook 的 Flag 值,详见 [一键协程化](http://wiki.swoole.com/#/runtime?id=%e5%87%bd%e6%95%b0%e5%8e%9f%e5%9e%8b) | `SWOOLE_HOOK_ALL & (~SWOOLE_HOOK_CURL)` |
|
||||
| `swoole_server_mode` | Swoole Server 启动的进程模式,有 `SWOOLE_PROCESS` 和 `SWOOLE_BASE` 两种,见 [启动方式](http://wiki.swoole.com/#/learn?id=swoole_process) | `SWOOLE_PROCESS` |
|
||||
| `middleware_error_policy` | 中间件错误处理策略,见 [中间件 - 错误处理策略](../../event/middleware/#_6) | 1 |
|
||||
| `reload_delay_time` | 框架 reload 重载命令接收后延迟的时间(毫秒,0 为不等待) | 800 |
|
||||
| `global_middleware_binding` | 给注解事件绑定全局中间件,见 [中间件 - 全局中间件](../../event/middleware/#_6) | `[]` |
|
||||
|
||||
### 子表 **light_cache**
|
||||
|
||||
| 配置名称 | 说明 | 默认值 |
|
||||
@@ -63,17 +75,18 @@
|
||||
| -------- | --------------------------- | ------ |
|
||||
| `worker` | 跨进程缓存的存储工作进程 id | 0 |
|
||||
|
||||
### 子表 **sql_config**
|
||||
### 子表 **mysql_config**
|
||||
|
||||
| 配置名称 | 说明 | 默认值 |
|
||||
| ------------------------ | ------------------------------ | ------------------------------------------------------------ |
|
||||
| `sql_host` | 数据库地址(留空则不使用数据库) | 空 |
|
||||
| `sql_port` | 数据库端口 | 3306 |
|
||||
| `sql_username` | 连接数据库的用户名 | |
|
||||
| `sql_database` | 要连接的数据库名 | |
|
||||
| `sql_password` | 数据库连接密码 | |
|
||||
| `sql_options` | PDO 数据库的 options 参数 | `[PDO::ATTR_STRINGIFY_FETCHES => false,PDO::ATTR_EMULATE_PREPARES => false]` |
|
||||
| `sql_default_fetch_mode` | PDO 的 fetch 模式 | `PDO::FETCH_ASSOC` |
|
||||
| `host` | 数据库地址(留空则不使用数据库) | 空 |
|
||||
| `port` | 数据库端口 | 3306 |
|
||||
| `username` | 连接数据库的用户名 | |
|
||||
| `dbname` | 要连接的数据库名 | |
|
||||
| `password` | 数据库连接密码 | |
|
||||
| `options` | PDO 数据库的 options 参数 | `[PDO::ATTR_STRINGIFY_FETCHES => false,PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC]` |
|
||||
| `pool_size` | MySQL 连接池大小 | 64 |
|
||||
| `charset` | MySQL 字符集 | `utf8mb4` |
|
||||
|
||||
### 子表 **redis_config**
|
||||
|
||||
@@ -95,11 +108,13 @@
|
||||
|
||||
### 子表 onebot
|
||||
|
||||
| 配置名称 | 说明 | 默认值 |
|
||||
| ----------------- | ------------------------------------------------------------ | ------ |
|
||||
| `status` | 是否开启 OneBot 标准机器人解析功能 | true |
|
||||
| `single_bot_mode` | 是否开启单机器人模式 | false |
|
||||
| `message_level` | 机器人的 WebSocket 事件在 Swoole 原生事件 `@OnMessageEvent` 中的等级(越高说明越被优先处理) | 99999 |
|
||||
| 配置名称 | 说明 | 默认值 |
|
||||
| ------------------------ | ------------------------------------------------------------ | ------ |
|
||||
| `status` | 是否开启 OneBot 标准机器人解析功能 | true |
|
||||
| `single_bot_mode` | 是否开启单机器人模式 | false |
|
||||
| `message_level` | 机器人的 WebSocket 事件在 Swoole 原生事件 `@OnMessageEvent` 中的等级(越高说明越被优先处理) | 99 |
|
||||
| `message_convert_string` | 是否将数组格式的消息转换为字符串以保证与旧版本的兼容性 | true |
|
||||
| `message_command_policy` | CQCommand命令匹配后执行流程,`interrupt` 为不执行后续 CQMessage,`continue` 为继续 | `interrupt` |
|
||||
|
||||
### 子表 remote_terminal
|
||||
|
||||
@@ -110,6 +125,13 @@
|
||||
| `port` | 远程终端监听的 TCP 端口 | 20002 |
|
||||
| `token` | 远程终端连接的令牌(如果为空("")则不验证) | "" |
|
||||
|
||||
### 子表 module_loader
|
||||
|
||||
| 配置名称 | 说明 | 默认值 |
|
||||
| -------- | ------------------------------------------------------------ | ----------- |
|
||||
| `enable_hotload` | 是否开启热加载模块包的功能 | false |
|
||||
| `load_path` | 模块包加载的目录地址 | `zm_data` 下的 `modules` |
|
||||
|
||||
## 多环境下的配置文件
|
||||
|
||||
炸毛框架的配置文件模块支持不同环境下的配置文件,主要结构为 `global.{环境}.php`。在一般情况下,炸毛框架默认从教程引导方式根据指令 `vendor/bin/start server` 启动的框架是不带环境控制的。这章将讲述如何根据不同的环境(development / staging / production)来编写配置文件。
|
||||
|
||||
@@ -69,5 +69,7 @@
|
||||
| E00067 | 模块解包合并 `composer.json` 时没有找到项目原文件 | 检查项目的工作目录下是否有 `composer.json` 文件存在。 |
|
||||
| E00068 | 模块解包时无法正常拷贝文件 | 检查文件夹是否正常可以创建和写入。 |
|
||||
| E00069 | 框架不能启动两个 ConsoleApplication 实例 | 不要重复使用 `new ConsoleApplication()`。 |
|
||||
| E00070 | 框架找不到 `zm_data` 目录 | 检查配置中指定的 `zm_data` 目录是否存在。 |
|
||||
| E00071 | 框架找不到对应类型的 API 调用类 | 检查 `getExtendedAPI($name)` 传入的 `$name` 是否正确 |
|
||||
| E99999 | 未知错误 | |
|
||||
|
||||
|
||||
37
docs/guide/upgrade.md
Normal file
37
docs/guide/upgrade.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# 升级指南
|
||||
|
||||
因为框架在随着需求以及 Bug 在不断更新,所以在未来框架会发布新版本。为了方便从旧版本安装并使用框架的开发者无损更新到新版本,这里提供了升级版本上需要注意的事项。
|
||||
|
||||
## 版本约定
|
||||
|
||||
炸毛框架的版本号一般情况均按照 [Semantic Versioning 2.0.0](https://semver.org/) 标准进行滚动发行,规则简述如下:
|
||||
|
||||
假设版本号为 x.y.z:
|
||||
|
||||
- `x` 为大版本,一般只有在发生完全无法兼容的更新时增加,需要开发者最重视。
|
||||
- `y` 为小发行版本,默认情况下会新增组件功能,但会尽可能兼容旧版本,存在不兼容情况极少。
|
||||
- `z` 为补丁版本,在不进行任何大功能更新情况下提供 Bug 的修复,完全兼容前版本。
|
||||
|
||||
例如,炸毛框架的 `2.4.2` 版本,在 `2.5.0` 发行后,框架提供了大量新组件,但是对旧版本的配置和组件完全兼容,无任何额外的说明,则可以直接升级。
|
||||
|
||||
## 升级方法
|
||||
|
||||
根据安装方法不同,升级的方法也不同。
|
||||
|
||||
框架安装方式有多种,但主要分为三类:
|
||||
|
||||
- Composer 加载库的方式
|
||||
- 框架源码模式
|
||||
- Phar 打包模式
|
||||
|
||||
在 Composer 加载库的方式下,一般是指使用命令 `composer require zhamao/framework` 或 `composer create-project zhamao/framework-starter` 的方式安装框架,框架的核心文件都在 `vendor` 目录下。
|
||||
|
||||
此方式安装的框架升级最方便,直接执行命令 `composer update` 即可。
|
||||
|
||||
框架源码模式安装一般为直接使用 `git clone` 框架本体的 GitHub 仓库或下载 master 分支安装,这种情况不可升级版本(或使用 `git pull` 拉取)。
|
||||
|
||||
Phar 打包模式更新则必须重新自行打包新版本,例如从 Composer 加载库方式打包的框架,则需在原目录使用 `composer update` 后再次打包一个新版本。
|
||||
|
||||
## 升级提示
|
||||
|
||||
如果在升级过程中遇到了提示,则可能是需要升级某些配置文件需要手动进行合并更新。如果提示了更新,建议到 `vendor/zhamao/framework/config/global.php` 框架的最新库内配置文件与 `config/global.php` 文件进行对比。
|
||||
@@ -3,14 +3,12 @@
|
||||
> 本文档为炸毛框架 v2 版本,如需查看 v1 版本,[点我](https://docs-v1.zhamao.xin/)。
|
||||
|
||||
!!! tip "提示"
|
||||
|
||||
|
||||
编写文档需要较大精力,你也可以参与到本文档的建设中来,比如找错字,增加或更正内容,每页文档可直接点击右上方铅笔图标直接跳转至 GitHub 进行编辑,编辑后自动 Fork 并生成 Pull Request,以此来贡献此文档!
|
||||
|
||||
炸毛框架使用 PHP 编写,采用 Swoole 扩展为基础,主要面向 API 服务,聊天机器人(OneBot 标准的机器人对接),包含 WebSocket、HTTP 等监听和请求库,用户代码采用模块化处理,使用注解可以方便地编写各类功能。
|
||||
|
||||
框架主要用途为 HTTP/WS 服务器,机器人搭建框架。尤其对于聊天机器人消息处理较为方便和全面,提供了众多会话机制和内部调用机制,可以以各种方式设计你自己的模块。
|
||||
|
||||
在 HTTP 和 WebSocket 服务器上,PHP 的扩展 Swoole 提供了高性能的支持,使其效率可媲美 nginx 静态网页处理的效率。
|
||||
框架主要用途为 HTTP/WebSocket 服务器,机器人搭建框架。尤其对于聊天机器人消息处理较为方便和全面,提供了众多会话机制和内部调用机制,可以以各种方式设计你自己的模块。
|
||||
|
||||
此外,QQ 机器人方面此框架基于 OneBot 标准的反向 WebSocket 连接,比传统 HTTP 通信更快。
|
||||
|
||||
@@ -36,7 +34,7 @@ public function index() {
|
||||
首先,你需要了解你需要知道哪些事情才能开始着手使用框架:
|
||||
|
||||
1. Linux 命令行(会跑 Linux 程序)
|
||||
2. php 7.2+ 开发环境(项目会持续支持最新的 PHP 版本)
|
||||
2. php >=7.2 开发环境(项目会持续支持最新的 PHP 版本)
|
||||
4. OneBot 机器人聊天接口标准
|
||||
|
||||
需要值得注意的是,本教程中所涉及的内容均为尽可能翻译为白话的方式进行描述,但对于框架的组件或事件等需要单独拆分说明文档的部分则需要足够详细,所以本教程提供一个快速上手的教程,并且会将最典型的安装方式写到快速教程篇。
|
||||
|
||||
128
docs/update/build-update.md
Normal file
128
docs/update/build-update.md
Normal file
@@ -0,0 +1,128 @@
|
||||
# 更新日志(master 分支 commit)
|
||||
|
||||
此文档将显示非发布版的提交版本相关更新文档,可能与发布版的更新日志有重合,在此仅作更新记录。
|
||||
|
||||
同时此处将只使用 build 版本号进行区分。
|
||||
|
||||
## build 434 (2022-1-8)
|
||||
|
||||
- 修复框架在 PHP 8.1 下运行时的一些问题
|
||||
- 新增 Console 日志输出到文件的功能
|
||||
|
||||
## build 433 (2021-12-28)
|
||||
|
||||
- 修复 OneBotV11 因 IDE 自动优化导致 API 接口发生变化的问题
|
||||
|
||||
## build 432 (2021-12-25)
|
||||
|
||||
- 新增 GoCqhttpAPI 包,用于支持额外的 OneBot Action(API)
|
||||
- 修复 MySQL 查询器中 `fetchOne()` 方法无法正确返回值的 Bug
|
||||
- 修复 Swoole Hook 因配置不当无法正确使用的 Bug
|
||||
|
||||
## build 431 (2021-12-22)
|
||||
|
||||
- 修复 Issue #50
|
||||
- 新增 PhpStorm IDE 直接运行框架的脚本
|
||||
|
||||
## build 430 (2021-12-8)
|
||||
|
||||
- 删除调试信息
|
||||
- 修复 #56 中关于数据库组件的 Bug
|
||||
|
||||
## build 429 (2021-12-7)
|
||||
|
||||
- 新增配置项 `onebot`.`message_command_policy`
|
||||
- 新增 CQCommand 阻断策略的自定义配置功能
|
||||
- 修复 CQAfter 无法正常使用的 bug #53
|
||||
|
||||
## build 428 (2021-11-16)
|
||||
|
||||
- 修复 `ctx()->waitMessage()` 在 array 消息模式下无法正确返回消息字符串的问题
|
||||
- 新增 `ctx()->getArrayMessage()` 和 `ctx()->getStringMessage()` 两个方法
|
||||
- 修复注解事件 `CQCommand` 和 `CQMessage` 在 array 消息模式下无法正确解析的 Bug
|
||||
|
||||
## build 427 (2021-11-16)
|
||||
|
||||
- 新增全局中间件,可在全局配置文件中设置
|
||||
- 修复部分 Typo
|
||||
- 新增指令 `server:status`、`server:reload`、`server:stop` 可用在新开终端中查看框架运行状态、重启和退出
|
||||
- 新增支持 `array` 格式的消息
|
||||
- 上下文 Context 对象新增 `getOriginMessage()` 用于获取原消息,`getMessage()` 如果在设置了转换后,将默认转换消息为字符串格式保持与旧模块兼容
|
||||
- OneBot API 新增全局过滤器,可用作 Action 过滤重写等操作
|
||||
- 配置文件新增 `runtime.reload_delay_time`,用于可配置重载 Worker 等待的时间(毫秒)
|
||||
- 配置文件新增 `runtime.global_middleware_binding`,用于配置全局中间件
|
||||
- 配置文件新增 `onebot.message_convert_string`,用于配置是否转换数组格式为字符串,保证与前版本的兼容性(默认为 true)
|
||||
- MessageUtil 消息工具类新增方法:`strToArray($msg, bool $ignore_space = true, bool $trim_text = false)`
|
||||
- MessageUtil 消息工具类新增方法:`arrayToStr(array $array)`
|
||||
- 新增框架启动多次监测功能,无法使用同一个框架项目同时启动两个框架
|
||||
|
||||
## build 426 (2021-11-10)
|
||||
|
||||
- 修复 CQ 码的解析函数 Bug(#52)
|
||||
|
||||
## build 425 (2021-11-3)
|
||||
|
||||
- 删除未实际应用功能的配置参数
|
||||
- 修复 reload 时会断开 WebSocket 连接且导致进程崩溃的 Bug
|
||||
|
||||
## build 424 (2021-11-2)
|
||||
|
||||
- 新增 InstantModule 类、ZMServer 类、ModuleBase 类
|
||||
- 配置文件新增 `runtime.reload_kill_connect`、`runtime.global_middleware_binding` 选项
|
||||
- 修复部分情况下闭包事件分发时崩溃的 bug
|
||||
- 新增内部方法 `_zm_env_check`
|
||||
- 调整默认的 OneBot 模块对应的等级从 99999 调整为 99
|
||||
- 新增导出框架运行参数的列表功能
|
||||
|
||||
## build 423 (2021-10-17)
|
||||
|
||||
- 修复 PHP 7.2 ~ 7.3 下无法使用新版 MySQL 组件的 bug
|
||||
|
||||
## build 422 (2021-10-6)
|
||||
|
||||
- 修复 `script_` 前缀无法被排除加载模块的 bug
|
||||
- 修复 MySQL 组件的依赖问题
|
||||
|
||||
## build 421 (2021-9-11)
|
||||
|
||||
- 删除多余的调试信息
|
||||
|
||||
## build 420 (2021-9-11)
|
||||
|
||||
- 修复 OneBot 事件无法响应的 bug
|
||||
- 新增部分 EventDispatcher 触发的事件 debug 日志
|
||||
|
||||
## build 419 (2021-9-11)
|
||||
|
||||
- 修复 DB 模块在未连接数据库的时候抛出未知异常
|
||||
- 修复部分情况下打包模块出现的错误
|
||||
|
||||
## build 418 (2021-9-10)
|
||||
|
||||
- 修复 ZMAtomic 在 test 环境下的 bug
|
||||
- 修复 MessageUtil 的报错
|
||||
|
||||
## build 417 (2021-8-29)
|
||||
|
||||
- 新增 AnnotationException,统一框架内部的抛出异常的类型
|
||||
- 新增 AnnotationParser 下的 `verifyMiddlewares()` 方法
|
||||
- 私有化 CQAPI 类下的内部方法
|
||||
- 将 WebSocket API 响应超时时间从 60 秒缩短为 30 秒
|
||||
- 修复 DB 类不能使用旧查询器的 bug
|
||||
- 统一 DB 类下抛出 Exception 的类型为 ZMException 的子类
|
||||
- EventDispatcher 新增对 `middleware_error_policy` 的处理段
|
||||
- 配置文件下 `runtime` 新增 `middleware_error_policy` 字段
|
||||
- 将 LightCache 组件抛出的异常改为 LightCacheException
|
||||
- ModuleManager 修复改配置的 `load_path` 不生效的 bug
|
||||
- 修复打包时生成的 Phar Autoload 列表出错的 bug
|
||||
- 将配置的 override 改为 overwrite
|
||||
- 新增解包时忽略依赖的选项(`--ignore-depends`)
|
||||
- 删除众多调试日志,修改部分调试日志为 debug 级别的输出
|
||||
- 修改 `ZM\MySQL\MySQLManager` 下的 `getConnection()` 为 `getWrapper()`
|
||||
- MySQLPool 对象新增 `getCount()` 方法
|
||||
- 新增 MySQLQueryBuilder 类(`doctrine/dbal` 的 wrapper 类)
|
||||
- 修复 MySQLStatement 封装原 dbal 组件时与连接池不兼容的 bug
|
||||
- 新增 MySQLStatementWrapper 类
|
||||
- 完善 MySQLWrapper 类,用作主要的查询对象控制类
|
||||
- 编写外部插件加载方式(Phar 热加载功能)
|
||||
- 修复 `ZMUtil::getClassesPsr4()` 方法在遇到空扩展名文件时的报错
|
||||
@@ -2,6 +2,19 @@
|
||||
|
||||
这里将会记录各个主版本的框架升级后,涉及 `global.php` 的更新日志,你可以根据这里描述的内容与你的旧配置文件进行合并。
|
||||
|
||||
## v2.6.6 (build 434)
|
||||
|
||||
- 新增 `$config['runtime']` 下的 `save_console_log_file` 项。
|
||||
|
||||
## v2.6.0 (build 427)
|
||||
|
||||
- 新增 `$config['runtime']` 下的 `reload_delay_time`、`global_middleware_binding` 项。
|
||||
- 新增 `$config['onebot']` 下的 `message_convert_string` 项。
|
||||
|
||||
## v2.5.1 (build 417)
|
||||
|
||||
- 新增 `$config['runtime']` 下的 `middleware_error_policy` 选项。
|
||||
|
||||
## v2.5.0 (build 413)
|
||||
|
||||
- 新增 `$config['runtime']` 运行时设置。
|
||||
|
||||
@@ -1,8 +1,146 @@
|
||||
# 更新日志(v2 版本)
|
||||
|
||||
## v2.5.0(未发布,测试中)
|
||||
## v2.6.6(build 434)
|
||||
|
||||
> 更新时间:未知
|
||||
> 更新时间:2022.1.8
|
||||
|
||||
- 修复框架在 PHP 8.1 下运行时的一些问题
|
||||
- 新增 Console 日志输出到文件的功能
|
||||
|
||||
## v2.6.5(build 433)
|
||||
|
||||
> 更新时间:2021.12.28
|
||||
|
||||
- 修复 OneBotV11 因 IDE 自动优化导致 API 接口发生变化的问题
|
||||
|
||||
## v2.6.4(build 432)
|
||||
|
||||
> 更新时间:2021.12.25
|
||||
|
||||
- 新增 GoCqhttpAPI 包,用于支持额外的 OneBot Action(API)
|
||||
- 修复 MySQL 查询器中 `fetchOne()` 方法无法正确返回值的 Bug
|
||||
- 修复 Swoole Hook 因配置不当无法正确使用的 Bug
|
||||
- 修复 Issue #50
|
||||
- 新增 PhpStorm IDE 直接运行框架的脚本
|
||||
|
||||
## v2.6.3 (build 430)
|
||||
|
||||
> 更新时间:2021.12.8
|
||||
|
||||
- 删除调试信息
|
||||
- 修复 #56 中关于数据库组件的 Bug
|
||||
|
||||
## v2.6.2 (build 429)
|
||||
|
||||
> 更新时间:2021.12.7
|
||||
|
||||
- 新增配置项 `onebot`.`message_command_policy`
|
||||
- 新增 CQCommand 阻断策略的自定义配置功能
|
||||
- 修复 CQAfter 无法正常使用的 bug #53
|
||||
|
||||
## v2.6.1 (build 428)
|
||||
|
||||
> 更新时间:2021.11.16
|
||||
|
||||
- 修复 ctx()->waitMessage() 在 array 消息模式下无法正确返回消息字符串的问题
|
||||
- 新增 ctx()->getArrayMessage() 和 ctx()->getStringMessage() 两个方法
|
||||
- 修复注解事件 CQCommand 和 CQMessage 在 array 消息模式下无法正确解析的 Bug
|
||||
|
||||
## v2.6.0 (build 427)
|
||||
|
||||
> 更新时间:2021.11.16
|
||||
|
||||
- 新增全局中间件,可在全局配置文件中设置
|
||||
- 修复部分 Typo
|
||||
- 新增指令 `server:status`、`server:reload`、`server:stop` 可用在新开终端中查看框架运行状态、重启和退出
|
||||
- 新增支持 `array` 格式的消息
|
||||
- 上下文 Context 对象新增 `getOriginMessage()` 用于获取原消息,`getMessage()` 如果在设置了转换后,将默认转换消息为字符串格式保持与旧模块兼容
|
||||
- OneBot API 新增全局过滤器,可用作 Action 过滤重写等操作
|
||||
- 配置文件新增 `runtime.reload_delay_time`,用于可配置重载 Worker 等待的时间(毫秒)
|
||||
- 配置文件新增 `runtime.global_middleware_binding`,用于配置全局中间件
|
||||
- 配置文件新增 `onebot.message_convert_string`,用于配置是否转换数组格式为字符串,保证与前版本的兼容性(默认为 true)
|
||||
- MessageUtil 消息工具类新增方法:`strToArray($msg, bool $ignore_space = true, bool $trim_text = false)`
|
||||
- MessageUtil 消息工具类新增方法:`arrayToStr(array $array)`
|
||||
- 新增框架启动多次监测功能,无法使用同一个框架项目同时启动两个框架
|
||||
|
||||
## v2.5.8 (build 426)
|
||||
|
||||
> 更新时间:2021.11.10
|
||||
|
||||
- 修复 CQ 码的解析函数 Bug(#52)
|
||||
|
||||
## v2.5.7 (build 425)
|
||||
|
||||
> 更新时间:2021.11.3
|
||||
|
||||
- 调低 OneBot 相关事件在 Swoole 的优先级
|
||||
- 修复部分情况下闭包事件函数分发时引发的崩溃 bug
|
||||
- 修复 reload 时会断开 WebSocket 连接且导致进程崩溃的 bug
|
||||
|
||||
## v2.5.6 (build 423)
|
||||
|
||||
> 更新时间:2021.10.17
|
||||
|
||||
- 修复 PHP 7.2 ~ 7.3 下无法使用新版 MySQL 组件的 bug
|
||||
|
||||
## v2.5.5 (build 422)
|
||||
|
||||
> 更新时间:2021.10.6
|
||||
|
||||
- 修复 `script_` 前缀无法被排除加载模块的 bug
|
||||
- 修复 MySQL 组件的依赖问题
|
||||
|
||||
## v2.5.4 (buidl 421)
|
||||
|
||||
> 更新时间:2021.9.11
|
||||
|
||||
- 删除多余的调试信息
|
||||
|
||||
## v2.5.3 (build 420)
|
||||
|
||||
> 更新时间:2021.9.11
|
||||
|
||||
- 修复 DB 模块在未连接数据库的时候抛出未知异常
|
||||
- 修复部分情况下打包模块出现的错误
|
||||
- 修复 OneBot 事件无法响应的 bug
|
||||
- 新增部分 EventDispatcher 触发的事件 debug 日志
|
||||
|
||||
## v2.5.2 (build 418)
|
||||
|
||||
> 更新时间:2021.9.10
|
||||
|
||||
- 新增 AnnotationException,统一框架内部的抛出异常的类型
|
||||
- 新增 AnnotationParser 下的 `verifyMiddlewares()` 方法
|
||||
- 私有化 CQAPI 类下的内部方法
|
||||
- 将 WebSocket API 响应超时时间从 60 秒缩短为 30 秒
|
||||
- 修复 DB 类不能使用旧查询器的 bug
|
||||
- 统一 DB 类下抛出 Exception 的类型为 ZMException 的子类
|
||||
- EventDispatcher 新增对 `middleware_error_policy` 的处理段
|
||||
- 配置文件下 `runtime` 新增 `middleware_error_policy` 字段
|
||||
- 将 LightCache 组件抛出的异常改为 LightCacheException
|
||||
- ModuleManager 修复改配置的 `load_path` 不生效的 bug
|
||||
- 修复打包时生成的 Phar Autoload 列表出错的 bug
|
||||
- 将配置的 override 改为 overwrite
|
||||
- 新增解包时忽略依赖的选项(`--ignore-depends`)
|
||||
- 删除众多调试日志,修改部分调试日志为 debug 级别的输出
|
||||
- 修改 `ZM\MySQL\MySQLManager` 下的 `getConnection()` 为 `getWrapper()`
|
||||
- MySQLPool 对象新增 `getCount()` 方法
|
||||
- 新增 MySQLQueryBuilder 类(`doctrine/dbal` 的 wrapper 类)
|
||||
- 修复 MySQLStatement 封装原 dbal 组件时与连接池不兼容的 bug
|
||||
- 新增 MySQLStatementWrapper 类
|
||||
- 完善 MySQLWrapper 类,用作主要的查询对象控制类
|
||||
- 编写外部插件加载方式(Phar 热加载功能)
|
||||
- 修复 `ZMUtil::getClassesPsr4()` 方法在遇到空扩展名文件时的报错
|
||||
|
||||
## v2.5.1 (build 416)
|
||||
|
||||
> 更新时间:2021.7.9
|
||||
|
||||
- 修复:脚手架无法正常使用 `init` 命令的 bug。
|
||||
|
||||
## v2.5.0(build 415)
|
||||
|
||||
> 更新时间:2021.7.9
|
||||
|
||||
以下是版本**新增内容**:
|
||||
|
||||
@@ -373,4 +511,4 @@
|
||||
|
||||
> 更新时间:2020.12.23
|
||||
|
||||
已发布正式版。
|
||||
已发布正式版。
|
||||
|
||||
0
ext/go-cqhttp/go-cqhttp-down.sh
Executable file → Normal file
0
ext/go-cqhttp/go-cqhttp-down.sh
Executable file → Normal file
@@ -55,4 +55,4 @@ if [ $? -ne 0 ]; then
|
||||
fi
|
||||
echo "成功下载!" && \
|
||||
echo -e "PHP使用:\truntime/php -v" && \
|
||||
echo -e "Composer使用:\truntime/composer"
|
||||
echo -e "Composer使用:\truntime/composer"
|
||||
|
||||
25
instant-test.php
Normal file
25
instant-test.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
require_once "vendor/autoload.php";
|
||||
|
||||
use ZM\Annotation\CQ\CQCommand;
|
||||
use ZM\Annotation\Swoole\OnOpenEvent;
|
||||
use ZM\ConnectionManager\ConnectionObject;
|
||||
use ZM\Console\Console;
|
||||
use ZM\Module\InstantModule;
|
||||
use ZM\ZMServer;
|
||||
|
||||
$weather = new InstantModule("weather");
|
||||
|
||||
|
||||
$weather->onEvent(OnOpenEvent::class, ['connect_type' => 'qq'], function(ConnectionObject $conn) {
|
||||
Console::info("机器人 " . $conn->getOption("connect_id") . " 已连接!");
|
||||
});
|
||||
|
||||
$weather->onEvent(CQCommand::class, ['match' => '你好'], function() {
|
||||
ctx()->reply("hello呀!");
|
||||
});
|
||||
|
||||
$app = new ZMServer("app-name");
|
||||
$app->addModule($weather);
|
||||
$app->run();
|
||||
51
mkdocs.yml
51
mkdocs.yml
@@ -14,6 +14,7 @@ theme:
|
||||
accent: indigo
|
||||
features:
|
||||
- navigation.tabs
|
||||
- navigation.sections
|
||||
extra_javascript:
|
||||
- https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.1.1/highlight.min.js
|
||||
- javascripts/config.js
|
||||
@@ -66,6 +67,7 @@ nav:
|
||||
- 基本配置: guide/basic-config.md
|
||||
- 编写模块: guide/write-module.md
|
||||
- 注册事件响应: guide/register-event.md
|
||||
- 升级指南: guide/upgrade.md
|
||||
- 错误码对照表: guide/errcode.md
|
||||
- 事件和注解:
|
||||
- 事件和注解: event/index.md
|
||||
@@ -77,46 +79,54 @@ nav:
|
||||
- 事件分发器: event/event-dispatcher.md
|
||||
- 框架组件:
|
||||
- 框架组件: component/index.md
|
||||
- 上下文: component/context.md
|
||||
- 聊天机器人组件:
|
||||
- 机器人 API: component/bot/robot-api.md
|
||||
- 机器人动作(V12): component/bot/robot-api-12.md
|
||||
- 机器人动作(V11): component/bot/robot-api.md
|
||||
- CQ 码(多媒体消息): component/bot/cqcode.md
|
||||
- 机器人消息处理: component/bot/message-util.md
|
||||
- Token 验证: component/bot/access-token.md
|
||||
- 图灵机器人 API: component/bot/turing-api.md
|
||||
- 存储:
|
||||
- LightCache 轻量缓存: component/store/light-cache.md
|
||||
- MySQL 数据库: component/store/mysql.md
|
||||
- MySQL 查询器:
|
||||
- 简介: component/store/mysql/mysql.md
|
||||
- 配置: component/store/mysql/config.md
|
||||
- 执行 SQL 语句: component/store/mysql/common-query.md
|
||||
- MySQL 查询器(废弃): component/store/mysql-db.md
|
||||
- Redis 数据库: component/store/redis.md
|
||||
- ZMAtomic 原子计数器: component/store/atomics.md
|
||||
- SpinLock 自旋锁: component/store/spin-lock.md
|
||||
- 文件管理: component/store/data-provider.md
|
||||
- 通用组件:
|
||||
- 上下文: component/common/context.md
|
||||
- 协程池: component/common/coroutine-pool.md
|
||||
- 单例类: component/common/singleton-trait.md
|
||||
- ZMUtil 杂项: component/common/zmutil.md
|
||||
- 全局方法: component/common/global-functions.md
|
||||
- Console 终端: component/common/console.md
|
||||
- TaskWorker 管理: component/common/task-worker.md
|
||||
- Terminal 终端: component/common/remote-terminal.md
|
||||
- HTTP 服务器工具类:
|
||||
- HTTP 和 WebSocket 客户端: component/zmrequest.md
|
||||
- HTTP 路由管理: component/route-manager.md
|
||||
- HTTP 和 WebSocket 客户端: component/http/zmrequest.md
|
||||
- HTTP 路由管理: component/http/route-manager.md
|
||||
- 模块/插件管理:
|
||||
- 模块打包: component/module/module-pack.md
|
||||
- 协程池: component/coroutine-pool.md
|
||||
- 单例类: component/singleton-trait.md
|
||||
- ZMUtil 杂项: component/zmutil.md
|
||||
- 全局方法: component/global-functions.md
|
||||
- Console 终端: component/console.md
|
||||
- TaskWorker 管理: component/task-worker.md
|
||||
- Terminal 终端: component/remote-terminal.md
|
||||
- 模块解包: component/module/module-unpack.md
|
||||
- 进阶开发:
|
||||
- 进阶开发: advanced/index.md
|
||||
- 框架剖析: advanced/framework-structure.md
|
||||
- 框架启动模式: advanced/custom-start.md
|
||||
- 手动安装环境: advanced/manually-install.md
|
||||
- 从 v1 升级: advanced/to-v2.md
|
||||
- 内部类文件手册: advanced/inside-class.md
|
||||
- 接入 WebSocket 客户端: advanced/connect-ws-client.md
|
||||
- 框架多进程: advanced/multi-process.md
|
||||
- TaskWorker 提高并发: advanced/task-worker.md
|
||||
- 框架高级开发:
|
||||
- 框架剖析: advanced/framework-structure.md
|
||||
- 框架启动模式: advanced/custom-start.md
|
||||
- 手动安装环境: advanced/manually-install.md
|
||||
- 内部类文件手册: advanced/inside-class.md
|
||||
- 框架多进程: advanced/multi-process.md
|
||||
- TaskWorker 提高并发: advanced/task-worker.md
|
||||
- 开发实战教程:
|
||||
- 接入 WebSocket 客户端: advanced/connect-ws-client.md
|
||||
- 编写管理员才能触发的功能: advanced/example/admin.md
|
||||
- FAQ:
|
||||
- FAQ: faq/FAQ.md
|
||||
- 从 v1 升级: faq/to-v2.md
|
||||
- 框架常见问题(持续更新): faq/usual-question.md
|
||||
- 启动时报错 Address already in use: faq/address-already-in-use.md
|
||||
- 出现 deadlock 字样: faq/display-deadlock.md
|
||||
@@ -125,5 +135,6 @@ nav:
|
||||
- 更新日志:
|
||||
- 更新日志(v2): update/v2.md
|
||||
- 更新日志(v1): update/v1.md
|
||||
- 更新日志(build update): update/build-update.md
|
||||
- 配置文件更新日志: update/config.md
|
||||
- <u>炸毛框架 v1</u>: https://docs-v1.zhamao.xin/
|
||||
|
||||
@@ -10,6 +10,7 @@ use ZM\Annotation\Swoole\OnOpenEvent;
|
||||
use ZM\Annotation\Swoole\OnRequestEvent;
|
||||
use ZM\API\CQ;
|
||||
use ZM\API\TuringAPI;
|
||||
use ZM\API\OneBotV11;
|
||||
use ZM\ConnectionManager\ConnectionObject;
|
||||
use ZM\Console\Console;
|
||||
use ZM\Annotation\CQ\CQCommand;
|
||||
@@ -49,8 +50,12 @@ class Hello
|
||||
* @CQCommand("我是谁")
|
||||
*/
|
||||
public function whoami() {
|
||||
$user = ctx()->getRobot()->getLoginInfo();
|
||||
return "你是" . $user["data"]["nickname"] . ",QQ号是" . $user["data"]["user_id"];
|
||||
$bot = ctx()->getRobot()->getLoginInfo();
|
||||
$bot_id = $bot["data"]["user_id"];
|
||||
$r = OneBotV11::get($bot_id);
|
||||
$QQid = ctx()->getUserId();
|
||||
$nick = $r->getStrangerInfo($QQid)["data"]["nickname"];
|
||||
return "你是" . $nick . ",QQ号是" . $QQid;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -33,7 +33,7 @@ class TimerMiddleware implements MiddlewareInterface
|
||||
* @HandleAfter()
|
||||
*/
|
||||
public function onAfter() {
|
||||
Console::info("Using " . round((microtime(true) - $this->starttime) * 1000, 2) . " ms.");
|
||||
Console::info("Using " . round((microtime(true) - $this->starttime) * 1000, 3) . " ms.");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -42,7 +42,7 @@ class TimerMiddleware implements MiddlewareInterface
|
||||
* @throws Exception
|
||||
*/
|
||||
public function onException(Exception $e) {
|
||||
Console::error("Using " . round((microtime(true) - $this->starttime) * 1000, 2) . " ms but an Exception occurred.");
|
||||
Console::error("Using " . round((microtime(true) - $this->starttime) * 1000, 3) . " ms but an Exception occurred.");
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -356,7 +356,7 @@ class CQ
|
||||
}
|
||||
$cq["start"] = $offset + $head;
|
||||
$cq["end"] = $offset + $tmpmsg + $head;
|
||||
$offset += $tmpmsg + 1;
|
||||
$offset += $head + $tmpmsg + 1;
|
||||
$cqs[] = (!$is_object ? $cq : CQObject::fromArray($cq));
|
||||
}
|
||||
return $cqs;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
namespace ZM\API;
|
||||
|
||||
use Closure;
|
||||
use ZM\Console\Console;
|
||||
use ZM\Store\LightCacheInside;
|
||||
use ZM\Store\Lock\SpinLock;
|
||||
@@ -11,6 +12,13 @@ use ZM\Utils\CoMessage;
|
||||
|
||||
trait CQAPI
|
||||
{
|
||||
/** @var null|Closure */
|
||||
private static $filter = null;
|
||||
|
||||
public static function registerFilter(callable $callable) {
|
||||
self::$filter = $callable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $connection
|
||||
* @param $reply
|
||||
@@ -18,13 +26,18 @@ trait CQAPI
|
||||
* @return bool|array
|
||||
*/
|
||||
private function processAPI($connection, $reply, $function = null) {
|
||||
if (is_callable(self::$filter)) {
|
||||
$reply2 = call_user_func(self::$filter, $reply);
|
||||
if (is_bool($reply2)) return $reply2;
|
||||
else $reply = $reply2;
|
||||
}
|
||||
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 = false) {
|
||||
private function processWebsocketAPI($connection, $reply, $function = false) {
|
||||
$api_id = ZMAtomic::get("wait_msg_id")->add(1);
|
||||
$reply["echo"] = $api_id;
|
||||
if (server()->push($connection->getFd(), json_encode($reply))) {
|
||||
@@ -35,7 +48,7 @@ trait CQAPI
|
||||
"self_id" => $connection->getOption("connect_id"),
|
||||
"echo" => $api_id
|
||||
];
|
||||
return CoMessage::yieldByWS($obj, ["echo"], 60);
|
||||
return CoMessage::yieldByWS($obj, ["echo"], 30);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
@@ -63,10 +76,16 @@ trait CQAPI
|
||||
* @return bool
|
||||
* @noinspection PhpUnusedParameterInspection
|
||||
*/
|
||||
public function processHttpAPI($connection, $reply, $function = null): bool {
|
||||
private function processHttpAPI($connection, $reply, $function = null): bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getActionName($suffix, string $method) {
|
||||
$postfix = ($suffix == OneBotV11::API_ASYNC ? '_async' : ($suffix == OneBotV11::API_RATE_LIMITED ? '_rate_limited' : ''));
|
||||
$func_name = strtolower(preg_replace('/(?<=[a-z])([A-Z])/', '_$1', $method));
|
||||
return $func_name . $postfix;
|
||||
}
|
||||
|
||||
public function __call($name, $arguments) {
|
||||
return false;
|
||||
}
|
||||
|
||||
111
src/ZM/API/GoCqhttpAPIV11.php
Normal file
111
src/ZM/API/GoCqhttpAPIV11.php
Normal file
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
|
||||
namespace ZM\API;
|
||||
|
||||
class GoCqhttpAPIV11
|
||||
{
|
||||
const SUPPORT_VERSION = '1.0.0-beta8';
|
||||
|
||||
use CQAPI;
|
||||
|
||||
private $connection;
|
||||
private $callback;
|
||||
private $prefix;
|
||||
|
||||
public function __construct($connection, $callback, $prefix)
|
||||
{
|
||||
$this->connection = $connection;
|
||||
$this->callback = $callback;
|
||||
$this->prefix = $prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取频道系统内BOT的资料
|
||||
* 响应字段:nickname, tiny_id, avatar_url
|
||||
* @link https://github.com/Mrs4s/go-cqhttp/blob/master/docs/guild.md#%E8%8E%B7%E5%8F%96%E9%A2%91%E9%81%93%E7%B3%BB%E7%BB%9F%E5%86%85bot%E7%9A%84%E8%B5%84%E6%96%99
|
||||
* @return array|bool
|
||||
*/
|
||||
public function getGuildServiceProfile()
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__)
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取频道列表
|
||||
* @link https://github.com/Mrs4s/go-cqhttp/blob/master/docs/guild.md#%E8%8E%B7%E5%8F%96%E9%A2%91%E9%81%93%E5%88%97%E8%A1%A8
|
||||
* @return array|bool
|
||||
*/
|
||||
public function getGuildList() {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__)
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过访客获取频道元数据
|
||||
* @link https://github.com/Mrs4s/go-cqhttp/blob/master/docs/guild.md#%E9%80%9A%E8%BF%87%E8%AE%BF%E5%AE%A2%E8%8E%B7%E5%8F%96%E9%A2%91%E9%81%93%E5%85%83%E6%95%B0%E6%8D%AE
|
||||
* @param $guild_id
|
||||
* @return array|bool
|
||||
*/
|
||||
public function getGuildMetaByGuest($guild_id) {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
||||
'params' => [
|
||||
'guild_id' => $guild_id
|
||||
]
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取子频道列表
|
||||
* @link https://github.com/Mrs4s/go-cqhttp/blob/master/docs/guild.md#%E8%8E%B7%E5%8F%96%E5%AD%90%E9%A2%91%E9%81%93%E5%88%97%E8%A1%A8
|
||||
* @param $guild_id
|
||||
* @param false $no_cache
|
||||
* @return array|bool
|
||||
*/
|
||||
public function getGuildChannelList($guild_id, bool $no_cache = false) {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
||||
'params' => [
|
||||
'guild_id' => $guild_id,
|
||||
'no_cache' => $no_cache
|
||||
]
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取频道成员列表
|
||||
* @link https://github.com/Mrs4s/go-cqhttp/blob/master/docs/guild.md#%E8%8E%B7%E5%8F%96%E9%A2%91%E9%81%93%E6%88%90%E5%91%98%E5%88%97%E8%A1%A8
|
||||
* @param $guild_id
|
||||
* @return array|bool
|
||||
*/
|
||||
public function getGuildMembers($guild_id) {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
||||
'params' => [
|
||||
'guild_id' => $guild_id
|
||||
]
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送信息到子频道
|
||||
* @link https://github.com/Mrs4s/go-cqhttp/blob/master/docs/guild.md#%E5%8F%91%E9%80%81%E4%BF%A1%E6%81%AF%E5%88%B0%E5%AD%90%E9%A2%91%E9%81%93
|
||||
* @param $guild_id
|
||||
* @param $channel_id
|
||||
* @param $message
|
||||
* @return array|bool
|
||||
*/
|
||||
public function sendGuildChannelMsg($guild_id, $channel_id, $message) {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
||||
'params' => [
|
||||
'guild_id' => $guild_id,
|
||||
'channel_id' => $channel_id,
|
||||
'message' => $message
|
||||
]
|
||||
], $this->callback);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
<?php
|
||||
<?php /** @noinspection PhpUnused */
|
||||
|
||||
|
||||
namespace ZM\API;
|
||||
@@ -6,6 +6,7 @@ namespace ZM\API;
|
||||
use ZM\ConnectionManager\ConnectionObject;
|
||||
use ZM\ConnectionManager\ManagerGM;
|
||||
use ZM\Exception\RobotNotFoundException;
|
||||
use ZM\Exception\ZMKnownException;
|
||||
|
||||
/**
|
||||
* OneBot V11 的 API 实现类
|
||||
@@ -22,17 +23,18 @@ class OneBotV11
|
||||
const API_RATE_LIMITED = 2;
|
||||
|
||||
/** @var ConnectionObject|null */
|
||||
private $connection;
|
||||
protected $connection;
|
||||
|
||||
private $callback = true;
|
||||
private $prefix = 0;
|
||||
protected $callback = true;
|
||||
protected $prefix = 0;
|
||||
|
||||
/**
|
||||
* @param $robot_id
|
||||
* @return ZMRobot
|
||||
* @throws RobotNotFoundException
|
||||
*/
|
||||
public static function get($robot_id) {
|
||||
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);
|
||||
@@ -44,7 +46,8 @@ class OneBotV11
|
||||
* @return ZMRobot
|
||||
* @throws RobotNotFoundException
|
||||
*/
|
||||
public static function getRandom() {
|
||||
public static function getRandom()
|
||||
{
|
||||
$r = ManagerGM::getAllByName('qq');
|
||||
if ($r == []) throw new RobotNotFoundException("没有任何机器人连接到框架!");
|
||||
return new ZMRobot($r[array_rand($r)]);
|
||||
@@ -53,7 +56,8 @@ class OneBotV11
|
||||
/**
|
||||
* @return ZMRobot[]
|
||||
*/
|
||||
public static function getAllRobot() {
|
||||
public static function getAllRobot()
|
||||
{
|
||||
$r = ManagerGM::getAllByName('qq');
|
||||
$obj = [];
|
||||
foreach ($r as $v) {
|
||||
@@ -62,21 +66,25 @@ class OneBotV11
|
||||
return $obj;
|
||||
}
|
||||
|
||||
public function __construct(ConnectionObject $connection) {
|
||||
public function __construct(ConnectionObject $connection)
|
||||
{
|
||||
$this->connection = $connection;
|
||||
}
|
||||
|
||||
public function setCallback($callback = true) {
|
||||
public function setCallback($callback = true)
|
||||
{
|
||||
$this->callback = $callback;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setPrefix($prefix = self::API_NORMAL) {
|
||||
public function setPrefix($prefix = self::API_NORMAL)
|
||||
{
|
||||
$this->prefix = $prefix;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSelfId() {
|
||||
public function getSelfId()
|
||||
{
|
||||
return $this->connection->getOption('connect_id');
|
||||
}
|
||||
|
||||
@@ -90,9 +98,10 @@ class OneBotV11
|
||||
* @param bool $auto_escape
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function sendPrivateMsg($user_id, $message, $auto_escape = false) {
|
||||
public function sendPrivateMsg($user_id, $message, bool $auto_escape = false)
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
||||
'params' => [
|
||||
'user_id' => $user_id,
|
||||
'message' => $message,
|
||||
@@ -109,9 +118,10 @@ class OneBotV11
|
||||
* @param bool $auto_escape
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function sendGroupMsg($group_id, $message, $auto_escape = false) {
|
||||
public function sendGroupMsg($group_id, $message, bool $auto_escape = false)
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
||||
'params' => [
|
||||
'group_id' => $group_id,
|
||||
'message' => $message,
|
||||
@@ -129,9 +139,10 @@ class OneBotV11
|
||||
* @param bool $auto_escape
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function sendMsg($message_type, $target_id, $message, $auto_escape = false) {
|
||||
public function sendMsg($message_type, $target_id, $message, bool $auto_escape = false)
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
||||
'params' => [
|
||||
'message_type' => $message_type,
|
||||
($message_type == 'private' ? 'user' : $message_type) . '_id' => $target_id,
|
||||
@@ -147,9 +158,10 @@ class OneBotV11
|
||||
* @param $message_id
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function deleteMsg($message_id) {
|
||||
public function deleteMsg($message_id)
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
||||
'params' => [
|
||||
'message_id' => $message_id
|
||||
]
|
||||
@@ -162,9 +174,10 @@ class OneBotV11
|
||||
* @param $message_id
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function getMsg($message_id) {
|
||||
public function getMsg($message_id)
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
||||
'params' => [
|
||||
'message_id' => $message_id
|
||||
]
|
||||
@@ -177,9 +190,10 @@ class OneBotV11
|
||||
* @param $id
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function getForwardMsg($id) {
|
||||
public function getForwardMsg($id)
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
||||
'params' => [
|
||||
'id' => $id
|
||||
]
|
||||
@@ -193,9 +207,10 @@ class OneBotV11
|
||||
* @param int $times
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function sendLike($user_id, $times = 1) {
|
||||
public function sendLike($user_id, int $times = 1)
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
||||
'params' => [
|
||||
'user_id' => $user_id,
|
||||
'times' => $times
|
||||
@@ -211,9 +226,10 @@ class OneBotV11
|
||||
* @param bool $reject_add_request
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function setGroupKick($group_id, $user_id, $reject_add_request = false) {
|
||||
public function setGroupKick($group_id, $user_id, bool $reject_add_request = false)
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
||||
'params' => [
|
||||
'group_id' => $group_id,
|
||||
'user_id' => $user_id,
|
||||
@@ -227,12 +243,13 @@ class OneBotV11
|
||||
* @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 $group_id
|
||||
* @param $user_id
|
||||
* @param $duration
|
||||
* @param int $duration
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function setGroupBan($group_id, $user_id, $duration = 1800) {
|
||||
public function setGroupBan($group_id, $user_id, int $duration = 1800)
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
||||
'params' => [
|
||||
'group_id' => $group_id,
|
||||
'user_id' => $user_id,
|
||||
@@ -249,9 +266,10 @@ class OneBotV11
|
||||
* @param int $duration
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function setGroupAnonymousBan($group_id, $anonymous_or_flag, $duration = 1800) {
|
||||
public function setGroupAnonymousBan($group_id, $anonymous_or_flag, int $duration = 1800)
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
||||
'params' => [
|
||||
'group_id' => $group_id,
|
||||
(is_string($anonymous_or_flag) ? 'flag' : 'anonymous') => $anonymous_or_flag,
|
||||
@@ -267,9 +285,10 @@ class OneBotV11
|
||||
* @param bool $enable
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function setGroupWholeBan($group_id, $enable = true) {
|
||||
public function setGroupWholeBan($group_id, bool $enable = true)
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
||||
'params' => [
|
||||
'group_id' => $group_id,
|
||||
'enable' => $enable
|
||||
@@ -285,9 +304,10 @@ class OneBotV11
|
||||
* @param bool $enable
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function setGroupAdmin($group_id, $user_id, $enable = true) {
|
||||
public function setGroupAdmin($group_id, $user_id, bool $enable = true)
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
||||
'params' => [
|
||||
'group_id' => $group_id,
|
||||
'user_id' => $user_id,
|
||||
@@ -303,9 +323,10 @@ class OneBotV11
|
||||
* @param bool $enable
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function setGroupAnonymous($group_id, $enable = true) {
|
||||
public function setGroupAnonymous($group_id, bool $enable = true)
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
||||
'params' => [
|
||||
'group_id' => $group_id,
|
||||
'enable' => $enable
|
||||
@@ -321,9 +342,10 @@ class OneBotV11
|
||||
* @param string $card
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function setGroupCard($group_id, $user_id, $card = "") {
|
||||
public function setGroupCard($group_id, $user_id, string $card = '')
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
||||
'params' => [
|
||||
'group_id' => $group_id,
|
||||
'user_id' => $user_id,
|
||||
@@ -339,9 +361,10 @@ class OneBotV11
|
||||
* @param $group_name
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function setGroupName($group_id, $group_name) {
|
||||
public function setGroupName($group_id, $group_name)
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
||||
'params' => [
|
||||
'group_id' => $group_id,
|
||||
'group_name' => $group_name
|
||||
@@ -356,9 +379,10 @@ class OneBotV11
|
||||
* @param bool $is_dismiss
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function setGroupLeave($group_id, $is_dismiss = false) {
|
||||
public function setGroupLeave($group_id, bool $is_dismiss = false)
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
||||
'params' => [
|
||||
'group_id' => $group_id,
|
||||
'is_dismiss' => $is_dismiss
|
||||
@@ -375,9 +399,10 @@ class OneBotV11
|
||||
* @param int $duration
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function setGroupSpecialTitle($group_id, $user_id, $special_title = "", $duration = -1) {
|
||||
public function setGroupSpecialTitle($group_id, $user_id, string $special_title = "", int $duration = -1)
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
||||
'params' => [
|
||||
'group_id' => $group_id,
|
||||
'user_id' => $user_id,
|
||||
@@ -395,9 +420,10 @@ class OneBotV11
|
||||
* @param string $remark
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function setFriendAddRequest($flag, $approve = true, $remark = "") {
|
||||
public function setFriendAddRequest($flag, bool $approve = true, string $remark = "")
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
||||
'params' => [
|
||||
'flag' => $flag,
|
||||
'approve' => $approve,
|
||||
@@ -415,9 +441,10 @@ class OneBotV11
|
||||
* @param string $reason
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function setGroupAddRequest($flag, $sub_type, $approve = true, $reason = "") {
|
||||
public function setGroupAddRequest($flag, $sub_type, bool $approve = true, string $reason = "")
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
||||
'params' => [
|
||||
'flag' => $flag,
|
||||
'sub_type' => $sub_type,
|
||||
@@ -432,8 +459,9 @@ class OneBotV11
|
||||
* @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);
|
||||
public function getLoginInfo()
|
||||
{
|
||||
return $this->processAPI($this->connection, ['action' => $this->getActionName($this->prefix, __FUNCTION__)], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -443,9 +471,10 @@ class OneBotV11
|
||||
* @param bool $no_cache
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function getStrangerInfo($user_id, $no_cache = false) {
|
||||
public function getStrangerInfo($user_id, bool $no_cache = false)
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
||||
'params' => [
|
||||
'user_id' => $user_id,
|
||||
'no_cache' => $no_cache
|
||||
@@ -458,9 +487,10 @@ class OneBotV11
|
||||
* @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() {
|
||||
public function getFriendList()
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__)
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__)
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
@@ -471,9 +501,10 @@ class OneBotV11
|
||||
* @param bool $no_cache
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function getGroupInfo($group_id, $no_cache = false) {
|
||||
public function getGroupInfo($group_id, bool $no_cache = false)
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
||||
'params' => [
|
||||
'group_id' => $group_id,
|
||||
'no_cache' => $no_cache
|
||||
@@ -486,9 +517,10 @@ class OneBotV11
|
||||
* @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() {
|
||||
public function getGroupList()
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__)
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__)
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
@@ -500,9 +532,10 @@ class OneBotV11
|
||||
* @param bool $no_cache
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function getGroupMemberInfo($group_id, $user_id, $no_cache = false) {
|
||||
public function getGroupMemberInfo($group_id, $user_id, bool $no_cache = false)
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
||||
'params' => [
|
||||
'group_id' => $group_id,
|
||||
'user_id' => $user_id,
|
||||
@@ -517,9 +550,10 @@ class OneBotV11
|
||||
* @param $group_id
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function getGroupMemberList($group_id) {
|
||||
public function getGroupMemberList($group_id)
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
||||
'params' => [
|
||||
'group_id' => $group_id
|
||||
]
|
||||
@@ -533,9 +567,10 @@ class OneBotV11
|
||||
* @param $type
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function getGroupHonorInfo($group_id, $type) {
|
||||
public function getGroupHonorInfo($group_id, $type)
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
||||
'params' => [
|
||||
'group_id' => $group_id,
|
||||
'type' => $type
|
||||
@@ -548,9 +583,10 @@ class OneBotV11
|
||||
* @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() {
|
||||
public function getCsrfToken()
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__)
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__)
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
@@ -560,9 +596,10 @@ class OneBotV11
|
||||
* @param string $domain
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function getCredentials($domain = "") {
|
||||
public function getCredentials(string $domain = "")
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
||||
'params' => [
|
||||
'domain' => $domain
|
||||
]
|
||||
@@ -576,9 +613,10 @@ class OneBotV11
|
||||
* @param $out_format
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function getRecord($file, $out_format) {
|
||||
public function getRecord($file, $out_format)
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
||||
'params' => [
|
||||
'file' => $file,
|
||||
'out_format' => $out_format
|
||||
@@ -592,9 +630,10 @@ class OneBotV11
|
||||
* @param $file
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function getImage($file) {
|
||||
public function getImage($file)
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
||||
'params' => [
|
||||
'file' => $file
|
||||
]
|
||||
@@ -606,9 +645,10 @@ class OneBotV11
|
||||
* @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() {
|
||||
public function canSendImage()
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__)
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__)
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
@@ -617,9 +657,10 @@ class OneBotV11
|
||||
* @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() {
|
||||
public function canSendRecord()
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__)
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__)
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
@@ -628,9 +669,10 @@ class OneBotV11
|
||||
* @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() {
|
||||
public function getStatus()
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__)
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__)
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
@@ -639,9 +681,10 @@ class OneBotV11
|
||||
* @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() {
|
||||
public function getVersionInfo()
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__)
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__)
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
@@ -651,9 +694,10 @@ class OneBotV11
|
||||
* @param int $delay
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function setRestart($delay = 0) {
|
||||
public function setRestart(int $delay = 0)
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__),
|
||||
'params' => [
|
||||
'delay' => $delay
|
||||
]
|
||||
@@ -665,22 +709,37 @@ class OneBotV11
|
||||
* @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() {
|
||||
public function cleanCache()
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__)
|
||||
'action' => $this->getActionName($this->prefix, __FUNCTION__)
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
public function callExtendedAPI($action, $params = []) {
|
||||
/**
|
||||
* 获取内置支持的扩展API对象
|
||||
* 现支持 go-cqhttp 的扩展API
|
||||
* @param string $package_name
|
||||
* @return mixed
|
||||
* @throws ZMKnownException
|
||||
*/
|
||||
public function getExtendedAPI(string $package_name = 'go-cqhttp')
|
||||
{
|
||||
$table = [
|
||||
'go-cqhttp' => GoCqhttpAPIV11::class,
|
||||
];
|
||||
if (isset($table[$package_name])) {
|
||||
return new $table[$package_name]($this->connection, $this->callback, $this->prefix);
|
||||
} else {
|
||||
throw new ZMKnownException(zm_internal_errcode('E00071'), '无法找到对应的调用扩展类');
|
||||
}
|
||||
}
|
||||
|
||||
public function callExtendedAPI($action, $params = [])
|
||||
{
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $action,
|
||||
'params' => $params
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
private function getActionName(string $method) {
|
||||
$prefix = ($this->prefix == self::API_ASYNC ? '_async' : ($this->prefix == self::API_RATE_LIMITED ? '_rate_limited' : ''));
|
||||
$func_name = strtolower(preg_replace('/(?<=[a-z])([A-Z])/', '_$1', $method));
|
||||
return $func_name . $prefix;
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,9 @@
|
||||
namespace ZM\Annotation;
|
||||
|
||||
use Doctrine\Common\Annotations\AnnotationReader;
|
||||
use ZM\Annotation\CQ\CQCommand;
|
||||
use ZM\Annotation\Interfaces\ErgodicAnnotation;
|
||||
use ZM\Config\ZMConfig;
|
||||
use ZM\Console\Console;
|
||||
use ReflectionClass;
|
||||
use ReflectionException;
|
||||
@@ -17,6 +19,7 @@ use ZM\Annotation\Http\MiddlewareClass;
|
||||
use ZM\Annotation\Http\RequestMapping;
|
||||
use ZM\Annotation\Interfaces\Level;
|
||||
use ZM\Annotation\Module\Closed;
|
||||
use ZM\Exception\AnnotationException;
|
||||
use ZM\Utils\Manager\RouteManager;
|
||||
use ZM\Utils\ZMUtil;
|
||||
|
||||
@@ -53,7 +56,7 @@ class AnnotationParser
|
||||
*/
|
||||
public function registerMods() {
|
||||
foreach ($this->path_list as $path) {
|
||||
Console::debug("parsing annotation in " . $path[0]);
|
||||
Console::debug("parsing annotation in " . $path[0].":".$path[1]);
|
||||
$all_class = ZMUtil::getClassesPsr4($path[0], $path[1]);
|
||||
$this->reader = new AnnotationReader();
|
||||
foreach ($all_class as $v) {
|
||||
@@ -96,7 +99,6 @@ class AnnotationParser
|
||||
if ($vs instanceof ErgodicAnnotation) {
|
||||
foreach (($this->annotation_map[$v]["methods"] ?? []) as $method) {
|
||||
$copy = clone $vs;
|
||||
/** @noinspection PhpUndefinedFieldInspection */
|
||||
$copy->method = $method->getName();
|
||||
$this->annotation_map[$v]["methods_annotations"][$method->getName()][] = $copy;
|
||||
}
|
||||
@@ -107,18 +109,34 @@ class AnnotationParser
|
||||
unset($this->annotation_map[$v]);
|
||||
continue 2;
|
||||
} elseif ($vs instanceof MiddlewareClass) {
|
||||
//注册中间件本身的类,标记到 middlewares 属性中
|
||||
Console::debug("正在注册中间件 " . $reflection_class->getName());
|
||||
$rs = $this->registerMiddleware($vs, $reflection_class);
|
||||
$this->middlewares[$rs["name"]] = $rs;
|
||||
}
|
||||
}
|
||||
|
||||
$inserted = [];
|
||||
|
||||
//预处理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 Middleware) && ($middlewares = ZMConfig::get("global", "runtime")["global_middleware_binding"][get_class($method_anno)] ?? []) !== []) {
|
||||
if (!isset($inserted[$v][$method_name])) {
|
||||
// 在这里在其他中间件前插入插入全局的中间件
|
||||
foreach ($middlewares as $middleware) {
|
||||
$mid_class = new Middleware();
|
||||
$mid_class->middleware = $middleware;
|
||||
$mid_class->class = $v;
|
||||
$mid_class->method = $method_name;
|
||||
$this->middleware_map[$v][$method_name][] = $mid_class;
|
||||
}
|
||||
$inserted[$v][$method_name] = true;
|
||||
}
|
||||
}
|
||||
if ($method_anno instanceof RequestMapping) {
|
||||
RouteManager::importRouteByAnnotation($method_anno, $method_name, $v, $methods_annotations);
|
||||
} elseif ($method_anno instanceof Middleware) {
|
||||
@@ -131,9 +149,10 @@ class AnnotationParser
|
||||
Console::debug("解析注解完毕!");
|
||||
}
|
||||
|
||||
public function generateAnnotationEvents() {
|
||||
public function generateAnnotationEvents(): array {
|
||||
$o = [];
|
||||
foreach ($this->annotation_map as $obj) {
|
||||
// 这里的ErgodicAnnotation是为了解决类上的注解可穿透到方法上的问题
|
||||
foreach (($obj["class_annotations"] ?? []) as $class_annotation) {
|
||||
if ($class_annotation instanceof ErgodicAnnotation) continue;
|
||||
else $o[get_class($class_annotation)][] = $class_annotation;
|
||||
@@ -193,6 +212,12 @@ class AnnotationParser
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal 用于 level 排序
|
||||
* @param $events
|
||||
* @param string $class_name
|
||||
* @param string $prefix
|
||||
*/
|
||||
public function sortByLevel(&$events, string $class_name, $prefix = "") {
|
||||
if (is_a($class_name, Level::class, true)) {
|
||||
$class_name .= $prefix;
|
||||
@@ -203,4 +228,22 @@ class AnnotationParser
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws AnnotationException
|
||||
*/
|
||||
public function verifyMiddlewares() {
|
||||
if ((ZMConfig::get("global", "runtime")["middleware_error_policy"] ?? 1) === 2) {
|
||||
//我承认套三层foreach很不优雅,但是这个会很快的。
|
||||
foreach($this->middleware_map as $class => $v) {
|
||||
foreach ($v as $method => $vs) {
|
||||
foreach($vs as $mid) {
|
||||
if (!isset($this->middlewares[$mid->middleware])) {
|
||||
throw new AnnotationException("Annotation parse error: Unknown MiddlewareClass named \"{$mid->middleware}\"!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ class DaemonReloadCommand extends DaemonCommand
|
||||
protected static $defaultName = 'daemon:reload';
|
||||
|
||||
protected function configure() {
|
||||
$this->setDescription("重载守护进程下的用户代码(仅限--daemon模式可用)");
|
||||
$this->setDescription("重载框架");
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int {
|
||||
|
||||
@@ -11,18 +11,20 @@ class DaemonStatusCommand extends DaemonCommand
|
||||
protected static $defaultName = 'daemon:status';
|
||||
|
||||
protected function configure() {
|
||||
$this->setDescription("查看守护进程框架的运行状态(仅限--daemon模式可用)");
|
||||
$this->setDescription("查看框架的运行状态");
|
||||
}
|
||||
|
||||
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>");
|
||||
$stdout = file_get_contents($this->daemon_file["stdout"]);
|
||||
$stdout = explode("\n", $stdout);
|
||||
for ($i = 15; $i > 0; --$i) {
|
||||
if (isset($stdout[count($stdout) - $i]))
|
||||
echo $stdout[count($stdout) - $i] . PHP_EOL;
|
||||
$output->writeln("<info>框架" . ($this->daemon_file["daemon"] ? "以守护进程模式" : "") . "运行中,pid:" . $this->daemon_file["pid"] . "</info>");
|
||||
if ($this->daemon_file["daemon"]) {
|
||||
$output->writeln("<comment>----- 以下是stdout内容 -----</comment>");
|
||||
$stdout = file_get_contents($this->daemon_file["stdout"]);
|
||||
$stdout = explode("\n", $stdout);
|
||||
for ($i = 15; $i > 0; --$i) {
|
||||
if (isset($stdout[count($stdout) - $i]))
|
||||
echo $stdout[count($stdout) - $i] . PHP_EOL;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ class DaemonStopCommand extends DaemonCommand
|
||||
protected static $defaultName = 'daemon:stop';
|
||||
|
||||
protected function configure() {
|
||||
$this->setDescription("停止守护进程下运行的框架(仅限--daemon模式可用)");
|
||||
$this->setDescription("停止运行的框架");
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int {
|
||||
|
||||
@@ -17,10 +17,10 @@ class SystemdGenerateCommand extends Command
|
||||
|
||||
protected function configure() {
|
||||
$this->setDescription("生成框架的 systemd 配置文件");
|
||||
ZMConfig::setDirectory(DataProvider::getSourceRootDir() . '/config');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int {
|
||||
ZMConfig::setDirectory(DataProvider::getSourceRootDir() . '/config');
|
||||
$path = $this->generate();
|
||||
$output->writeln("<info>成功生成 systemd 文件,位置:" . $path . "</info>");
|
||||
$output->writeln("<info>有关如何使用 systemd 配置文件,请访问 `https://github.com/zhamao-robot/zhamao-framework/issues/36`</info>");
|
||||
|
||||
@@ -18,8 +18,12 @@ class ModuleListCommand extends Command
|
||||
protected static $defaultName = 'module:list';
|
||||
|
||||
protected function configure() {
|
||||
$this->setDescription("Build an \".phar\" file | 将项目构建一个phar包");
|
||||
$this->setHelp("此功能将会把炸毛框架的模块打包为\".phar\",供发布和执行。");
|
||||
$this->setDescription("查看所有模块信息");
|
||||
$this->setHelp("此功能将会把炸毛框架的模块列举出来。");
|
||||
// ...
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int {
|
||||
ZMConfig::setDirectory(DataProvider::getSourceRootDir() . '/config');
|
||||
ZMConfig::setEnv($args["env"] ?? "");
|
||||
if (ZMConfig::get("global") === false) {
|
||||
@@ -27,6 +31,7 @@ class ModuleListCommand extends Command
|
||||
}
|
||||
|
||||
//定义常量
|
||||
/** @noinspection PhpIncludeInspection */
|
||||
include_once DataProvider::getFrameworkRootDir() . "/src/ZM/global_defines.php";
|
||||
|
||||
Console::init(
|
||||
@@ -38,15 +43,12 @@ class ModuleListCommand extends Command
|
||||
|
||||
$timezone = ZMConfig::get("global", "timezone") ?? "Asia/Shanghai";
|
||||
date_default_timezone_set($timezone);
|
||||
// ...
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int {
|
||||
$list = ModuleManager::getConfiguredModules();
|
||||
|
||||
foreach ($list as $v) {
|
||||
echo "[" . Console::setColor($v["name"], "green") . "]" . PHP_EOL;
|
||||
$out_list = ["类型" => "source"];
|
||||
$out_list = ["类型" => "源码(source)"];
|
||||
if (isset($v["version"])) $out_list["版本"] = $v["version"];
|
||||
if (isset($v["description"])) $out_list["描述"] = $v["description"];
|
||||
$out_list["目录"] = str_replace(DataProvider::getSourceRootDir() . "/", "", $v["module-path"]);
|
||||
@@ -58,7 +60,7 @@ class ModuleListCommand extends Command
|
||||
$list = ModuleManager::getPackedModules();
|
||||
foreach ($list as $v) {
|
||||
echo "[" . Console::setColor($v["name"], "gold") . "]" . PHP_EOL;
|
||||
$out_list = ["类型" => "archive(phar)"];
|
||||
$out_list = ["类型" => "模块包(phar)"];
|
||||
if (isset($v["module-config"]["version"])) $out_list["版本"] = $v["module-config"]["version"];
|
||||
if (isset($v["module-config"]["description"])) $out_list["描述"] = $v["module-config"]["description"];
|
||||
$out_list["位置"] = str_replace(DataProvider::getSourceRootDir() . "/", "", $v["phar-path"]);
|
||||
|
||||
@@ -21,9 +21,13 @@ class ModulePackCommand extends Command
|
||||
|
||||
protected function configure() {
|
||||
$this->addArgument("module-name", InputArgument::REQUIRED);
|
||||
$this->setDescription("Build an \".phar\" file | 将项目构建一个phar包");
|
||||
$this->setDescription("将配置好的模块构建一个phar包");
|
||||
$this->setHelp("此功能将会把炸毛框架的模块打包为\".phar\",供发布和执行。");
|
||||
$this->addOption("target", "D", InputOption::VALUE_REQUIRED, "Output Directory | 指定输出目录");
|
||||
// ...
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int {
|
||||
ZMConfig::setDirectory(DataProvider::getSourceRootDir() . '/config');
|
||||
ZMConfig::setEnv($args["env"] ?? "");
|
||||
if (ZMConfig::get("global") === false) {
|
||||
@@ -42,16 +46,13 @@ class ModulePackCommand extends Command
|
||||
|
||||
$timezone = ZMConfig::get("global", "timezone") ?? "Asia/Shanghai";
|
||||
date_default_timezone_set($timezone);
|
||||
// ...
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int {
|
||||
$list = ModuleManager::getConfiguredModules();
|
||||
if (!isset($list[$input->getArgument("module-name")])) {
|
||||
$output->writeln("<error>不存在模块 ".$input->getArgument("module-name")." !</error>");
|
||||
return 1;
|
||||
}
|
||||
$result = ModuleManager::packModule($list[$input->getArgument("module-name")]);
|
||||
$result = ModuleManager::packModule($list[$input->getArgument("module-name")], $input->getOption("target"));
|
||||
if ($result) Console::success("打包完成!");
|
||||
else Console::error("打包失败!");
|
||||
return 0;
|
||||
|
||||
@@ -22,12 +22,17 @@ class ModuleUnpackCommand extends Command
|
||||
protected function configure() {
|
||||
$this->setDefinition([
|
||||
new InputArgument("module-name", InputArgument::REQUIRED),
|
||||
new InputOption("override-light-cache", null, null, "覆盖现有的LightCache项目"),
|
||||
new InputOption("override-zm-data", null, null, "覆盖现有的zm_data文件"),
|
||||
new InputOption("override-source", null, null, "覆盖现有的源码文件")
|
||||
new InputOption("overwrite-light-cache", null, null, "覆盖现有的LightCache项目"),
|
||||
new InputOption("overwrite-zm-data", null, null, "覆盖现有的zm_data文件"),
|
||||
new InputOption("overwrite-source", null, null, "覆盖现有的源码文件"),
|
||||
new InputOption("ignore-depends", null, null, "解包时忽略检查依赖")
|
||||
]);
|
||||
$this->setDescription("Unpack a phar module into src directory");
|
||||
$this->setDescription("解包一个phar模块到src目录");
|
||||
$this->setHelp("此功能将phar格式的模块包解包到src目录下。");
|
||||
// ...
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int {
|
||||
ZMConfig::setDirectory(DataProvider::getSourceRootDir() . '/config');
|
||||
ZMConfig::setEnv($args["env"] ?? "");
|
||||
if (ZMConfig::get("global") === false) {
|
||||
@@ -35,6 +40,7 @@ class ModuleUnpackCommand extends Command
|
||||
}
|
||||
|
||||
//定义常量
|
||||
/** @noinspection PhpIncludeInspection */
|
||||
include_once DataProvider::getFrameworkRootDir()."/src/ZM/global_defines.php";
|
||||
|
||||
Console::init(
|
||||
@@ -46,10 +52,7 @@ class ModuleUnpackCommand extends Command
|
||||
|
||||
$timezone = ZMConfig::get("global", "timezone") ?? "Asia/Shanghai";
|
||||
date_default_timezone_set($timezone);
|
||||
// ...
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int {
|
||||
$list = ModuleManager::getPackedModules();
|
||||
if (!isset($list[$input->getArgument("module-name")])) {
|
||||
$output->writeln("<error>不存在打包的模块 ".$input->getArgument("module-name")." !</error>");
|
||||
|
||||
@@ -8,12 +8,14 @@ use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use ZM\Framework;
|
||||
use ZM\Utils\DataProvider;
|
||||
|
||||
class RunServerCommand extends Command
|
||||
{
|
||||
protected static $defaultName = 'server';
|
||||
|
||||
protected function configure() {
|
||||
$this->setAliases(['server:start']);
|
||||
$this->setDefinition([
|
||||
new InputOption("debug-mode", "D", null, "开启调试模式 (这将关闭协程化)"),
|
||||
new InputOption("log-debug", null, null, "调整消息等级到debug (log-level=4)"),
|
||||
@@ -47,7 +49,24 @@ class RunServerCommand extends Command
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
$pid_path = DataProvider::getWorkingDir() . "/.daemon_pid";
|
||||
if (file_exists($pid_path)) {
|
||||
$pid = json_decode(file_get_contents($pid_path), true)["pid"] ?? null;
|
||||
if ($pid !== null && posix_getsid($pid) !== false) {
|
||||
$output->writeln("<error>检测到已经在 pid: $pid 进程启动了框架!</error>");
|
||||
$output->writeln("<error>不可以同时启动两个框架!</error>");
|
||||
return 1;
|
||||
} else {
|
||||
unlink($pid_path);
|
||||
}
|
||||
}
|
||||
(new Framework($input->getOptions()))->start();
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static function exportDefinition() {
|
||||
$cmd = new self();
|
||||
$cmd->configure();
|
||||
return $cmd->getDefinition();
|
||||
}
|
||||
}
|
||||
|
||||
25
src/ZM/Command/Server/ServerReloadCommand.php
Normal file
25
src/ZM/Command/Server/ServerReloadCommand.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Command\Server;
|
||||
|
||||
use Swoole\Process;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use ZM\Command\Daemon\DaemonCommand;
|
||||
|
||||
class ServerReloadCommand extends DaemonCommand
|
||||
{
|
||||
protected static $defaultName = 'server:reload';
|
||||
|
||||
protected function configure() {
|
||||
$this->setDescription("重载框架");
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int {
|
||||
parent::execute($input, $output);
|
||||
Process::kill(intval($this->daemon_file["pid"]), SIGUSR1);
|
||||
$output->writeln("<info>成功重载!</info>");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
34
src/ZM/Command/Server/ServerStatusCommand.php
Normal file
34
src/ZM/Command/Server/ServerStatusCommand.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace ZM\Command\Server;
|
||||
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use ZM\Command\Daemon\DaemonCommand;
|
||||
|
||||
class ServerStatusCommand extends DaemonCommand
|
||||
{
|
||||
protected static $defaultName = 'server:status';
|
||||
|
||||
protected function configure() {
|
||||
$this->setDescription("查看框架的运行状态");
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int {
|
||||
parent::execute($input, $output);
|
||||
$output->writeln("<info>框架" . ($this->daemon_file["daemon"] ? "以守护进程模式" : "") . "运行中,pid:" . $this->daemon_file["pid"] . "</info>");
|
||||
if ($this->daemon_file["daemon"]) {
|
||||
$output->writeln("<comment>----- 以下是stdout内容 -----</comment>");
|
||||
$stdout = file_get_contents($this->daemon_file["stdout"]);
|
||||
$stdout = explode("\n", $stdout);
|
||||
for ($i = 15; $i > 0; --$i) {
|
||||
if (isset($stdout[count($stdout) - $i]))
|
||||
echo $stdout[count($stdout) - $i] . PHP_EOL;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
{
|
||||
|
||||
}
|
||||
35
src/ZM/Command/Server/ServerStopCommand.php
Normal file
35
src/ZM/Command/Server/ServerStopCommand.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Command\Server;
|
||||
|
||||
use Swoole\Process;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use ZM\Command\Daemon\DaemonCommand;
|
||||
use ZM\Utils\DataProvider;
|
||||
|
||||
class ServerStopCommand extends DaemonCommand
|
||||
{
|
||||
protected static $defaultName = 'server:stop';
|
||||
|
||||
protected function configure() {
|
||||
$this->setDescription("停止运行的框架");
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int {
|
||||
parent::execute($input, $output);
|
||||
Process::kill(intval($this->daemon_file["pid"]), SIGTERM);
|
||||
$i = 10;
|
||||
while (file_exists(DataProvider::getWorkingDir() . "/.daemon_pid") && $i > 0) {
|
||||
sleep(1);
|
||||
--$i;
|
||||
}
|
||||
if ($i === 0) {
|
||||
$output->writeln("<error>停止失败,请检查进程pid #" . $this->daemon_file["pid"] . " 是否响应!</error>");
|
||||
} else {
|
||||
$output->writeln("<info>成功停止!</info>");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -21,15 +21,17 @@ use ZM\Command\RunServerCommand;
|
||||
use Symfony\Component\Console\Application;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use ZM\Console\Console;
|
||||
use ZM\Command\Server\ServerReloadCommand;
|
||||
use ZM\Command\Server\ServerStatusCommand;
|
||||
use ZM\Command\Server\ServerStopCommand;
|
||||
use ZM\Exception\InitException;
|
||||
|
||||
class ConsoleApplication extends Application
|
||||
{
|
||||
private static $obj = null;
|
||||
|
||||
const VERSION_ID = 415;
|
||||
const VERSION = "2.5.0";
|
||||
const VERSION_ID = 434;
|
||||
const VERSION = "2.6.6";
|
||||
|
||||
/**
|
||||
* @throws InitException
|
||||
@@ -43,11 +45,14 @@ class ConsoleApplication extends Application
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $with_default_cmd
|
||||
* @return ConsoleApplication
|
||||
* @throws InitException
|
||||
*/
|
||||
public function initEnv($with_default_cmd = ""): ConsoleApplication {
|
||||
if (defined("WORKDING_DIR")) throw new InitException();
|
||||
$this->selfCheck();
|
||||
public function initEnv(string $with_default_cmd = ""): ConsoleApplication {
|
||||
if (defined("WORKING_DIR")) throw new InitException();
|
||||
|
||||
_zm_env_check();
|
||||
|
||||
define("WORKING_DIR", getcwd());
|
||||
if (Phar::running() !== "") {
|
||||
@@ -60,34 +65,15 @@ class ConsoleApplication extends Application
|
||||
define("LOAD_MODE", is_dir(SOURCE_ROOT_DIR . "/src/ZM") ? 0 : 1);
|
||||
define("FRAMEWORK_ROOT_DIR", realpath(__DIR__ . "/../../"));
|
||||
}
|
||||
if (LOAD_MODE == 0) {
|
||||
$composer = json_decode(file_get_contents(SOURCE_ROOT_DIR . "/composer.json"), true);
|
||||
if (!isset($composer["autoload"]["psr-4"]["Module\\"])) {
|
||||
echo "框架源码模式需要在autoload文件中添加Module目录为自动加载,是否添加?[Y/n] ";
|
||||
$r = strtolower(trim(fgets(STDIN)));
|
||||
if ($r === "" || $r === "y") {
|
||||
$composer["autoload"]["psr-4"]["Module\\"] = "src/Module";
|
||||
$composer["autoload"]["psr-4"]["Custom\\"] = "src/Custom";
|
||||
$r = file_put_contents(WORKING_DIR . "/composer.json", json_encode($composer, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
|
||||
if ($r !== false) {
|
||||
echo "成功添加!请运行 composer dump-autoload !\n";
|
||||
exit(0);
|
||||
} else {
|
||||
echo zm_internal_errcode("E00006") . "添加失败!请按任意键继续!";
|
||||
fgets(STDIN);
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->addCommands([
|
||||
new DaemonStatusCommand(),
|
||||
new DaemonReloadCommand(),
|
||||
new DaemonStopCommand(),
|
||||
new RunServerCommand(), //运行主服务的指令控制器
|
||||
new ServerStatusCommand(),
|
||||
new ServerStopCommand(),
|
||||
new ServerReloadCommand(),
|
||||
new PureHttpCommand(), //纯HTTP服务器指令
|
||||
new SystemdGenerateCommand()
|
||||
]);
|
||||
@@ -119,17 +105,4 @@ class ConsoleApplication extends Application
|
||||
die(zm_internal_errcode("E00005") . "{$e->getMessage()} at {$e->getFile()}({$e->getLine()})");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动炸毛前要做的环境检查项目
|
||||
*/
|
||||
private function selfCheck(): void {
|
||||
if (!extension_loaded("swoole")) die(zm_internal_errcode("E00001") . "Can not find swoole extension.\n");
|
||||
if (version_compare(SWOOLE_VERSION, "4.5.0") == -1) die(zm_internal_errcode("E00002") . "You must install swoole version >= 4.5.0 !");
|
||||
if (version_compare(PHP_VERSION, "7.2") == -1) die(zm_internal_errcode("E00003") . "PHP >= 7.2 required.");
|
||||
if (version_compare(SWOOLE_VERSION, "4.6.7") < 0 && !extension_loaded("pcntl")) {
|
||||
Console::error(zm_internal_errcode("E00004") . "Swoole 版本必须不低于 4.6.7 或 PHP 安装加载了 pcntl 扩展!");
|
||||
die();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ use Swoole\Coroutine;
|
||||
use Swoole\Http\Request;
|
||||
use Swoole\WebSocket\Frame;
|
||||
use Swoole\WebSocket\Server;
|
||||
use ZM\Config\ZMConfig;
|
||||
use ZM\ConnectionManager\ConnectionObject;
|
||||
use ZM\ConnectionManager\ManagerGM;
|
||||
use ZM\Console\Console;
|
||||
@@ -19,6 +20,7 @@ use ZM\Exception\WaitTimeoutException;
|
||||
use ZM\Http\Response;
|
||||
use ZM\API\ZMRobot;
|
||||
use ZM\Utils\CoMessage;
|
||||
use ZM\Utils\MessageUtil;
|
||||
|
||||
class Context implements ContextInterface
|
||||
{
|
||||
@@ -56,8 +58,8 @@ class Context implements ContextInterface
|
||||
*/
|
||||
public function getResponse(): ?Response { return self::$context[$this->cid]["response"] ?? null; }
|
||||
|
||||
/** @return ConnectionObject|null|Response */
|
||||
public function getConnection() { return ManagerGM::get($this->getFd()); }
|
||||
/** @return ConnectionObject|null */
|
||||
public function getConnection(): ?ConnectionObject { return ManagerGM::get($this->getFd()); }
|
||||
|
||||
/**
|
||||
* @return int|null
|
||||
@@ -72,9 +74,20 @@ class Context implements ContextInterface
|
||||
return $conn instanceof ConnectionObject ? new ZMRobot($conn) : null;
|
||||
}
|
||||
|
||||
public function getMessage() { return self::$context[$this->cid]["data"]["message"] ?? null; }
|
||||
public function getMessage() {
|
||||
if ((ZMConfig::get("global", "onebot")["message_convert_string"] ?? true) === true && is_array($msg = $this->getOriginMessage())) {
|
||||
return MessageUtil::arrayToStr($msg);
|
||||
} else {
|
||||
return self::$context[$this->cid]["data"]["message"] ?? null;
|
||||
}
|
||||
}
|
||||
|
||||
public function setMessage($msg) { self::$context[$this->cid]["data"]["message"] = $msg; }
|
||||
public function setMessage($msg) {
|
||||
if (is_string($msg) && is_array($this->getOriginMessage())) {
|
||||
$msg = MessageUtil::strToArray($msg);
|
||||
}
|
||||
self::$context[$this->cid]["data"]["message"] = $msg;
|
||||
}
|
||||
|
||||
public function getUserId() { return $this->getData()["user_id"] ?? null; }
|
||||
|
||||
@@ -168,7 +181,11 @@ class Context implements ContextInterface
|
||||
if ($r === false) {
|
||||
throw new WaitTimeoutException($this, $timeout_prompt);
|
||||
}
|
||||
return $r["message"];
|
||||
if (is_array($r["message"]) && (ZMConfig::get("global", "onebot")["message_convert_string"] ?? true) === true) {
|
||||
return MessageUtil::arrayToStr($r["message"]);
|
||||
} else {
|
||||
return $r["message"];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -239,4 +256,18 @@ class Context implements ContextInterface
|
||||
public function copy() { return self::$context[$this->cid]; }
|
||||
|
||||
public function getOption() { return self::getCache("match"); }
|
||||
|
||||
public function getOriginMessage() { return self::$context[$this->cid]["data"]["message"] ?? null; }
|
||||
|
||||
public function getArrayMessage(): array {
|
||||
$msg = $this->getOriginMessage();
|
||||
if (is_array($msg)) return $msg;
|
||||
else return MessageUtil::strToArray($msg);
|
||||
}
|
||||
|
||||
public function getStringMessage(): string {
|
||||
$msg = $this->getOriginMessage();
|
||||
if (is_string($msg)) return $msg;
|
||||
else return MessageUtil::arrayToStr($msg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,9 +6,8 @@
|
||||
namespace ZM\DB;
|
||||
|
||||
|
||||
use Exception;
|
||||
use ZM\Config\ZMConfig;
|
||||
use ZM\Console\Console;
|
||||
use ZM\MySQL\MySQLManager;
|
||||
use ZM\Store\MySQL\SqlPoolStorage;
|
||||
use PDOException;
|
||||
use PDOStatement;
|
||||
@@ -25,12 +24,12 @@ class DB
|
||||
private static $table_list = [];
|
||||
|
||||
/**
|
||||
* @param $db_name
|
||||
* @throws DbException
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function initTableList() {
|
||||
if (!extension_loaded("mysqlnd")) throw new Exception("Can not find mysqlnd PHP extension.");
|
||||
$result = self::rawQuery("select TABLE_NAME from INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA='" . ZMConfig::get("global", "sql_config")["sql_database"] . "';", []);
|
||||
public static function initTableList($db_name) {
|
||||
if (!extension_loaded("mysqlnd")) throw new DbException("Can not find mysqlnd PHP extension.");
|
||||
$result = MySQLManager::getWrapper()->fetchAllAssociative("select TABLE_NAME from INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA=?;", [$db_name]);
|
||||
foreach ($result as $v) {
|
||||
self::$table_list[] = $v['TABLE_NAME'];
|
||||
}
|
||||
@@ -94,6 +93,7 @@ class DB
|
||||
if (!is_array($params)) $params = [$params];
|
||||
Console::debug("MySQL: " . $line . " | " . implode(", ", $params));
|
||||
try {
|
||||
if (SqlPoolStorage::$sql_pool === null) throw new DbException("未连接到任何数据库!");
|
||||
$conn = SqlPoolStorage::$sql_pool->getConnection();
|
||||
if ($conn === false) {
|
||||
SqlPoolStorage::$sql_pool->putConnection(null);
|
||||
|
||||
@@ -4,9 +4,11 @@
|
||||
namespace ZM\Event;
|
||||
|
||||
|
||||
use Closure;
|
||||
use Doctrine\Common\Annotations\AnnotationException;
|
||||
use Error;
|
||||
use Exception;
|
||||
use ZM\Config\ZMConfig;
|
||||
use ZM\Console\Console;
|
||||
use ZM\Exception\InterruptException;
|
||||
use ZM\Store\LightCacheInside;
|
||||
@@ -70,11 +72,13 @@ class EventDispatcher
|
||||
}
|
||||
|
||||
public function setRuleFunction(callable $rule = null): EventDispatcher {
|
||||
if ($this->log) Console::verbose("[事件分发{$this->eid}] 设置事件rule: " . $this->class);
|
||||
$this->rule = $rule;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setReturnFunction(callable $return_func): EventDispatcher {
|
||||
if ($this->log) Console::verbose("[事件分发{$this->eid}] 设置事件returnFunc: " . $this->class);
|
||||
$this->return_func = $return_func;
|
||||
return $this;
|
||||
}
|
||||
@@ -87,7 +91,7 @@ class EventDispatcher
|
||||
try {
|
||||
foreach ((EventManager::$events[$this->class] ?? []) as $v) {
|
||||
$this->dispatchEvent($v, $this->rule, ...$params);
|
||||
if ($this->log) Console::verbose("[事件分发{$this->eid}] 单一对象 " . $v->class . "::" . $v->method . " 分发结束。");
|
||||
if ($this->log) Console::verbose("[事件分发{$this->eid}] 单一对象 " . $v->class . "::" . (is_string($v->method) ? $v->method : "{closure}") . " 分发结束。");
|
||||
if ($this->status == self::STATUS_BEFORE_FAILED || $this->status == self::STATUS_RULE_FAILED) continue;
|
||||
if (is_callable($this->return_func) && $this->status === self::STATUS_NORMAL) {
|
||||
if ($this->log) Console::verbose("[事件分发{$this->eid}] 单一对象 " . $v->class . "::" . $v->method . " 正在执行返回值处理函数 ...");
|
||||
@@ -117,7 +121,7 @@ class EventDispatcher
|
||||
public function dispatchEvent($v, $rule_func = null, ...$params) {
|
||||
$q_c = $v->class;
|
||||
$q_f = $v->method;
|
||||
if ($q_c === "" && ($q_f instanceof \Closure)) {
|
||||
if (($q_c ?? "") === "" && ($q_f instanceof Closure)) {
|
||||
if ($this->log) Console::verbose("[事件分发{$this->eid}] 闭包函数的事件触发过程!");
|
||||
if ($rule_func !== null && !$rule_func($v)) {
|
||||
if ($this->log) Console::verbose("[事件分发{$this->eid}] 闭包函数下的 ruleFunc 判断为 false, 拒绝执行此方法。");
|
||||
@@ -137,11 +141,16 @@ class EventDispatcher
|
||||
if ($this->log) Console::verbose("[事件分发{$this->eid}] " . $q_c . "::" . $q_f . " 方法下的 ruleFunc 为真,继续执行方法本身 ...");
|
||||
if (isset(EventManager::$middleware_map[$q_c][$q_f])) {
|
||||
$middlewares = EventManager::$middleware_map[$q_c][$q_f];
|
||||
if ($this->log) Console::verbose("[事件分发{$this->eid}] " . $q_c . "::" . $q_f . " 方法还绑定了 Middleware:" . implode(", ", array_map(function($x){ return $x->middleware; }, $middlewares)));
|
||||
if ($this->log) Console::verbose("[事件分发{$this->eid}] " . $q_c . "::" . $q_f . " 方法还绑定了 Middleware:" . implode(", ", array_map(function ($x) { return $x->middleware; }, $middlewares)));
|
||||
$before_result = true;
|
||||
$r = [];
|
||||
foreach ($middlewares as $k => $middleware) {
|
||||
if (!isset(EventManager::$middlewares[$middleware->middleware])) throw new AnnotationException("Annotation parse error: Unknown MiddlewareClass named \"{$middleware->middleware}\"!");
|
||||
if (!isset(EventManager::$middlewares[$middleware->middleware])) {
|
||||
if ((ZMConfig::get("global", "runtime")["middleware_error_policy"] ?? 1) == 1)
|
||||
throw new AnnotationException("Annotation parse error: Unknown MiddlewareClass named \"{$middleware->middleware}\"!");
|
||||
else
|
||||
continue;
|
||||
}
|
||||
$middleware_obj = EventManager::$middlewares[$middleware->middleware];
|
||||
$before = $middleware_obj["class"];
|
||||
//var_dump($middleware_obj);
|
||||
@@ -149,6 +158,7 @@ class EventDispatcher
|
||||
$r[$k]->class = $q_c;
|
||||
$r[$k]->method = $q_f;
|
||||
$r[$k]->middleware = $middleware;
|
||||
$r[$k]->current_event = $v;
|
||||
if (isset($middleware_obj["before"])) {
|
||||
if ($this->log) Console::verbose("[事件分发{$this->eid}] Middleware 存在前置事件,执行中 ...");
|
||||
$rs = $middleware_obj["before"];
|
||||
@@ -186,7 +196,8 @@ class EventDispatcher
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
for ($i = count($middlewares) - 1; $i >= 0; --$i) {
|
||||
$cnts = count($middlewares) - 1;
|
||||
for ($i = $cnts; $i >= 0; --$i) {
|
||||
$middleware_obj = EventManager::$middlewares[$middlewares[$i]->middleware];
|
||||
if (isset($middleware_obj["after"], $r[$i])) {
|
||||
if ($this->log) Console::verbose("[事件分发{$this->eid}] Middleware 存在后置事件,执行中 ...");
|
||||
|
||||
@@ -12,6 +12,7 @@ use ZM\Annotation\AnnotationParser;
|
||||
use ZM\Annotation\Swoole\OnTick;
|
||||
use ZM\Config\ZMConfig;
|
||||
use ZM\Console\Console;
|
||||
use ZM\Exception\AnnotationException;
|
||||
use ZM\Store\LightCache;
|
||||
use ZM\Store\ZMAtomic;
|
||||
|
||||
@@ -23,15 +24,24 @@ class EventManager
|
||||
public static $req_mapping = [];
|
||||
|
||||
public static function addEvent($event_name, ?AnnotationBase $event_obj) {
|
||||
if ($event_obj->method instanceof \Closure) {
|
||||
Console::debug("Adding event $event_name at @Anonymous");
|
||||
} else {
|
||||
Console::debug("Adding event $event_name at " . ($event_obj->class) . ":" . ($event_obj->method));
|
||||
}
|
||||
self::$events[$event_name][] = $event_obj;
|
||||
(new AnnotationParser())->sortByLevel(self::$events, $event_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws AnnotationException
|
||||
*/
|
||||
public static function loadEventByParser(AnnotationParser $parser) {
|
||||
self::$events = $parser->generateAnnotationEvents();
|
||||
self::$middlewares = $parser->getMiddlewares();
|
||||
self::$middleware_map = $parser->getMiddlewareMap();
|
||||
self::$req_mapping = $parser->getReqMapping();
|
||||
$parser->verifyMiddlewares();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace ZM\Event\SwooleEvent;
|
||||
use Swoole\Process;
|
||||
use Swoole\WebSocket\Server;
|
||||
use ZM\Annotation\Swoole\SwooleHandler;
|
||||
use ZM\ConnectionManager\ManagerGM;
|
||||
use ZM\Config\ZMConfig;
|
||||
use ZM\Console\Console;
|
||||
use ZM\Event\SwooleEvent;
|
||||
|
||||
@@ -23,11 +23,7 @@ class OnBeforeReload implements SwooleEvent
|
||||
for ($i = 0; $i < ZM_WORKER_NUM; ++$i) {
|
||||
Process::kill(zm_atomic("_#worker_" . $i)->get(), SIGUSR1);
|
||||
}
|
||||
foreach ($server->connections as $fd) {
|
||||
if (ManagerGM::get($fd) !== null) $server->disconnect($fd);
|
||||
else $server->close($fd);
|
||||
}
|
||||
|
||||
usleep(800 * 1000);
|
||||
$conf = ZMConfig::get("global", "runtime")["reload_delay_time"] ?? 800;
|
||||
if ($conf !== 0) usleep($conf * 1000);
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,9 @@ use ZM\Event\SwooleEvent;
|
||||
*/
|
||||
class OnMessage implements SwooleEvent
|
||||
{
|
||||
/**
|
||||
* @noinspection PhpUnreachableStatementInspection
|
||||
*/
|
||||
public function onCall($server, Frame $frame) {
|
||||
Console::debug("Calling Swoole \"message\" from fd=" . $frame->fd . ": " . TermColor::ITALIC . $frame->data . TermColor::RESET);
|
||||
unset(Context::$context[Coroutine::getCid()]);
|
||||
@@ -32,8 +35,8 @@ class OnMessage implements SwooleEvent
|
||||
set_coroutine_params(["server" => $server, "frame" => $frame, "connection" => $conn]);
|
||||
$dispatcher1 = new EventDispatcher(OnMessageEvent::class);
|
||||
$dispatcher1->setRuleFunction(function ($v) {
|
||||
/** @noinspection PhpUnreachableStatementInspection */
|
||||
return ctx()->getConnection()->getName() == $v->connect_type && eval("return " . $v->getRule() . ";");
|
||||
if ($v->getRule() == '') return true;
|
||||
else return eval("return " . $v->getRule() . ";");
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ class OnOpen implements SwooleEvent
|
||||
try {
|
||||
$obb_onebot = ZMConfig::get("global", "onebot") ??
|
||||
ZMConfig::get("global", "modules")["onebot"] ??
|
||||
["status" => true, "single_bot_mode" => false, "message_level" => 99999];
|
||||
["status" => true, "single_bot_mode" => false, "message_level" => 99];
|
||||
$onebot_status = $obb_onebot["status"];
|
||||
if ($conn->getName() === 'qq' && $onebot_status === true) {
|
||||
if ($obb_onebot["single_bot_mode"]) {
|
||||
|
||||
@@ -8,6 +8,7 @@ use Swoole\Server;
|
||||
use ZM\Annotation\Swoole\SwooleHandler;
|
||||
use ZM\Console\Console;
|
||||
use ZM\Event\SwooleEvent;
|
||||
use ZM\Utils\DataProvider;
|
||||
|
||||
/**
|
||||
* Class OnShutdown
|
||||
@@ -18,5 +19,9 @@ class OnShutdown implements SwooleEvent
|
||||
{
|
||||
public function onCall(Server $server) {
|
||||
Console::verbose("正在关闭 Master 进程,pid=" . posix_getpid());
|
||||
$pid_path = DataProvider::getWorkingDir() . "/.daemon_pid";
|
||||
if (file_exists($pid_path)) {
|
||||
unlink($pid_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,13 +25,12 @@ class OnStart implements SwooleEvent
|
||||
if (!Framework::$argv["disable-safe-exit"]) {
|
||||
SignalListener::signalMaster($server);
|
||||
}
|
||||
if (Framework::$argv["daemon"]) {
|
||||
$daemon_data = json_encode([
|
||||
"pid" => $server->master_pid,
|
||||
"stdout" => ZMConfig::get("global")["swoole"]["log_file"]
|
||||
], 128 | 256);
|
||||
file_put_contents(DataProvider::getWorkingDir() . "/.daemon_pid", $daemon_data);
|
||||
}
|
||||
$daemon_data = json_encode([
|
||||
"pid" => $server->master_pid,
|
||||
"stdout" => ZMConfig::get("global")["swoole"]["log_file"],
|
||||
"daemon" => (bool)Framework::$argv["daemon"]
|
||||
], 128 | 256);
|
||||
file_put_contents(DataProvider::getWorkingDir() . "/.daemon_pid", $daemon_data);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ use Swoole\Database\PDOConfig;
|
||||
use Swoole\Process;
|
||||
use Swoole\Server;
|
||||
use ZM\Annotation\AnnotationParser;
|
||||
use ZM\Annotation\Swoole\OnMessageEvent;
|
||||
use ZM\Annotation\Swoole\OnStart;
|
||||
use ZM\Annotation\Swoole\OnSwooleEvent;
|
||||
use ZM\Annotation\Swoole\SwooleHandler;
|
||||
@@ -24,7 +25,7 @@ use ZM\Event\EventDispatcher;
|
||||
use ZM\Event\EventManager;
|
||||
use ZM\Event\SwooleEvent;
|
||||
use ZM\Exception\DbException;
|
||||
use ZM\Exception\ZMException;
|
||||
use ZM\Exception\ZMKnownException;
|
||||
use ZM\Framework;
|
||||
use ZM\Module\QQBot;
|
||||
use ZM\MySQL\MySQLPool;
|
||||
@@ -32,6 +33,7 @@ use ZM\Store\LightCacheInside;
|
||||
use ZM\Store\MySQL\SqlPoolStorage;
|
||||
use ZM\Store\Redis\ZMRedisPool;
|
||||
use ZM\Utils\DataProvider;
|
||||
use ZM\Utils\Manager\ModuleManager;
|
||||
use ZM\Utils\SignalListener;
|
||||
|
||||
/**
|
||||
@@ -48,7 +50,6 @@ class OnWorkerStart implements SwooleEvent
|
||||
}
|
||||
unset(Context::$context[Coroutine::getCid()]);
|
||||
if ($server->taskworker === false) {
|
||||
|
||||
zm_atomic("_#worker_" . $worker_id)->set($server->worker_pid);
|
||||
if (LightCacheInside::get("wait_api", "wait_api") !== null) {
|
||||
LightCacheInside::unset("wait_api", "wait_api");
|
||||
@@ -131,7 +132,7 @@ class OnWorkerStart implements SwooleEvent
|
||||
* @throws Exception
|
||||
*/
|
||||
private function loadAnnotations() {
|
||||
|
||||
if (Framework::$instant_mode) goto skip;
|
||||
//加载各个模块的注解类,以及反射
|
||||
Console::debug("Mapping annotations");
|
||||
$parser = new AnnotationParser();
|
||||
@@ -144,38 +145,48 @@ class OnWorkerStart implements SwooleEvent
|
||||
$parser->addRegisterPath(DataProvider::getSourceRootDir() . "/" . $v . "/", trim($k, "\\"));
|
||||
}
|
||||
}
|
||||
|
||||
// 检查是否允许热加载phar模块,允许的话将遍历phar内的文件
|
||||
$plugin_enable_hotload = ZMConfig::get("global", "module_loader")["enable_hotload"] ?? false;
|
||||
if ($plugin_enable_hotload) {
|
||||
$list = ModuleManager::getPackedModules();
|
||||
foreach($list as $k => $v) {
|
||||
if (\server()->worker_id === 0) Console::info("Loading packed module: ".$k);
|
||||
require_once $v["phar-path"];
|
||||
$func = "loader".$v["generated-id"];
|
||||
$func();
|
||||
$parser->addRegisterPath("phar://".$v["phar-path"]."/".$v["module-root-path"], $v["namespace"]);
|
||||
}
|
||||
}
|
||||
|
||||
$parser->registerMods();
|
||||
EventManager::loadEventByParser($parser); //加载事件
|
||||
|
||||
skip:
|
||||
//加载自定义的全局函数
|
||||
Console::debug("Loading context class...");
|
||||
$context_class = ZMConfig::get("global", "context_class");
|
||||
if (!is_a($context_class, ContextInterface::class, true)) {
|
||||
throw new ZMException(zm_internal_errcode("E00032") . "Context class must implemented from ContextInterface!");
|
||||
throw new ZMKnownException("E00032", "Context class must implemented from ContextInterface!");
|
||||
}
|
||||
|
||||
//加载插件
|
||||
$obb_onebot = ZMConfig::get("global", "onebot") ??
|
||||
ZMConfig::get("global", "modules")["onebot"] ??
|
||||
["status" => true, "single_bot_mode" => false, "message_level" => 99999];
|
||||
|
||||
if ($obb_onebot["status"]) {
|
||||
Console::debug("OneBot support enabled, listening OneBot event(3).");
|
||||
$obj = new OnSwooleEvent();
|
||||
$obj = new OnMessageEvent();
|
||||
$obj->class = QQBot::class;
|
||||
$obj->method = 'handleByEvent';
|
||||
$obj->type = 'message';
|
||||
$obj->level = $obb_onebot["message_level"] ?? 99999;
|
||||
$obj->level = $obb_onebot["message_level"] ?? 99;
|
||||
$obj->rule = 'connectIsQQ()';
|
||||
EventManager::addEvent(OnSwooleEvent::class, $obj);
|
||||
EventManager::addEvent(OnMessageEvent::class, $obj);
|
||||
if ($obb_onebot["single_bot_mode"]) {
|
||||
LightCacheInside::set("connect", "conn_fd", -1);
|
||||
} else {
|
||||
LightCacheInside::set("connect", "conn_fd", -2);
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: 编写加载外部插件的方式
|
||||
}
|
||||
|
||||
private function initMySQLPool() {
|
||||
@@ -233,7 +244,7 @@ class OnWorkerStart implements SwooleEvent
|
||||
->withPassword($real_conf["password"])
|
||||
->withOptions($real_conf["options"] ?? [PDO::ATTR_STRINGIFY_FETCHES => false])
|
||||
);
|
||||
DB::initTableList();
|
||||
DB::initTableList($real_conf["dbname"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
10
src/ZM/Exception/AnnotationException.php
Normal file
10
src/ZM/Exception/AnnotationException.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Exception;
|
||||
|
||||
|
||||
class AnnotationException extends \Doctrine\Common\Annotations\AnnotationException
|
||||
{
|
||||
|
||||
}
|
||||
@@ -6,9 +6,9 @@ namespace ZM\Exception;
|
||||
|
||||
use Throwable;
|
||||
|
||||
class DbException extends ZMException
|
||||
class DbException extends ZMKnownException
|
||||
{
|
||||
public function __construct($message = "", $code = 0, Throwable $previous = null) {
|
||||
parent::__construct(zm_internal_errcode("E00043") . $message, $code, $previous);
|
||||
parent::__construct("E00043", $message, $code, $previous);
|
||||
}
|
||||
}
|
||||
|
||||
14
src/ZM/Exception/LightCacheException.php
Normal file
14
src/ZM/Exception/LightCacheException.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Exception;
|
||||
|
||||
|
||||
use Throwable;
|
||||
|
||||
class LightCacheException extends ZMKnownException
|
||||
{
|
||||
public function __construct($err_code, $message = "", $code = 0, Throwable $previous = null) {
|
||||
parent::__construct($err_code, $message, $code, $previous);
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ namespace ZM\Exception;
|
||||
|
||||
use Exception;
|
||||
|
||||
class ZMException extends Exception
|
||||
abstract class ZMException extends Exception
|
||||
{
|
||||
|
||||
}
|
||||
14
src/ZM/Exception/ZMKnownException.php
Normal file
14
src/ZM/Exception/ZMKnownException.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Exception;
|
||||
|
||||
|
||||
use Throwable;
|
||||
|
||||
class ZMKnownException extends ZMException
|
||||
{
|
||||
public function __construct($err_code, $message = "", $code = 0, Throwable $previous = null) {
|
||||
parent::__construct(zm_internal_errcode($err_code) . $message, $code, $previous);
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,33 @@ use ZM\Annotation\Swoole\SwooleHandler;
|
||||
use ZM\Console\Console;
|
||||
use ZM\Utils\Terminal;
|
||||
use ZM\Utils\ZMUtil;
|
||||
use function date_default_timezone_set, define;
|
||||
use function exec;
|
||||
use function explode;
|
||||
use function file_exists;
|
||||
use function file_get_contents;
|
||||
use function file_put_contents;
|
||||
use function get_class;
|
||||
use function get_included_files;
|
||||
use function in_array;
|
||||
use function intval;
|
||||
use function is_array;
|
||||
use function is_dir;
|
||||
use function json_decode;
|
||||
use function json_encode;
|
||||
use function mkdir;
|
||||
use function ob_get_clean;
|
||||
use function ob_start;
|
||||
use function posix_getpid;
|
||||
use function str_pad;
|
||||
use function strlen;
|
||||
use function substr;
|
||||
use function swoole_cpu_num;
|
||||
use function trim;
|
||||
use function uuidgen;
|
||||
use function working_dir;
|
||||
use function zm_atomic;
|
||||
use function zm_internal_errcode;
|
||||
|
||||
class Framework
|
||||
{
|
||||
@@ -49,9 +76,11 @@ class Framework
|
||||
*/
|
||||
private $setup_events = [];
|
||||
|
||||
public function __construct($args = []) {
|
||||
$tty_width = $this->getTtyWidth();
|
||||
public static $instant_mode = false;
|
||||
|
||||
public function __construct($args = [], $instant_mode = false) {
|
||||
$tty_width = $this->getTtyWidth();
|
||||
self::$instant_mode = $instant_mode;
|
||||
self::$argv = $args;
|
||||
ZMConfig::setDirectory(DataProvider::getSourceRootDir() . '/config');
|
||||
ZMConfig::setEnv($args["env"] ?? "");
|
||||
@@ -89,6 +118,9 @@ class Framework
|
||||
$args["log-theme"] ?? "default",
|
||||
($o = ZMConfig::get("console_color")) === false ? [] : $o
|
||||
);
|
||||
if ((ZMConfig::get("global", "runtime")["save_console_log_file"] ?? false) !== false) {
|
||||
Console::setOutputFile(ZMConfig::get("global", "runtime")["save_console_log_file"]);
|
||||
}
|
||||
|
||||
$timezone = ZMConfig::get("global", "timezone") ?? "Asia/Shanghai";
|
||||
date_default_timezone_set($timezone);
|
||||
@@ -97,7 +129,9 @@ class Framework
|
||||
$this->server_set["log_level"] = SWOOLE_LOG_DEBUG;
|
||||
$add_port = ZMConfig::get("global", "remote_terminal")["status"] ?? false;
|
||||
|
||||
$this->loadServerEvents();
|
||||
if ($instant_mode) {
|
||||
$this->loadServerEvents();
|
||||
}
|
||||
|
||||
$this->parseCliArgs(self::$argv, $add_port);
|
||||
|
||||
@@ -121,7 +155,7 @@ class Framework
|
||||
} else {
|
||||
$out["worker"] = $this->server_set["worker_num"];
|
||||
}
|
||||
$out["environment"] = $args["env"] === null ? "default" : $args["env"];
|
||||
$out["environment"] = ($args["env"] ?? null) === null ? "default" : $args["env"];
|
||||
$out["log_level"] = Console::getLevel();
|
||||
$out["version"] = ZM_VERSION . (LOAD_MODE == 0 ? (" (build " . ZM_VERSION_ID . ")") : "");
|
||||
$out["master_pid"] = posix_getpid();
|
||||
@@ -129,7 +163,7 @@ class Framework
|
||||
if (isset($this->server_set["task_worker_num"])) {
|
||||
$out["task_worker"] = $this->server_set["task_worker_num"];
|
||||
}
|
||||
if (ZMConfig::get("global", "sql_config")["sql_host"] !== "") {
|
||||
if ((ZMConfig::get("global", "sql_config")["sql_host"] ?? "") !== "") {
|
||||
$conf = ZMConfig::get("global", "sql_config");
|
||||
$out["mysql_pool"] = $conf["sql_database"] . "@" . $conf["sql_host"] . ":" . $conf["sql_port"];
|
||||
}
|
||||
@@ -155,7 +189,7 @@ class Framework
|
||||
}
|
||||
|
||||
self::printProps($out, $tty_width, $args["log-theme"] === null);
|
||||
if ($args["preview"]) {
|
||||
if ($args["preview"] ?? false) {
|
||||
exit();
|
||||
}
|
||||
|
||||
@@ -305,9 +339,7 @@ class Framework
|
||||
$motd = file_get_contents(__DIR__ . "/../../config/motd.txt");
|
||||
}
|
||||
$motd = explode("\n", $motd);
|
||||
foreach ($motd as $k => $v) {
|
||||
$motd[$k] = substr($v, 0, $tty_width);
|
||||
}
|
||||
foreach ($motd as $k => $v) $motd[$k] = substr($v, 0, $tty_width);
|
||||
$motd = implode("\n", $motd);
|
||||
echo $motd;
|
||||
}
|
||||
@@ -411,6 +443,7 @@ class Framework
|
||||
$coroutine_mode = false;
|
||||
break;
|
||||
case 'debug-mode':
|
||||
self::$argv["disable-safe-exit"] = true;
|
||||
$coroutine_mode = false;
|
||||
$terminal_id = null;
|
||||
self::$argv["watch"] = true;
|
||||
@@ -457,7 +490,7 @@ class Framework
|
||||
}
|
||||
}
|
||||
$global_hook = ZMConfig::get("global", 'runtime')['swoole_coroutine_hook_flags'] ?? (SWOOLE_HOOK_ALL & (~SWOOLE_HOOK_CURL));
|
||||
if ($coroutine_mode && $global_hook === false) Runtime::enableCoroutine(true, $global_hook);
|
||||
if ($coroutine_mode) Runtime::enableCoroutine(true, $global_hook);
|
||||
else Runtime::enableCoroutine(false, SWOOLE_HOOK_ALL);
|
||||
}
|
||||
|
||||
|
||||
18
src/ZM/Module/InstantModule.php
Normal file
18
src/ZM/Module/InstantModule.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace ZM\Module;
|
||||
|
||||
/**
|
||||
* @since 2.6
|
||||
*/
|
||||
class InstantModule extends ModuleBase
|
||||
{
|
||||
public function onEvent($event_class, $params, callable $callable) {
|
||||
$class = new $event_class();
|
||||
foreach ($params as $k => $v) {
|
||||
if (is_string($k)) $class->$k = $v;
|
||||
}
|
||||
$class->method = $callable;
|
||||
$this->events[] = $class;
|
||||
}
|
||||
}
|
||||
31
src/ZM/Module/ModuleBase.php
Normal file
31
src/ZM/Module/ModuleBase.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace ZM\Module;
|
||||
|
||||
/**
|
||||
* @since 2.6
|
||||
*/
|
||||
abstract class ModuleBase
|
||||
{
|
||||
protected $module_name;
|
||||
|
||||
protected $events = [];
|
||||
|
||||
public function __construct($module_name) {
|
||||
$this->module_name = $module_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getModuleName() {
|
||||
return $this->module_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getEvents(): array {
|
||||
return $this->events;
|
||||
}
|
||||
}
|
||||
@@ -54,6 +54,7 @@ class ModulePacker
|
||||
*/
|
||||
public function setOutputPath($path) {
|
||||
$this->output_path = $path;
|
||||
if (!is_dir($path)) mkdir($path, 0755, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -122,7 +123,13 @@ class ModulePacker
|
||||
}
|
||||
|
||||
private function generatePharAutoload(): array {
|
||||
return ZMUtil::getClassesPsr4($this->module['module-path'], $this->module['namespace'], null, true);
|
||||
$pos = strpos($this->module['module-path'], DataProvider::getSourceRootDir().'/');
|
||||
if ($pos === 0) {
|
||||
$path_value = substr($this->module['module-path'], strlen(DataProvider::getSourceRootDir().'/'));
|
||||
} else {
|
||||
throw new ModulePackException(zm_internal_errcode("E99999")); //未定义的错误
|
||||
}
|
||||
return ZMUtil::getClassesPsr4($this->module['module-path'], $this->module['namespace'], null, $path_value);
|
||||
}
|
||||
|
||||
private function getComposerAutoloadItems(): array {
|
||||
|
||||
@@ -32,13 +32,14 @@ class ModuleUnpacker
|
||||
* @param bool $override_light_cache
|
||||
* @param bool $override_data_files
|
||||
* @param bool $override_source
|
||||
* @param bool $ignore_depends
|
||||
* @return array
|
||||
* @throws ModulePackException
|
||||
* @throws ZMException
|
||||
*/
|
||||
public function unpack(bool $override_light_cache = false, bool $override_data_files = false, bool $override_source = false): array {
|
||||
public function unpack(bool $override_light_cache = false, bool $override_data_files = false, bool $override_source = false, $ignore_depends = false): array {
|
||||
$this->checkConfig();
|
||||
$this->checkDepends();
|
||||
$this->checkDepends($ignore_depends);
|
||||
$this->checkLightCacheStore();
|
||||
$this->checkZMDataStore();
|
||||
|
||||
@@ -62,18 +63,19 @@ class ModuleUnpacker
|
||||
|
||||
/**
|
||||
* 检查模块依赖关系
|
||||
* @param bool $ignore_depends
|
||||
* @throws ModulePackException
|
||||
* @throws ZMException
|
||||
*/
|
||||
private function checkDepends() {
|
||||
private function checkDepends($ignore_depends = false) {
|
||||
$configured = ModuleManager::getConfiguredModules();
|
||||
$depends = $this->module_config["depends"] ?? [];
|
||||
foreach ($depends as $k => $v) {
|
||||
if (!isset($configured[$k])) {
|
||||
if (!isset($configured[$k]) && !$ignore_depends) {
|
||||
throw new ModulePackException(zm_internal_errcode("E00064") . "模块 " . $this->module_config["name"] . " 依赖的模块 $k 不存在");
|
||||
}
|
||||
$current_ver = $configured[$k]["version"] ?? "1.0";
|
||||
if (!VersionComparator::compareVersionRange($current_ver, $v)) {
|
||||
if (!VersionComparator::compareVersionRange($current_ver, $v) && !$ignore_depends) {
|
||||
throw new ModulePackException(zm_internal_errcode("E00063") . "模块 " . $this->module_config["name"] . " 依赖的模块 $k 版本依赖不符合条件(现有版本: " . $current_ver . ", 需求版本: " . $v . ")");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
namespace ZM\Module;
|
||||
|
||||
use Exception;
|
||||
use ZM\Annotation\CQ\CQAfter;
|
||||
use ZM\Annotation\CQ\CQAPIResponse;
|
||||
use ZM\Annotation\CQ\CQBefore;
|
||||
use ZM\Annotation\CQ\CQCommand;
|
||||
@@ -11,6 +12,7 @@ use ZM\Annotation\CQ\CQMessage;
|
||||
use ZM\Annotation\CQ\CQMetaEvent;
|
||||
use ZM\Annotation\CQ\CQNotice;
|
||||
use ZM\Annotation\CQ\CQRequest;
|
||||
use ZM\Config\ZMConfig;
|
||||
use ZM\Event\EventDispatcher;
|
||||
use ZM\Exception\InterruptException;
|
||||
use ZM\Exception\WaitTimeoutException;
|
||||
@@ -59,9 +61,25 @@ class QQBot
|
||||
}
|
||||
if (isset($data["post_type"])) $this->dispatchEvents($data);
|
||||
else $this->dispatchAPIResponse($data);
|
||||
|
||||
if (($data["post_type"] ?? "meta_event") != "meta_event") {
|
||||
$r = $this->dispatchAfterEvents($data); // before在这里执行,元事件不执行before为减少不必要的调试日志
|
||||
if ($r->store === "block") EventDispatcher::interrupt();
|
||||
}
|
||||
} /** @noinspection PhpRedundantCatchClauseInspection */ catch (WaitTimeoutException $e) {
|
||||
if (($data["post_type"] ?? "meta_event") != "meta_event") {
|
||||
$r = $this->dispatchAfterEvents($data); // before在这里执行,元事件不执行before为减少不必要的调试日志
|
||||
if ($r->store === "block") EventDispatcher::interrupt();
|
||||
}
|
||||
$e->module->finalReply($e->getMessage());
|
||||
} catch (InterruptException $e) {
|
||||
if (($data["post_type"] ?? "meta_event") != "meta_event") {
|
||||
$r = $this->dispatchAfterEvents($data); // before在这里执行,元事件不执行before为减少不必要的调试日志
|
||||
if ($r->store === "block") EventDispatcher::interrupt();
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
// 这里修复CQAfter不能使用的问题,我竟然一直没写,绝了
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -73,11 +91,11 @@ class QQBot
|
||||
public function dispatchBeforeEvents($data, $time): EventDispatcher {
|
||||
$before = new EventDispatcher(CQBefore::class);
|
||||
if ($time === "pre") {
|
||||
$before->setRuleFunction(function($v) use ($data){
|
||||
$before->setRuleFunction(function ($v) use ($data) {
|
||||
return $v->level >= 200 && $v->cq_event == $data["post_type"];
|
||||
});
|
||||
} else {
|
||||
$before->setRuleFunction(function($v) use ($data){
|
||||
$before->setRuleFunction(function ($v) use ($data) {
|
||||
return $v->level < 200 && $v->cq_event == $data["post_type"];
|
||||
});
|
||||
}
|
||||
@@ -103,22 +121,33 @@ class QQBot
|
||||
if (is_string($result)) ctx()->reply($result);
|
||||
if (ctx()->getCache("has_reply") === true) EventDispatcher::interrupt();
|
||||
});
|
||||
$s = MessageUtil::matchCommand(ctx()->getMessage(), ctx()->getData());
|
||||
$s = MessageUtil::matchCommand(ctx()->getStringMessage(), ctx()->getData());
|
||||
if ($s->status !== false) {
|
||||
if (!empty($s->match)) ctx()->setCache("match", $s->match);
|
||||
$dispatcher->dispatchEvent($s->object, null);
|
||||
if (is_string($dispatcher->store)) ctx()->reply($dispatcher->store);
|
||||
if (ctx()->getCache("has_reply") === true) EventDispatcher::interrupt();
|
||||
if (ctx()->getCache("has_reply") === true) {
|
||||
$policy = ZMConfig::get("global", "onebot")['message_command_policy'] ?? 'interrupt';
|
||||
switch ($policy) {
|
||||
case 'interrupt':
|
||||
EventDispatcher::interrupt();
|
||||
break;
|
||||
case 'continue':
|
||||
break;
|
||||
default:
|
||||
throw new Exception("未知的消息命令策略:" . $policy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//分发CQMessage事件
|
||||
$msg_dispatcher = new EventDispatcher(CQMessage::class);
|
||||
$msg_dispatcher->setRuleFunction(function ($v) {
|
||||
return ($v->message == '' || ($v->message != '' && $v->message == context()->getData()["message"])) &&
|
||||
($v->user_id == 0 || ($v->user_id != 0 && $v->user_id == context()->getData()["user_id"])) &&
|
||||
($v->group_id == 0 || ($v->group_id != 0 && $v->group_id == (context()->getData()["group_id"] ?? 0))) &&
|
||||
($v->message_type == '' || ($v->message_type != '' && $v->message_type == context()->getData()["message_type"])) &&
|
||||
($v->raw_message == '' || ($v->raw_message != '' && $v->raw_message == context()->getData()["raw_message"]));
|
||||
return ($v->message == '' || ($v->message == ctx()->getStringMessage())) &&
|
||||
($v->user_id == 0 || ($v->user_id == ctx()->getUserId())) &&
|
||||
($v->group_id == 0 || ($v->group_id == (ctx()->getGroupId() ?? 0))) &&
|
||||
($v->message_type == '' || ($v->message_type == ctx()->getMessageType())) &&
|
||||
($v->raw_message == '' || ($v->raw_message == context()->getData()["raw_message"]));
|
||||
});
|
||||
$msg_dispatcher->setReturnFunction(function ($result) {
|
||||
if (is_string($result)) ctx()->reply($result);
|
||||
@@ -158,6 +187,15 @@ class QQBot
|
||||
}
|
||||
}
|
||||
|
||||
private function dispatchAfterEvents($data): EventDispatcher {
|
||||
$after = new EventDispatcher(CQAfter::class);
|
||||
$after->setRuleFunction(function ($v) use ($data) {
|
||||
return $v->cq_event == $data["post_type"];
|
||||
});
|
||||
$after->dispatchEvents($data);
|
||||
return $after;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $req
|
||||
* @throws Exception
|
||||
|
||||
@@ -21,12 +21,13 @@ class MySQLConnection implements Connection
|
||||
private $conn;
|
||||
|
||||
public function __construct() {
|
||||
Console::info("Constructing...");
|
||||
Console::debug("Constructing...");
|
||||
$this->conn = SqlPoolStorage::$sql_pool->getConnection();
|
||||
}
|
||||
|
||||
public function prepare($sql, $options = []) {
|
||||
try {
|
||||
Console::debug("Running SQL prepare: ".$sql);
|
||||
$statement = $this->conn->prepare($sql, $options);
|
||||
assert(($statement instanceof PDOStatementProxy) || ($statement instanceof PDOStatement));
|
||||
} catch (PDOException $exception) {
|
||||
@@ -51,6 +52,7 @@ class MySQLConnection implements Connection
|
||||
|
||||
public function exec($sql) {
|
||||
try {
|
||||
Console::debug("Running SQL exec: ".$sql);
|
||||
$statement = $this->conn->exec($sql);
|
||||
assert($statement !== false);
|
||||
return $statement;
|
||||
@@ -88,7 +90,7 @@ class MySQLConnection implements Connection
|
||||
}
|
||||
|
||||
public function __destruct() {
|
||||
Console::info("Destructing!!!");
|
||||
Console::debug("Destructing!!!");
|
||||
SqlPoolStorage::$sql_pool->putConnection($this->conn);
|
||||
}
|
||||
}
|
||||
@@ -4,16 +4,16 @@
|
||||
namespace ZM\MySQL;
|
||||
|
||||
|
||||
use Doctrine\DBAL\Driver;
|
||||
use Doctrine\DBAL\Driver as DoctrineDriver;
|
||||
use Doctrine\DBAL\Platforms\MySqlPlatform;
|
||||
use Doctrine\DBAL\Schema\MySqlSchemaManager;
|
||||
use ZM\Config\ZMConfig;
|
||||
use ZM\Console\Console;
|
||||
|
||||
class MySQLDriver implements Driver
|
||||
class MySQLDriver implements DoctrineDriver
|
||||
{
|
||||
public function connect(array $params, $username = null, $password = null, array $driverOptions = []) {
|
||||
Console::info("Requiring new connection");
|
||||
Console::debug("Requiring new connection");
|
||||
return new MySQLConnection();
|
||||
}
|
||||
|
||||
|
||||
@@ -4,17 +4,12 @@
|
||||
namespace ZM\MySQL;
|
||||
|
||||
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Doctrine\DBAL\DriverManager;
|
||||
use Doctrine\DBAL\Exception;
|
||||
|
||||
class MySQLManager
|
||||
{
|
||||
/**
|
||||
* @return Connection
|
||||
* @throws Exception
|
||||
* @return MySQLWrapper
|
||||
*/
|
||||
public static function getConnection() {
|
||||
return DriverManager::getConnection(["driverClass" => MySQLDriver::class]);
|
||||
public static function getWrapper(): MySQLWrapper {
|
||||
return new MySQLWrapper();
|
||||
}
|
||||
}
|
||||
@@ -34,4 +34,8 @@ class MySQLPool extends PDOPool
|
||||
$this->count--;
|
||||
parent::put($connection);
|
||||
}
|
||||
|
||||
public function getCount() {
|
||||
return $this->count;
|
||||
}
|
||||
}
|
||||
29
src/ZM/MySQL/MySQLQueryBuilder.php
Normal file
29
src/ZM/MySQL/MySQLQueryBuilder.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\MySQL;
|
||||
|
||||
|
||||
use Doctrine\DBAL\Query\QueryBuilder;
|
||||
use ZM\Exception\DbException;
|
||||
|
||||
class MySQLQueryBuilder extends QueryBuilder
|
||||
{
|
||||
private $wrapper;
|
||||
|
||||
public function __construct(MySQLWrapper $wrapper) {
|
||||
parent::__construct($wrapper->getConnection());
|
||||
$this->wrapper = $wrapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int|MySQLStatementWrapper
|
||||
* @throws DbException
|
||||
*/
|
||||
public function execute() {
|
||||
if ($this->getType() === self::SELECT) {
|
||||
return $this->wrapper->executeQuery($this->getSQL(), $this->getParameters(), $this->getParameterTypes());
|
||||
}
|
||||
return $this->wrapper->executeStatement($this->getSQL(), $this->getParameters(), $this->getParameterTypes());
|
||||
}
|
||||
}
|
||||
@@ -7,72 +7,100 @@ namespace ZM\MySQL;
|
||||
use Doctrine\DBAL\Driver\Statement;
|
||||
use Doctrine\DBAL\Driver\StatementIterator;
|
||||
use Doctrine\DBAL\ParameterType;
|
||||
use IteratorAggregate;
|
||||
use PDO;
|
||||
use PDOStatement;
|
||||
use Swoole\Database\PDOStatementProxy;
|
||||
|
||||
class MySQLStatement implements Statement, \IteratorAggregate
|
||||
class MySQLStatement implements IteratorAggregate, Statement
|
||||
{
|
||||
/** @var PDOStatement|PDOStatementProxy */
|
||||
private $statement;
|
||||
|
||||
public function __construct($obj) {
|
||||
public function __construct($obj)
|
||||
{
|
||||
$this->statement = $obj;
|
||||
}
|
||||
|
||||
public function closeCursor() {
|
||||
public function closeCursor()
|
||||
{
|
||||
return $this->statement->closeCursor();
|
||||
}
|
||||
|
||||
public function columnCount() {
|
||||
public function columnCount()
|
||||
{
|
||||
return $this->statement->columnCount();
|
||||
}
|
||||
|
||||
public function setFetchMode($fetchMode, $arg2 = null, $arg3 = []) {
|
||||
return $this->statement->setFetchMode($fetchMode, $arg2, $arg3);
|
||||
public function setFetchMode($fetchMode, $arg2 = null, $arg3 = [])
|
||||
{
|
||||
if ($arg2 !== null && $arg3 !== [])
|
||||
return $this->statement->setFetchMode($fetchMode, $arg2, $arg3);
|
||||
elseif ($arg2 !== null && $arg3 === [])
|
||||
return $this->statement->setFetchMode($fetchMode, $arg2);
|
||||
elseif ($arg2 === null && $arg3 !== [])
|
||||
return $this->statement->setFetchMode($fetchMode, $arg2, $arg3);
|
||||
else
|
||||
return $this->statement->setFetchMode($fetchMode);
|
||||
}
|
||||
|
||||
public function fetch($fetchMode = PDO::FETCH_ASSOC, $cursorOrientation = PDO::FETCH_ORI_NEXT, $cursorOffset = 0) {
|
||||
public function fetch($fetchMode = PDO::FETCH_ASSOC, $cursorOrientation = PDO::FETCH_ORI_NEXT, $cursorOffset = 0)
|
||||
{
|
||||
return $this->statement->fetch($fetchMode, $cursorOrientation, $cursorOffset);
|
||||
}
|
||||
|
||||
public function fetchAll($fetchMode = PDO::FETCH_ASSOC, $fetchArgument = null, $ctorArgs = null) {
|
||||
return $this->statement->fetchAll($fetchMode, $fetchArgument, $ctorArgs);
|
||||
public function fetchAll($fetchMode = PDO::FETCH_ASSOC, $fetchArgument = null, $ctorArgs = null)
|
||||
{
|
||||
if ($fetchArgument === null && $ctorArgs === null)
|
||||
return $this->statement->fetchAll($fetchMode);
|
||||
elseif ($fetchArgument !== null && $ctorArgs === null)
|
||||
return $this->statement->fetchAll($fetchMode, $fetchArgument);
|
||||
else
|
||||
return $this->statement->fetchAll($fetchMode, $fetchArgument, $ctorArgs);
|
||||
}
|
||||
|
||||
public function fetchColumn($columnIndex = 0) {
|
||||
public function fetchColumn($columnIndex = 0)
|
||||
{
|
||||
return $this->statement->fetchColumn($columnIndex);
|
||||
}
|
||||
|
||||
public function bindValue($param, $value, $type = ParameterType::STRING) {
|
||||
public function bindValue($param, $value, $type = ParameterType::STRING)
|
||||
{
|
||||
return $this->statement->bindValue($param, $value, $type);
|
||||
}
|
||||
|
||||
public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null) {
|
||||
public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null)
|
||||
{
|
||||
return $this->statement->bindParam($param, $variable, $type, $length);
|
||||
}
|
||||
|
||||
public function errorCode() {
|
||||
public function errorCode()
|
||||
{
|
||||
return $this->statement->errorCode();
|
||||
}
|
||||
|
||||
public function errorInfo() {
|
||||
public function errorInfo()
|
||||
{
|
||||
return $this->statement->errorInfo();
|
||||
}
|
||||
|
||||
public function execute($params = null) {
|
||||
public function execute($params = null)
|
||||
{
|
||||
return $this->statement->execute($params);
|
||||
}
|
||||
|
||||
public function rowCount() {
|
||||
public function rowCount()
|
||||
{
|
||||
return $this->statement->rowCount();
|
||||
}
|
||||
|
||||
public function getIterator() {
|
||||
public function getIterator(): StatementIterator
|
||||
{
|
||||
return new StatementIterator($this);
|
||||
}
|
||||
|
||||
public function current() {
|
||||
public function current()
|
||||
{
|
||||
return $this->statement->current();
|
||||
}
|
||||
}
|
||||
232
src/ZM/MySQL/MySQLStatementWrapper.php
Normal file
232
src/ZM/MySQL/MySQLStatementWrapper.php
Normal file
@@ -0,0 +1,232 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @noinspection PhpMissingReturnTypeInspection
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
|
||||
namespace ZM\MySQL;
|
||||
|
||||
use Doctrine\DBAL\Driver\ResultStatement;
|
||||
use Doctrine\DBAL\ForwardCompatibility\Result;
|
||||
use Throwable;
|
||||
use Traversable;
|
||||
use ZM\Exception\DbException;
|
||||
|
||||
class MySQLStatementWrapper
|
||||
{
|
||||
public $stmt;
|
||||
|
||||
public function __construct(?Result $stmt) {
|
||||
$this->stmt = $stmt;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取结果的迭代器
|
||||
* wrapper method
|
||||
* @return ResultStatement
|
||||
*/
|
||||
public function getIterator() {
|
||||
return $this->stmt->getIterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取列数
|
||||
* wrapper method
|
||||
* @return int
|
||||
*/
|
||||
public function columnCount() {
|
||||
return $this->stmt->columnCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper method
|
||||
* @return array|false|mixed
|
||||
* @throws DbException
|
||||
*/
|
||||
public function fetchNumeric() {
|
||||
try {
|
||||
return $this->stmt->fetchNumeric();
|
||||
} catch (Throwable $e) {
|
||||
throw new DbException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper method
|
||||
* @return array|false|mixed
|
||||
* @throws DbException
|
||||
*/
|
||||
public function fetchAssociative() {
|
||||
try {
|
||||
return $this->stmt->fetchAssociative();
|
||||
} catch (Throwable $e) {
|
||||
throw new DbException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper method
|
||||
* @return false|mixed
|
||||
* @throws DbException
|
||||
*/
|
||||
public function fetchOne() {
|
||||
try {
|
||||
return $this->stmt->fetchOne();
|
||||
} catch (Throwable $e) {
|
||||
throw new DbException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper method
|
||||
* @return array
|
||||
* @throws DbException
|
||||
*/
|
||||
public function fetchAllNumeric(): array {
|
||||
try {
|
||||
return $this->stmt->fetchAllNumeric();
|
||||
} catch (Throwable $e) {
|
||||
throw new DbException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper method
|
||||
* @return array
|
||||
* @throws DbException
|
||||
*/
|
||||
public function fetchAllAssociative(): array {
|
||||
try {
|
||||
return $this->stmt->fetchAllAssociative();
|
||||
} catch (Throwable $e) {
|
||||
throw new DbException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper method
|
||||
* @return array
|
||||
* @throws DbException
|
||||
*/
|
||||
public function fetchAllKeyValue(): array {
|
||||
try {
|
||||
return $this->stmt->fetchAllKeyValue();
|
||||
} catch (Throwable $e) {
|
||||
throw new DbException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper method
|
||||
* @return array
|
||||
* @throws DbException
|
||||
*/
|
||||
public function fetchAllAssociativeIndexed(): array {
|
||||
try {
|
||||
return $this->stmt->fetchAllAssociativeIndexed();
|
||||
} catch (Throwable $e) {
|
||||
throw new DbException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper method
|
||||
* @return array
|
||||
* @throws DbException
|
||||
*/
|
||||
public function fetchFirstColumn(): array {
|
||||
try {
|
||||
return $this->stmt->fetchFirstColumn();
|
||||
} catch (Throwable $e) {
|
||||
throw new DbException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper method
|
||||
* @return Traversable
|
||||
* @throws DbException
|
||||
*/
|
||||
public function iterateNumeric(): Traversable {
|
||||
try {
|
||||
return $this->stmt->iterateNumeric();
|
||||
} catch (Throwable $e) {
|
||||
throw new DbException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper method
|
||||
* @return Traversable
|
||||
* @throws DbException
|
||||
*/
|
||||
public function iterateAssociative(): Traversable {
|
||||
try {
|
||||
return $this->stmt->iterateAssociative();
|
||||
} catch (Throwable $e) {
|
||||
throw new DbException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper method
|
||||
* @return Traversable
|
||||
* @throws DbException
|
||||
*/
|
||||
public function iterateKeyValue(): Traversable {
|
||||
try {
|
||||
return $this->stmt->iterateKeyValue();
|
||||
} catch (Throwable $e) {
|
||||
throw new DbException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper method
|
||||
* @return Traversable
|
||||
* @throws DbException
|
||||
*/
|
||||
public function iterateAssociativeIndexed(): Traversable {
|
||||
try {
|
||||
return $this->stmt->iterateAssociativeIndexed();
|
||||
} catch (Throwable $e) {
|
||||
throw new DbException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper method
|
||||
* @return Traversable
|
||||
* @throws DbException
|
||||
*/
|
||||
public function iterateColumn(): Traversable {
|
||||
try {
|
||||
return $this->stmt->iterateColumn();
|
||||
} catch (Throwable $e) {
|
||||
throw new DbException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper method
|
||||
* @return int
|
||||
* @throws DbException
|
||||
*/
|
||||
public function rowCount() {
|
||||
try {
|
||||
return $this->stmt->rowCount();
|
||||
} catch (Throwable $e) {
|
||||
throw new DbException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper method
|
||||
*/
|
||||
public function free(): void {
|
||||
$this->stmt->free();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,15 +1,575 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
|
||||
namespace ZM\MySQL;
|
||||
|
||||
use Closure;
|
||||
use Doctrine\DBAL\Cache\QueryCacheProfile;
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Doctrine\DBAL\DriverManager;
|
||||
use Doctrine\DBAL\ParameterType;
|
||||
use Throwable;
|
||||
use Traversable;
|
||||
use ZM\Exception\DbException;
|
||||
|
||||
class MySQLWrapper
|
||||
{
|
||||
public $connection;
|
||||
private $connection;
|
||||
|
||||
/**
|
||||
* MySQLWrapper constructor.
|
||||
* @throws DbException
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->connection = MySQLManager::getConnection();
|
||||
try {
|
||||
$this->connection = DriverManager::getConnection(["driverClass" => MySQLDriver::class]);
|
||||
} catch (Throwable $e) {
|
||||
throw new DbException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper method
|
||||
* @return string
|
||||
*/
|
||||
public function getDatabase(): string {
|
||||
return $this->connection->getDatabase();
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper method
|
||||
* @return bool
|
||||
*/
|
||||
public function isAutoCommit(): bool {
|
||||
return $this->connection->isAutoCommit();
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper method
|
||||
* @param $autoCommit
|
||||
*/
|
||||
public function setAutoCommit($autoCommit) {
|
||||
$this->connection->setAutoCommit($autoCommit);
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper method
|
||||
* @param string $query
|
||||
* @param array $params
|
||||
* @param array $types
|
||||
* @return array|false
|
||||
* @throws DbException
|
||||
*/
|
||||
public function fetchAssociative(string $query, array $params = [], array $types = []) {
|
||||
try {
|
||||
return $this->connection->fetchAssociative($query, $params, $types);
|
||||
} catch (Throwable $e) {
|
||||
throw new DbException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper method
|
||||
* @param string $query
|
||||
* @param array $params
|
||||
* @param array $types
|
||||
* @return array|false
|
||||
* @throws DbException
|
||||
*/
|
||||
public function fetchNumeric(string $query, array $params = [], array $types = []) {
|
||||
try {
|
||||
return $this->connection->fetchNumeric($query, $params, $types);
|
||||
} catch (Throwable $e) {
|
||||
throw new DbException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $query
|
||||
* @param array $params
|
||||
* @param array $types
|
||||
* @return false|mixed
|
||||
* @throws DbException
|
||||
*/
|
||||
public function fetchOne(string $query, array $params = [], array $types = []) {
|
||||
try {
|
||||
return $this->connection->fetchOne($query, $params, $types);
|
||||
} catch (Throwable $e) {
|
||||
throw new DbException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper method
|
||||
* @return bool
|
||||
*/
|
||||
public function isTransactionActive(): bool {
|
||||
return $this->connection->isTransactionActive();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $table
|
||||
* @param array $criteria
|
||||
* @param array $types
|
||||
* @return int
|
||||
* @throws DbException
|
||||
*/
|
||||
public function delete($table, array $criteria, array $types = []): int {
|
||||
try {
|
||||
return $this->connection->delete($table, $criteria, $types);
|
||||
} catch (Throwable $e) {
|
||||
throw new DbException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper method
|
||||
* @param $level
|
||||
* @return int
|
||||
*/
|
||||
public function setTransactionIsolation($level): int {
|
||||
return $this->connection->setTransactionIsolation($level);
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper method
|
||||
* @return int|null
|
||||
*/
|
||||
public function getTransactionIsolation(): ?int {
|
||||
return $this->connection->getTransactionIsolation();
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper method
|
||||
* @param $table
|
||||
* @param array $data
|
||||
* @param array $criteria
|
||||
* @param array $types
|
||||
* @return int
|
||||
* @throws DbException
|
||||
*/
|
||||
public function update($table, array $data, array $criteria, array $types = []): int {
|
||||
try {
|
||||
return $this->connection->update($table, $data, $criteria, $types);
|
||||
} catch (Throwable $e) {
|
||||
throw new DbException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper method
|
||||
* @param $table
|
||||
* @param array $data
|
||||
* @param array $types
|
||||
* @return int
|
||||
* @throws DbException
|
||||
*/
|
||||
public function insert($table, array $data, array $types = []): int {
|
||||
try {
|
||||
return $this->connection->insert($table, $data, $types);
|
||||
} catch (Throwable $e) {
|
||||
throw new DbException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper method
|
||||
* @param $str
|
||||
* @return string
|
||||
*/
|
||||
public function quoteIdentifier($str): string {
|
||||
return $this->connection->quoteIdentifier($str);
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper method
|
||||
* @param $value
|
||||
* @param int $type
|
||||
* @return mixed
|
||||
*/
|
||||
public function quote($value, $type = ParameterType::STRING) {
|
||||
return $this->connection->quote($value, $type);
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper method
|
||||
* @param string $query
|
||||
* @param array $params
|
||||
* @param array $types
|
||||
* @return array
|
||||
* @throws DbException
|
||||
*/
|
||||
public function fetchAllNumeric(string $query, array $params = [], array $types = []): array {
|
||||
try {
|
||||
return $this->connection->fetchAllNumeric($query, $params, $types);
|
||||
} catch (Throwable $e) {
|
||||
throw new DbException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper method
|
||||
* @param string $query
|
||||
* @param array $params
|
||||
* @param array $types
|
||||
* @return array
|
||||
* @throws DbException
|
||||
*/
|
||||
public function fetchAllAssociative(string $query, array $params = [], array $types = []): array {
|
||||
try {
|
||||
return $this->connection->fetchAllAssociative($query, $params, $types);
|
||||
} catch (Throwable $e) {
|
||||
throw new DbException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper method
|
||||
* @param string $query
|
||||
* @param array $params
|
||||
* @param array $types
|
||||
* @return array
|
||||
* @throws DbException
|
||||
*/
|
||||
public function fetchAllKeyValue(string $query, array $params = [], array $types = []): array {
|
||||
try {
|
||||
return $this->connection->fetchAllKeyValue($query, $params, $types);
|
||||
} catch (Throwable $e) {
|
||||
throw new DbException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper method
|
||||
* @param string $query
|
||||
* @param array $params
|
||||
* @param array $types
|
||||
* @return array
|
||||
* @throws DbException
|
||||
*/
|
||||
public function fetchAllAssociativeIndexed(string $query, array $params = [], array $types = []): array {
|
||||
try {
|
||||
return $this->connection->fetchAllAssociativeIndexed($query, $params, $types);
|
||||
} catch (Throwable $e) {
|
||||
throw new DbException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper method
|
||||
* @param string $query
|
||||
* @param array $params
|
||||
* @param array $types
|
||||
* @return array
|
||||
* @throws DbException
|
||||
*/
|
||||
public function fetchFirstColumn(string $query, array $params = [], array $types = []): array {
|
||||
try {
|
||||
return $this->connection->fetchFirstColumn($query, $params, $types);
|
||||
} catch (Throwable $e) {
|
||||
throw new DbException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper method
|
||||
* @param string $query
|
||||
* @param array $params
|
||||
* @param array $types
|
||||
* @return Traversable
|
||||
* @throws DbException
|
||||
*/
|
||||
public function iterateNumeric(string $query, array $params = [], array $types = []): Traversable {
|
||||
try {
|
||||
return $this->connection->iterateNumeric($query, $params, $types);
|
||||
} catch (Throwable $e) {
|
||||
throw new DbException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper method
|
||||
* @param string $query
|
||||
* @param array $params
|
||||
* @param array $types
|
||||
* @return Traversable
|
||||
* @throws DbException
|
||||
*/
|
||||
public function iterateAssociative(string $query, array $params = [], array $types = []): Traversable {
|
||||
try {
|
||||
return $this->connection->iterateAssociative($query, $params, $types);
|
||||
} catch (Throwable $e) {
|
||||
throw new DbException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper method
|
||||
* @param string $query
|
||||
* @param array $params
|
||||
* @param array $types
|
||||
* @return Traversable
|
||||
* @throws DbException
|
||||
*/
|
||||
public function iterateKeyValue(string $query, array $params = [], array $types = []): Traversable {
|
||||
try {
|
||||
return $this->connection->iterateKeyValue($query, $params, $types);
|
||||
} catch (Throwable $e) {
|
||||
throw new DbException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper method
|
||||
* @param string $query
|
||||
* @param array $params
|
||||
* @param array $types
|
||||
* @return Traversable
|
||||
* @throws DbException
|
||||
*/
|
||||
public function iterateAssociativeIndexed(string $query, array $params = [], array $types = []): Traversable {
|
||||
try {
|
||||
return $this->connection->iterateAssociativeIndexed($query, $params, $types);
|
||||
} catch (Throwable $e) {
|
||||
throw new DbException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper method
|
||||
* @param string $query
|
||||
* @param array $params
|
||||
* @param array $types
|
||||
* @return Traversable
|
||||
* @throws DbException
|
||||
*/
|
||||
public function iterateColumn(string $query, array $params = [], array $types = []): Traversable {
|
||||
try {
|
||||
return $this->connection->iterateColumn($query, $params, $types);
|
||||
} catch (Throwable $e) {
|
||||
throw new DbException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper method
|
||||
* @param $sql
|
||||
* @param array $params
|
||||
* @param array $types
|
||||
* @param QueryCacheProfile|null $qcp
|
||||
* @return MySQLStatementWrapper
|
||||
* @throws DbException
|
||||
*/
|
||||
public function executeQuery($sql, array $params = [], $types = [], ?QueryCacheProfile $qcp = null): MySQLStatementWrapper {
|
||||
try {
|
||||
$query = $this->connection->executeQuery($sql, $params, $types, $qcp);
|
||||
return new MySQLStatementWrapper($query);
|
||||
} catch (Throwable $e) {
|
||||
throw new DbException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper method
|
||||
* @param $sql
|
||||
* @param $params
|
||||
* @param $types
|
||||
* @param QueryCacheProfile $qcp
|
||||
* @return MySQLStatementWrapper
|
||||
* @throws DbException
|
||||
*/
|
||||
public function executeCacheQuery($sql, $params, $types, QueryCacheProfile $qcp): MySQLStatementWrapper {
|
||||
try {
|
||||
$query = $this->connection->executeCacheQuery($sql, $params, $types, $qcp);
|
||||
return new MySQLStatementWrapper($query);
|
||||
} catch (Throwable $e) {
|
||||
throw new DbException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper method
|
||||
* @param $sql
|
||||
* @param array $params
|
||||
* @param array $types
|
||||
* @return int
|
||||
* @throws DbException
|
||||
*/
|
||||
public function executeStatement($sql, array $params = [], array $types = []): int {
|
||||
try {
|
||||
return $this->connection->executeStatement($sql, $params, $types);
|
||||
} catch (Throwable $e) {
|
||||
throw new DbException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper method
|
||||
* @return int
|
||||
*/
|
||||
public function getTransactionNestingLevel(): int {
|
||||
return $this->connection->getTransactionNestingLevel();
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper method
|
||||
* @param null $name
|
||||
* @return string
|
||||
*/
|
||||
public function lastInsertId($name = null): string {
|
||||
return $this->connection->lastInsertId($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* overwrite method to $this->connection->transactional()
|
||||
* @param Closure $func
|
||||
* @return mixed
|
||||
* @throws DbException
|
||||
*/
|
||||
public function transactional(Closure $func) {
|
||||
$this->beginTransaction();
|
||||
try {
|
||||
$res = $func($this);
|
||||
$this->commit();
|
||||
return $res;
|
||||
} catch (Throwable $e) {
|
||||
$this->rollBack();
|
||||
throw new DbException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper method
|
||||
* @param $nestTransactionsWithSavepoints
|
||||
* @throws DbException
|
||||
*/
|
||||
public function setNestTransactionsWithSavepoints($nestTransactionsWithSavepoints) {
|
||||
try {
|
||||
$this->connection->setNestTransactionsWithSavepoints($nestTransactionsWithSavepoints);
|
||||
} catch (Throwable $e) {
|
||||
throw new DbException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper method
|
||||
* @return bool
|
||||
*/
|
||||
public function getNestTransactionsWithSavepoints(): bool {
|
||||
return $this->connection->getNestTransactionsWithSavepoints();
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper method
|
||||
* @return bool
|
||||
*/
|
||||
public function beginTransaction(): bool {
|
||||
return $this->connection->beginTransaction();
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper method
|
||||
* @return bool
|
||||
* @throws DbException
|
||||
*/
|
||||
public function commit(): bool {
|
||||
try {
|
||||
return $this->connection->commit();
|
||||
} catch (Throwable $e) {
|
||||
throw new DbException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper method
|
||||
* @return bool
|
||||
* @throws DbException
|
||||
*/
|
||||
public function rollBack(): bool {
|
||||
try {
|
||||
return $this->connection->rollBack();
|
||||
} catch (Throwable $e) {
|
||||
throw new DbException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper method
|
||||
* @param $savepoint
|
||||
* @throws DbException
|
||||
*/
|
||||
public function createSavepoint($savepoint) {
|
||||
try {
|
||||
$this->connection->createSavepoint($savepoint);
|
||||
} catch (Throwable $e) {
|
||||
throw new DbException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper method
|
||||
* @param $savepoint
|
||||
* @throws DbException
|
||||
*/
|
||||
public function releaseSavepoint($savepoint) {
|
||||
try {
|
||||
$this->connection->releaseSavepoint($savepoint);
|
||||
} catch (Throwable $e) {
|
||||
throw new DbException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper method
|
||||
* @param $savepoint
|
||||
* @throws DbException
|
||||
*/
|
||||
public function rollbackSavepoint($savepoint) {
|
||||
try {
|
||||
$this->connection->rollbackSavepoint($savepoint);
|
||||
} catch (Throwable $e) {
|
||||
throw new DbException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper method
|
||||
* @throws DbException
|
||||
*/
|
||||
public function setRollbackOnly() {
|
||||
try {
|
||||
$this->connection->setRollbackOnly();
|
||||
} catch (Throwable $e) {
|
||||
throw new DbException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper method
|
||||
* @return bool
|
||||
* @throws DbException
|
||||
*/
|
||||
public function isRollbackOnly(): bool {
|
||||
try {
|
||||
return $this->connection->isRollbackOnly();
|
||||
} catch (Throwable $e) {
|
||||
throw new DbException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* overwrite method to $this->connection->createQueryBuilder
|
||||
* @return MySQLQueryBuilder
|
||||
*/
|
||||
public function createQueryBuilder(): MySQLQueryBuilder {
|
||||
return new MySQLQueryBuilder($this);
|
||||
}
|
||||
|
||||
public function getConnection(): Connection {
|
||||
return $this->connection;
|
||||
}
|
||||
|
||||
public function __destruct() {
|
||||
|
||||
@@ -9,6 +9,7 @@ use Swoole\Table;
|
||||
use ZM\Annotation\Swoole\OnSave;
|
||||
use ZM\Console\Console;
|
||||
use ZM\Event\EventDispatcher;
|
||||
use ZM\Exception\LightCacheException;
|
||||
use ZM\Exception\ZMException;
|
||||
use ZM\Framework;
|
||||
use ZM\Utils\Manager\ProcessManager;
|
||||
@@ -86,7 +87,7 @@ class LightCache
|
||||
* @throws ZMException
|
||||
*/
|
||||
public static function get(string $key) {
|
||||
if (self::$kv_table === null) throw new ZMException(zm_internal_errcode("E00048") . "not initialized LightCache");
|
||||
if (self::$kv_table === null) throw new LightCacheException("E00048", "not initialized LightCache");
|
||||
self::checkExpire($key);
|
||||
$r = self::$kv_table->get($key);
|
||||
return $r === false ? null : self::parseGet($r);
|
||||
@@ -98,7 +99,7 @@ class LightCache
|
||||
* @throws ZMException
|
||||
*/
|
||||
public static function getExpire(string $key) {
|
||||
if (self::$kv_table === null) throw new ZMException(zm_internal_errcode("E00048") . "not initialized LightCache");
|
||||
if (self::$kv_table === null) throw new LightCacheException("E00048", "not initialized LightCache");
|
||||
self::checkExpire($key);
|
||||
$r = self::$kv_table->get($key, "expire");
|
||||
return $r === false ? null : $r - time();
|
||||
@@ -111,7 +112,7 @@ class LightCache
|
||||
* @since 2.4.3
|
||||
*/
|
||||
public static function getExpireTS(string $key) {
|
||||
if (self::$kv_table === null) throw new ZMException(zm_internal_errcode("E00048") . "not initialized LightCache");
|
||||
if (self::$kv_table === null) throw new LightCacheException("E00048", "not initialized LightCache");
|
||||
self::checkExpire($key);
|
||||
$r = self::$kv_table->get($key, "expire");
|
||||
return $r === false ? null : $r;
|
||||
@@ -125,7 +126,7 @@ class LightCache
|
||||
* @throws ZMException
|
||||
*/
|
||||
public static function set(string $key, $value, int $expire = -1) {
|
||||
if (self::$kv_table === null) throw new ZMException(zm_internal_errcode("E00048") . "not initialized LightCache");
|
||||
if (self::$kv_table === null) throw new LightCacheException("E00048", "not initialized LightCache");
|
||||
if (is_array($value)) {
|
||||
$value = json_encode($value, JSON_UNESCAPED_UNICODE);
|
||||
if (strlen($value) >= self::$config["max_strlen"]) return false;
|
||||
@@ -138,7 +139,7 @@ class LightCache
|
||||
$data_type = "bool";
|
||||
$value = json_encode($value);
|
||||
} else {
|
||||
throw new ZMException(zm_internal_errcode("E00049") . "Only can set string, array and int");
|
||||
throw new LightCacheException("E00049", "Only can set string, array and int");
|
||||
}
|
||||
try {
|
||||
return self::$kv_table->set($key, [
|
||||
@@ -158,7 +159,7 @@ class LightCache
|
||||
* @throws ZMException
|
||||
*/
|
||||
public static function update(string $key, $value) {
|
||||
if (self::$kv_table === null) throw new ZMException(zm_internal_errcode("E00048") . "not initialized LightCache.");
|
||||
if (self::$kv_table === null) throw new LightCacheException("E00048", "not initialized LightCache.");
|
||||
if (is_array($value)) {
|
||||
$value = json_encode($value, JSON_UNESCAPED_UNICODE);
|
||||
if (strlen($value) >= self::$config["max_strlen"]) return false;
|
||||
@@ -168,7 +169,7 @@ class LightCache
|
||||
} elseif (is_int($value)) {
|
||||
$data_type = "int";
|
||||
} else {
|
||||
throw new ZMException(zm_internal_errcode("E00048") . "Only can set string, array and int");
|
||||
throw new LightCacheException("E00048", "Only can set string, array and int");
|
||||
}
|
||||
try {
|
||||
if (self::$kv_table->get($key) === false) return false;
|
||||
|
||||
@@ -6,6 +6,7 @@ namespace ZM\Store;
|
||||
|
||||
use Exception;
|
||||
use Swoole\Table;
|
||||
use ZM\Exception\LightCacheException;
|
||||
use ZM\Exception\ZMException;
|
||||
|
||||
class LightCacheInside
|
||||
@@ -67,6 +68,6 @@ class LightCacheInside
|
||||
self::$kv_table[$name] = new Table($size, $conflict_proportion);
|
||||
self::$kv_table[$name]->column("value", Table::TYPE_STRING, $str_size);
|
||||
$r = self::$kv_table[$name]->create();
|
||||
if ($r === false) throw new ZMException(zm_internal_errcode("E00050") . "内存不足,创建静态表失败!");
|
||||
if ($r === false) throw new LightCacheException("E00050", "内存不足,创建静态表失败!");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ class ZMAtomic
|
||||
self::$atomics["wait_msg_id"] = new Atomic(0);
|
||||
self::$atomics["_event_id"] = new Atomic(0);
|
||||
self::$atomics["server_is_stopped"] = new Atomic(0);
|
||||
if (!defined("ZM_WORKER_NUM")) define("ZM_WORKER_NUM", 1);
|
||||
for($i = 0; $i < ZM_WORKER_NUM; ++$i) {
|
||||
self::$atomics["_#worker_".$i] = new Atomic(0);
|
||||
}
|
||||
|
||||
0
src/ZM/Store/ZMBuf.php
Executable file → Normal file
0
src/ZM/Store/ZMBuf.php
Executable file → Normal file
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user