mirror of
https://github.com/zhamao-robot/zhamao-framework.git
synced 2026-07-03 06:45:36 +08:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
66dd91bb97 | ||
|
|
e020e5d593 | ||
|
|
3d62663281 | ||
|
|
8d9485c02e | ||
|
|
9fb45dd683 | ||
|
|
8a4924dba9 | ||
|
|
beaf7be606 | ||
|
|
7dbd21bdf4 | ||
|
|
9ce3056203 | ||
|
|
880b4e847c | ||
|
|
71e83d5bc8 | ||
|
|
432fd92cca | ||
|
|
907a9cea25 | ||
| 07391810ff |
@@ -98,7 +98,7 @@ 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 进行疑难解答。
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -57,21 +57,6 @@ $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' => '',
|
||||
|
||||
@@ -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 码。
|
||||
|
||||
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)。
|
||||
@@ -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()` 等。
|
||||
|
||||
@@ -14,7 +14,6 @@ src/
|
||||
│ └── zm.json
|
||||
└── Middleware/
|
||||
└── TimerMiddleware.php
|
||||
|
||||
```
|
||||
|
||||
我们在 Example 目录下创建一个 `zm.json` 的文件,编写配置,即代表 `src/Module/Example/` 文件夹及里面的用户模块源码为一个模块包,也就可以被框架识别并打包处理。
|
||||
@@ -160,6 +159,68 @@ src/
|
||||
|
||||
在打包时框架会自动添加这些文件到 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
|
||||
- 必要的框架热加载以及解包需要的配置信息
|
||||
|
||||
|
||||
## 打包命令
|
||||
|
||||
```bash
|
||||
|
||||
@@ -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()
|
||||
|
||||
180
docs/component/store/mysql/common-query.md
Normal file
180
docs/component/store/mysql/common-query.md
Normal file
@@ -0,0 +1,180 @@
|
||||
# 执行 SQL 语句
|
||||
|
||||
在一开始,无论你做什么数据库操作,均需要获取一个 `\ZM\MySQL\MySQLWrapper` 作为你的操作对象。
|
||||
|
||||
```php
|
||||
/** @var \ZM\MySQL\MySQLWrapper $wrapper */
|
||||
$wrapper = \ZM\MySQL\MySQLManager::getWrapper();
|
||||
```
|
||||
|
||||
## 执行预处理 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` 段。
|
||||
|
||||
如果 `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。
|
||||
@@ -1,10 +1,14 @@
|
||||
# MySQL 数据库
|
||||
|
||||
## 简介
|
||||
# MySQL 数据库简介
|
||||
|
||||
炸毛框架的数据库组件对接了 MySQL 连接池,在使用过程中无需配置即可实现 MySQL 查询,同时拥有高并发。
|
||||
|
||||
目前 2.5 版本后炸毛框架底层采用了 `doctrine/dbal` 组件,可以方便地构建 SQL 语句。
|
||||
|
||||
> 文档正在加急编写!!!用的话可以先用 `MySQLManager::getWrapper()` 获取 wrapper 后返回的方法。
|
||||
本章大体查询内容均以下表 `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 同源。
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
| ----------------------------- | ------------------------------------------------------------ | --------------------------------------- |
|
||||
| `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 |
|
||||
| `middleware_error_policy` | 中间件错误处理策略,见 [中间件 - 错误处理策略](../../event/middleware/#_6) | 1 |
|
||||
|
||||
### 子表 **light_cache**
|
||||
|
||||
|
||||
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,7 +3,7 @@
|
||||
> 本文档为炸毛框架 v2 版本,如需查看 v1 版本,[点我](https://docs-v1.zhamao.xin/)。
|
||||
|
||||
!!! tip "提示"
|
||||
|
||||
|
||||
编写文档需要较大精力,你也可以参与到本文档的建设中来,比如找错字,增加或更正内容,每页文档可直接点击右上方铅笔图标直接跳转至 GitHub 进行编辑,编辑后自动 Fork 并生成 Pull Request,以此来贡献此文档!
|
||||
|
||||
炸毛框架使用 PHP 编写,采用 Swoole 扩展为基础,主要面向 API 服务,聊天机器人(OneBot 标准的机器人对接),包含 WebSocket、HTTP 等监听和请求库,用户代码采用模块化处理,使用注解可以方便地编写各类功能。
|
||||
|
||||
@@ -4,11 +4,20 @@
|
||||
|
||||
同时此处将只使用 build 版本号进行区分。
|
||||
|
||||
## 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)
|
||||
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
|
||||
这里将会记录各个主版本的框架升级后,涉及 `global.php` 的更新日志,你可以根据这里描述的内容与你的旧配置文件进行合并。
|
||||
|
||||
## v2.5.1 (build 417)
|
||||
|
||||
- 新增 `$config['runtime']` 下的 `middleware_error_policy` 选项。
|
||||
|
||||
## v2.5.0 (build 413)
|
||||
|
||||
- 新增 `$config['runtime']` 运行时设置。
|
||||
|
||||
@@ -1,5 +1,60 @@
|
||||
# 更新日志(v2 版本)
|
||||
|
||||
## 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
|
||||
|
||||
48
mkdocs.yml
48
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,48 +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/module/module-unpack.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
|
||||
- 进阶开发:
|
||||
- 进阶开发: 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
|
||||
|
||||
@@ -52,7 +52,7 @@ class ModulePackCommand extends Command
|
||||
$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;
|
||||
|
||||
@@ -28,8 +28,8 @@ class ConsoleApplication extends Application
|
||||
{
|
||||
private static $obj = null;
|
||||
|
||||
const VERSION_ID = 418;
|
||||
const VERSION = "2.5.2";
|
||||
const VERSION_ID = 423;
|
||||
const VERSION = "2.5.6";
|
||||
|
||||
/**
|
||||
* @throws InitException
|
||||
|
||||
@@ -93,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);
|
||||
|
||||
@@ -72,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;
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ class EventManager
|
||||
public static $req_mapping = [];
|
||||
|
||||
public static function addEvent($event_name, ?AnnotationBase $event_obj) {
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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() . ";");
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
@@ -157,21 +158,7 @@ class OnWorkerStart implements SwooleEvent
|
||||
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->class = QQBot::class;
|
||||
$obj->method = 'handleByEvent';
|
||||
$obj->type = 'message';
|
||||
$obj->level = $obb_onebot["message_level"] ?? 99999;
|
||||
$obj->rule = 'connectIsQQ()';
|
||||
EventManager::addEvent(OnSwooleEvent::class, $obj);
|
||||
if ($obb_onebot["single_bot_mode"]) {
|
||||
LightCacheInside::set("connect", "conn_fd", -1);
|
||||
} else {
|
||||
LightCacheInside::set("connect", "conn_fd", -2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 检查是否允许热加载phar模块,允许的话将遍历phar内的文件
|
||||
$plugin_enable_hotload = ZMConfig::get("global", "module_loader")["enable_hotload"] ?? false;
|
||||
@@ -188,6 +175,21 @@ class OnWorkerStart implements SwooleEvent
|
||||
|
||||
$parser->registerMods();
|
||||
EventManager::loadEventByParser($parser); //加载事件
|
||||
|
||||
if ($obb_onebot["status"]) {
|
||||
Console::debug("OneBot support enabled, listening OneBot event(3).");
|
||||
$obj = new OnMessageEvent();
|
||||
$obj->class = QQBot::class;
|
||||
$obj->method = 'handleByEvent';
|
||||
$obj->level = $obb_onebot["message_level"] ?? 99999;
|
||||
$obj->rule = 'connectIsQQ()';
|
||||
EventManager::addEvent(OnMessageEvent::class, $obj);
|
||||
if ($obb_onebot["single_bot_mode"]) {
|
||||
LightCacheInside::set("connect", "conn_fd", -1);
|
||||
} else {
|
||||
LightCacheInside::set("connect", "conn_fd", -2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function initMySQLPool() {
|
||||
|
||||
@@ -156,7 +156,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"];
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ class ModulePacker
|
||||
*/
|
||||
public function setOutputPath($path) {
|
||||
$this->output_path = $path;
|
||||
if (!is_dir($path)) mkdir($path, 0755, true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,11 +7,12 @@ 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;
|
||||
|
||||
@@ -86,7 +86,7 @@ class ZMUtil
|
||||
if (($pathinfo['extension'] ?? '') == 'php') {
|
||||
if ($rule === null) { //规则未设置回调时候,使用默认的识别过滤规则
|
||||
if (substr(file_get_contents($dir . '/' . $v), 6, 6) == '#plain') continue;
|
||||
elseif (mb_substr($v, 0, 7) == 'global_' || mb_substr($v, 0, 7) == 'script_') continue;
|
||||
elseif (mb_substr($pathinfo["basename"], 0, 7) == 'global_' || mb_substr($pathinfo["basename"], 0, 7) == 'script_') continue;
|
||||
foreach (($composer['autoload']['files'] ?? []) as $fi) {
|
||||
if (md5_file(DataProvider::getSourceRootDir().'/'.$fi) == md5_file($dir.'/'.$v)) continue 2;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user