Compare commits

...

38 Commits
2.2.5 ... 2.2.8

Author SHA1 Message Date
jerry
e977b09e20 Merge remote-tracking branch 'origin/master' 2021-03-02 21:24:53 +08:00
jerry
4ff75cf199 update to 2.2.8 version
update motd message
2021-03-02 21:24:31 +08:00
Whale
24e70c70ce Update deploy-docs.yml 2021-03-02 14:27:55 +08:00
Whale
275a7bf00b Update deploy-docs.yml 2021-03-02 14:26:02 +08:00
Whale
455fc79818 Update deploy-docs.yml 2021-03-02 14:22:40 +08:00
Whale
8740c3c255 Update deploy-docs.yml 2021-03-02 14:19:51 +08:00
Whale
98bfca5bb9 Update deploy-docs.yml 2021-03-02 14:18:40 +08:00
Whale
fc8d01ad5f Update deploy-docs.yml 2021-03-02 13:53:12 +08:00
Whale
d9b8df1725 Update deploy-docs.yml 2021-03-02 13:50:52 +08:00
Whale
9b7802ac04 Update deploy-docs.yml 2021-03-02 13:50:39 +08:00
Whale
6e1f3e0406 Update deploy-docs.yml 2021-03-02 13:43:45 +08:00
Whale
a2d4bab062 Update index.md 2021-03-02 13:40:21 +08:00
Whale
f1cefad910 Create deploy-docs.yml 2021-03-02 13:37:07 +08:00
jerry
957c69bd1e update to 2.2.7 version
fix reply() bug
fix access_token bug
2021-02-27 16:19:18 +08:00
Whale
2902c5e805 Merge pull request #33 from YiwanGi/master
Update ServerEventHandler.php
2021-02-27 16:13:46 +08:00
Wang
faf9f5d988 Update ServerEventHandler.php
-When the token is incorrect, repeated connection events occur in the framework
2021-02-27 00:04:02 +08:00
Whale
874f061468 Update README.md 2021-02-26 11:05:04 +08:00
jerry
69521a1f1f cleanup code, update some features
add Hitokoto API
add Closure for access_token
add working_dir() global function
adjust reply() method to .handle_quick_operation
2021-02-24 23:37:00 +08:00
Whale
fb9dbed306 Merge pull request #32 from wen1014/master
warning bug fix
2021-02-23 23:24:45 +08:00
Whale
d42158ac90 Merge branch 'master' into master 2021-02-23 23:24:24 +08:00
Whale
ff3ebec562 Merge pull request #31 from YiwanGi/patch-8
Update spin-lock.md
2021-02-23 23:22:01 +08:00
wenhao
ea79de617e warning bug fix 2021-02-23 17:04:10 +08:00
Wang
9e1ad6a983 Update spin-lock.md
-Forgotten data content
2021-02-22 19:34:06 +08:00
Whale
c17248df31 Merge pull request #30 from YiwanGi/patch-7
Update light-cache.md
2021-02-22 11:33:52 +08:00
Whale
4c116ebd86 Merge pull request #29 from YiwanGi/patch-5
Update console.md
2021-02-22 11:33:29 +08:00
Whale
c490fe0c1c Merge pull request #28 from YiwanGi/patch-6
Update route-annotations.md
2021-02-22 11:32:46 +08:00
Whale
cefdf23799 Merge pull request #27 from YiwanGi/patch-4
Update README.md
2021-02-22 11:32:06 +08:00
Wang
7f70642606 Update light-cache.md
-Follow up the latest configuration data
2021-02-22 02:51:35 +08:00
Wang
1d5b2609f9 Update console.md 2021-02-22 02:03:22 +08:00
Wang
a206fe8b87 Update route-annotations.md
-Correction of typos
2021-02-22 01:18:12 +08:00
Wang
fb4f6c45ce Update README.md
-Detail optimization
2021-02-22 01:07:35 +08:00
jerry
c50ae245bd commitment, nothing 2021-02-21 22:17:34 +08:00
Whale
f6c2131ebf Merge pull request #26 from YiwanGi/patch-3
Update README.md
2021-02-21 22:15:39 +08:00
Whale
543d1d2922 Merge pull request #25 from YiwanGi/patch-2
Update basic-config.md
2021-02-21 22:14:27 +08:00
Whale
bb61e6f6a2 Merge pull request #24 from YiwanGi/patch-1
Update quickstart-robot.md
2021-02-21 22:13:06 +08:00
YiwanGi
2d1bbf6b48 Update README.md
-Adjust the display format appropriately
-Solve the problem of no access to images in China
2021-02-21 12:48:54 +08:00
YiwanGi
67e42cfe3e Update basic-config.md
-Better display
2021-02-21 11:28:01 +08:00
YiwanGi
429a2cf230 Update quickstart-robot.md
-Better display
2021-02-21 10:48:23 +08:00
27 changed files with 437 additions and 180 deletions

18
.github/workflows/deploy-docs.yml vendored Normal file
View File

@@ -0,0 +1,18 @@
name: ci
on:
push:
branches:
- master
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
- uses: shimataro/ssh-key-action@v2
with:
python-version: 3.x
key: ${{ secrets.VPS_SSH }}
known_hosts: ${{ secrets.KNOWN_HOSTS }}
- run: pip install mkdocs-material
- run: mkdocs build -d distribute && cd distribute/ && ssh root@139.199.77.130 rm -rf /var/www/docs_v2/\* && scp -r ./* root@139.199.77.130:/var/www/docs_v2/

View File

@@ -1,12 +1,12 @@
<div align="center"> <div align="center">
<img src="/resources/images/logo_trans.png" height = "150" alt="炸毛框架"><br> <img src="https://cdn.jsdelivr.net/gh/zhamao-robot/zhamao-framework/resources/images/logo_trans.png" width = "150" height = "150" alt="炸毛框架"><br>
<h2>炸毛框架</h2> <h2>炸毛框架</h2>
炸毛框架 (zhamao-framework) 是一个协程高性能的聊天机器人 + Web 服务器开发框架<br><br> 炸毛框架 (zhamao-framework) 是一个协程高性能的聊天机器人 + Web 服务器开发框架<br><br>
[![作者QQ](https://img.shields.io/badge/作者QQ-627577391-orange.svg)]() [![作者QQ](https://img.shields.io/badge/作者QQ-627577391-orange.svg)](http://wpa.qq.com/msgrd?v=3&uin=627577391&site=qq&menu=yes)
[![zhamao License](https://img.shields.io/hexpm/l/plug.svg?maxAge=2592000)](https://github.com/zhamao-robot/zhamao-framework/blob/master/LICENSE) [![zhamao License](https://img.shields.io/hexpm/l/plug.svg?maxAge=2592000)](https://github.com/zhamao-robot/zhamao-framework/blob/master/LICENSE)
[![Latest Stable Version](http://img.shields.io/packagist/v/zhamao/framework.svg)](https://packagist.org/packages/zhamao/framework) [![Latest Stable Version](http://img.shields.io/packagist/v/zhamao/framework.svg)](https://packagist.org/packages/zhamao/framework)
[![Banner](https://img.shields.io/badge/OneBot-v11-black)]() [![Banner](https://img.shields.io/badge/OneBot-v11-success)](https://github.com/howmanybots/onebot)
[![注解数量](https://img.shields.io/github/search/zhamao-robot/zhamao-framework/AnnotationBase.svg)](https://github.com/zhamao-robot/zhamao-framework/search?q=AnnotationBase) [![注解数量](https://img.shields.io/github/search/zhamao-robot/zhamao-framework/AnnotationBase.svg)](https://github.com/zhamao-robot/zhamao-framework/search?q=AnnotationBase)
[![TODO 数量](https://img.shields.io/github/search/zhamao-robot/zhamao-framework/TODO.svg)](https://github.com/zhamao-robot/zhamao-framework/search?q=TODO) [![TODO 数量](https://img.shields.io/github/search/zhamao-robot/zhamao-framework/TODO.svg)](https://github.com/zhamao-robot/zhamao-framework/search?q=TODO)
@@ -14,7 +14,7 @@
</div> </div>
## 开发者注意 ## 开发者注意
开发者 QQ 群:**670821194** 开发者 QQ 群:**670821194** [点击加入群聊](https://jq.qq.com/?_wv=1027&k=YkNI3AIr)
当前 v2 版本已正式发布,此 master 分支为 2.0 版本,如需查看 v1 版本,请移步 `v1-legacy` 分支! 当前 v2 版本已正式发布,此 master 分支为 2.0 版本,如需查看 v1 版本,请移步 `v1-legacy` 分支!
@@ -44,9 +44,9 @@ public function index() {
框架首先需要部署环境,可以参考下方文档中部署环境和框架的方法进行。 框架首先需要部署环境,可以参考下方文档中部署环境和框架的方法进行。
## 文档v2 版本) ## 文档v2 版本)
查看文档[https://docs-v2.zhamao.xin/](https://docs-v2.zhamao.xin/) 查看文档(国内自建):<https://docs-v2.zhamao.xin/>
备用链接[https://docs-v2.zhamao.me/](https://docs-v2.zhamao.me/) 备用链接(国外托管):<https://docs-v2.zhamao.me/>
自行构建文档:`mkdocs build -d distribute` 自行构建文档:`mkdocs build -d distribute`
@@ -76,7 +76,7 @@ public function index() {
我们会将捐赠的资金用于本项目驱动的炸毛机器人和框架文档的服务器开销上。[捐赠列表](https://github.com/zhamao-robot/thanks) 我们会将捐赠的资金用于本项目驱动的炸毛机器人和框架文档的服务器开销上。[捐赠列表](https://github.com/zhamao-robot/thanks)
### 支付宝 ### 支付宝
![支付宝二维码](/resources/images/alipay_img.jpg) ![支付宝二维码](https://cdn.jsdelivr.net/gh/zhamao-robot/zhamao-framework/resources/images/alipay_img.jpg)
如果你对我们的周边感兴趣,我们还有炸毛机器人定制 logo 的雨伞,详情咨询作者 QQ我们会作为您捐助了本项目 如果你对我们的周边感兴趣,我们还有炸毛机器人定制 logo 的雨伞,详情咨询作者 QQ我们会作为您捐助了本项目
@@ -85,7 +85,7 @@ public function index() {
作者的炸毛机器人已从2018年初起稳定运行了**三年**,并且持续迭代。 作者的炸毛机器人已从2018年初起稳定运行了**三年**,并且持续迭代。
欢迎随时在 HTTP-API 插件群里提问,当然更好的话可以加作者 QQ627577391或提交 Issue 进行疑难解答。 欢迎随时在 HTTP-API 插件群里提问,当然更好的话可以加作者 QQ[627577391](http://wpa.qq.com/msgrd?v=3&uin=627577391&site=qq&menu=yes))或提交 Issue 进行疑难解答。
本项目在更新内容时,请及时关注 GitHub 动态,更新前请将自己的模块代码做好备份。 本项目在更新内容时,请及时关注 GitHub 动态,更新前请将自己的模块代码做好备份。

View File

@@ -1,14 +1,6 @@
#!/usr/bin/env php #!/usr/bin/env php
<?php <?php /** @noinspection PhpIncludeInspection */
if (!is_dir(__DIR__ . '/../vendor')) { require_once ((!is_dir(__DIR__ . '/../vendor')) ? getcwd() : (__DIR__ . "/..")) . "/vendor/autoload.php";
define("LOAD_MODE", 1); //composer项目模式
define("LOAD_MODE_COMPOSER_PATH", getcwd());
/** @noinspection PhpIncludeInspection */
require_once LOAD_MODE_COMPOSER_PATH . "/vendor/autoload.php";
} else {
define("LOAD_MODE", 0); //源码模式
require_once __DIR__ . "/../vendor/autoload.php";
}
(new ZM\ConsoleApplication("zhamao-framework"))->initEnv()->run(); (new ZM\ConsoleApplication("zhamao-framework"))->initEnv()->run();

View File

@@ -3,7 +3,7 @@
"description": "High performance QQ robot and web server development framework", "description": "High performance QQ robot and web server development framework",
"minimum-stability": "stable", "minimum-stability": "stable",
"license": "Apache-2.0", "license": "Apache-2.0",
"version": "2.2.5", "version": "2.2.8",
"extra": { "extra": {
"exclude_annotate": [ "exclude_annotate": [
"src/ZM" "src/ZM"
@@ -26,19 +26,18 @@
], ],
"require": { "require": {
"php": ">=7.2", "php": ">=7.2",
"doctrine/annotations": "~1.10",
"ext-json": "*", "ext-json": "*",
"ext-posix": "*",
"doctrine/annotations": "~1.10",
"psy/psysh": "@stable", "psy/psysh": "@stable",
"symfony/polyfill-ctype": "^1.20", "symfony/polyfill-ctype": "^1.20",
"symfony/polyfill-mbstring": "^1.20", "symfony/polyfill-mbstring": "^1.20",
"symfony/console": "^5.1", "symfony/console": "^5.1",
"symfony/routing": "^5.1",
"zhamao/connection-manager": "*@dev", "zhamao/connection-manager": "*@dev",
"zhamao/console": "^1.0", "zhamao/console": "^1.0",
"zhamao/config": "^1.0", "zhamao/config": "^1.0",
"zhamao/request": "*@dev", "zhamao/request": "*@dev"
"symfony/routing": "^5.1",
"symfony/polyfill-php80": "^1.20",
"ext-posix": "*"
}, },
"suggest": { "suggest": {
"ext-ctype": "*", "ext-ctype": "*",
@@ -56,4 +55,4 @@
"swoole/ide-helper": "@dev", "swoole/ide-helper": "@dev",
"phpunit/phpunit": "^9.5" "phpunit/phpunit": "^9.5"
} }
} }

View File

@@ -1,6 +1,6 @@
______ ______
|__ / |__ __ _ _ __ ___ __ _ ___ |__ / |__ __ _ _ __ ___ __ _ ___
/ /| '_ \ / _` | '_ ` _ \ / _` |/ _ \ / /| '_ \ / _` | '_ ` _ \ / _` |/ _ \
/ /_| | | | (_| | | | | | | (_| | (_) | / /_| | | | (_| | | | | | | (_| | (_) |
/____|_| |_|\__,_|_| |_| |_|\__,_|\___/ /____|_| |_|\__,_|_| |_| |_|\__,_|\___/

View File

@@ -1 +1,3 @@
# FAQ # FAQ
这里会写一些常见的疑难解答。

View File

@@ -25,7 +25,7 @@ vendor/bin/start server --log-error # 以 error 等级启动框架
vendor/bin/start server --log-warning # 以 warning 等级启动框架 vendor/bin/start server --log-warning # 以 warning 等级启动框架
vendor/bin/start server --log-info # 以 info 等级启动框架 vendor/bin/start server --log-info # 以 info 等级启动框架
vendor/bin/start server --log-verbose # 以 verbose 等级启动框架 vendor/bin/start server --log-verbose # 以 verbose 等级启动框架
vendor/bin/start server --log-debug # 以 debug 等级 启动框架 vendor/bin/start server --log-debug # 以 debug 等级启动框架
``` ```
## 使用 Log 输出内容 ## 使用 Log 输出内容
@@ -100,11 +100,9 @@ $str = Console::setColor("I am gold color.", "gold");
炸毛框架支持从终端输入命令来进行一些操作,例如重启框架、停止框架、执行函数等。 炸毛框架支持从终端输入命令来进行一些操作,例如重启框架、停止框架、执行函数等。
::: warning 注意 !!! warning 注意
在 Docker、systemd、daemon 状态下启动的框架会自动关闭终端等待输入,交互不可用。 在 Docker、systemd、daemon 状态下启动的框架会自动关闭终端等待输入,交互不可用。
:::
### reload ### reload
@@ -160,6 +158,8 @@ color green 我是绿色的字
文件位置:`config/motd.txt` 文件位置:`config/motd.txt`
其中,默认的 `Zhamao` 字样的 MOTD 是使用 **figlet** 命令生成的,`figlet "Zhamao"`,你也可以针对自己的机器人名称或品牌进行生成。
## 设置输出主题 ## 设置输出主题
Console 组件支持为多种不同的终端设置不同的主题,比如有些人喜欢使用白色的终端,但是白色终端下 info 的颜色很浅,看不到,还有人使用不能显示颜色的黑白终端..... Console 组件支持为多种不同的终端设置不同的主题,比如有些人喜欢使用白色的终端,但是白色终端下 info 的颜色很浅,看不到,还有人使用不能显示颜色的黑白终端.....

View File

@@ -0,0 +1,37 @@
# 存储管理(文件)
DataProvider 是框架内提供的一个简易的文件管理类。
定义:`\ZM\Utils\DataProvider`
## DataProvider::getWorkingDir()
`working_dir()`
## DataProvider::getFrameworkLink()
`ZMConfig::get("global", "http_reverse_link")`,获取反向代理的链接。
## DataProvider::getDataFolder()
获取配置项 `zm_data` 指定的目录。
## DataProvider::saveToJson()
将变量内容保存为 json 格式的文件,存储在 `zm_data/config/` 目录下或子目录下。
定义:`saveToJson($filename, $file_array)`
`$filename` 是文件名,不需要加后缀,比如你想保存成 `foo/bar.json`,这里写 `foo/bar` 就好。如果不想要二级目录,就直接写 `bar`,不需要加 `.json` 后缀。
这里只支持二级目录,不支持更多级的子目录。
`$file_array` 为内容,一般是数组,比如你缓存了一个 API 接口返回的数据,然后直接解析成数组后丢给它就好了。
## DataProvider::loadFromJson()
从 json 文件加载内容至变量。
定义:`loadFromJson($filename)`
文件名同上 `saveToJson()` 的定义,解析后的返回值为原先的内容或 `null`(如果文件不存在或 json 解析失败)。

View File

@@ -227,5 +227,32 @@ bot()->sendPrivateMsg(123456, "你好啊!!");
// 等同于 ZMRobot::getRandom()->sendPrivateMsg(123456, "你好啊!!"); // 等同于 ZMRobot::getRandom()->sendPrivateMsg(123456, "你好啊!!");
``` ```
## zm_atomic()
获取计时器,效果同 `\ZM\Store\ZMAtomic::get($name)`。
定义:`zm_atmoic($name)`
## uuidgen()
> 2.2.5 版本起可用。
生成一个随机的 uuid支持大写或小写。
定义:`uuidgen($uppercase = false)`
当 `$uppercase` 为 `true` 时,返回的 uuid 中字母都是大写。
## working_dir()
> 2.2.6 版本起可用。
获取框架运行的工作目录。例如你是从 `/root/framework-starter/` 目录启动的框架,`vendor/bin/start server`,那么 `working_dir()` 返回的就是 `/root/framework-starter`。(注意,返回的目录最后没有斜杠,请自行添加。)
## getAllFdByConnectType()
获取同类型的所有连接的描述符 ID。
定义:`getAllFdByConnectType(string $type = 'default'): array`
当 `$type` 为 `qq` 时,则返回所有 OneBot 机器人接入的 WebSocket 连接号。

View File

@@ -25,8 +25,8 @@
```php ```php
/** 轻量字符串缓存,默认开启 */ /** 轻量字符串缓存,默认开启 */
$config['light_cache'] = [ $config['light_cache'] = [
'size' => 1024, //最多允许储存的条数需要2的倍数 'size' => 512, //最多允许储存的条数需要2的倍数
'max_strlen' => 16384, //单行字符串最大长度需要2的倍数 'max_strlen' => 32768, //单行字符串最大长度需要2的倍数
'hash_conflict_proportion' => 0.6, //Hash冲突率越大越好但是需要的内存更多 'hash_conflict_proportion' => 0.6, //Hash冲突率越大越好但是需要的内存更多
'persistence_path' => $config['zm_data'].'_cache.json', 'persistence_path' => $config['zm_data'].'_cache.json',
'auto_save_interval' => 900 'auto_save_interval' => 900

View File

@@ -31,7 +31,7 @@ SpinLock::unlock("foo");
给信号量 `$key` 上锁。如果该信号量已经被上锁,则立刻返回 false。 给信号量 `$key` 上锁。如果该信号量已经被上锁,则立刻返回 false。
```php ```php
SpinLock::lock("foo"); SpinLock::trylock("foo");
``` ```
## 综合实例 ## 综合实例
@@ -70,4 +70,4 @@ public function test() {
## 性能 ## 性能
使用自旋锁几乎没有性能损失,自旋锁要比其他类型的锁性能强很多,在上方举例使用的 `ab` 压测工具测试 100万请求 下使用自旋锁和不适用自旋锁的测试成绩时间分别为7.4s 和 6.9s。 使用自旋锁几乎没有性能损失,自旋锁要比其他类型的锁性能强很多,在上方举例使用的 `ab` 压测工具测试 100万请求 下使用自旋锁和不适用自旋锁的测试成绩时间分别为7.4s 和 6.9s。

View File

@@ -4,7 +4,7 @@
!!! quote "开发提示" !!! quote "开发提示"
本章节涉及的路由和控制器概念可能和其他传统框架有一些出入,而且炸毛框架非绝对根据 PSR 标准进行开发,目的是使用上一些常见的东西尽可能地灵活和不嗦。 本章节涉及的路由和控制器概念可能和其他传统框架有一些出入,而且炸毛框架非绝对根据 PSR 标准进行开发,目的是使用上一些常见的东西尽可能地灵活和不嗦。
## 控制器和路由 ## 控制器和路由
@@ -228,4 +228,4 @@ public function staticImage($param) {
} }
``` ```
这样当用户访问 `http://框架地址/images/aaa.jpg` 就可以快速地调用此路由下的局部文件服务器功能了。 这样当用户访问 `http://框架地址/images/aaa.jpg` 就可以快速地调用此路由下的局部文件服务器功能了。

View File

@@ -4,7 +4,7 @@
!!! error "警告" !!! error "警告"
因为炸毛框架的全局配置中含有数据库名称和密码以及 access_token 等敏感字段,在使用版本控制软件过程中请不要将敏感信息写入配置文件并提交至开源仓库! 因为炸毛框架的全局配置中含有数据库名称和密码以及 access_token 等敏感字段,在使用版本控制软件过程中请不要将敏感信息写入配置文件并提交至开源仓库!
## 全局配置文件 global.php ## 全局配置文件 global.php
@@ -47,12 +47,18 @@
| 配置名称 | 说明 | 默认值 | | 配置名称 | 说明 | 默认值 |
| -------------------------- | ----------------------------------------------- | ---------------------------- | | -------------------------- | ----------------------------------------------- | ---------------------------- |
| `size` | 最多可以缓存的 k-v 条目数(必须是 2 的 n 次方) | 1024 | | `size` | 最多可以缓存的 k-v 条目数(必须是 2 的 n 次方) | 512 |
| `max_strlen` | 作为 value 字符串的最大长度 | 16384 | | `max_strlen` | 作为 value 字符串的最大长度 | 32768 |
| `hash_conflict_proportion` | Hash冲突率越大越好但是需要的内存更多 | 0.6 | | `hash_conflict_proportion` | Hash冲突率越大越好但是需要的内存更多 | 0.6 |
| `persistence_path` | 持久化的键值对的存储路径 | `zm_data` 下的 `_cache.json` | | `persistence_path` | 持久化的键值对的存储路径 | `zm_data` 下的 `_cache.json` |
| `auto_save_interval` | 持久化的键值对自动保存时间间隔(秒) | 900 | | `auto_save_interval` | 持久化的键值对自动保存时间间隔(秒) | 900 |
### 子表 worker_cache
| 配置名称 | 说明 | 默认值 |
| -------- | --------------------------- | ------ |
| `worker` | 跨进程缓存的存储工作进程 id | 0 |
### 子表 **sql_config** ### 子表 **sql_config**
| 配置名称 | 说明 | 默认值 | | 配置名称 | 说明 | 默认值 |
@@ -83,19 +89,13 @@
| `document_root` | 静态文件的根目录 | `{WORKING_DIR}/resources/html` | | `document_root` | 静态文件的根目录 | `{WORKING_DIR}/resources/html` |
| `document_index` | 默认索引的文件名列表 | `["index.html"]` | | `document_index` | 默认索引的文件名列表 | `["index.html"]` |
### 子表 worker_cache
| 配置名称 | 说明 | 默认值 |
| -------- | --------------------------- | ------ |
| `worker` | 跨进程缓存的存储工作进程 id | 0 |
## 多环境下的配置文件 ## 多环境下的配置文件
炸毛框架的配置文件模块支持不同环境下的配置文件,主要结构为 `global.{环境}.php`。在一般情况下,炸毛框架默认从教程引导方式根据指令 `vendor/bin/start server` 启动的框架是不带环境控制的。这章将讲述如何根据不同的环境(production / development / staging来编写配置文件。 炸毛框架的配置文件模块支持不同环境下的配置文件,主要结构为 `global.{环境}.php`。在一般情况下,炸毛框架默认从教程引导方式根据指令 `vendor/bin/start server` 启动的框架是不带环境控制的。这章将讲述如何根据不同的环境development / staging / production)来编写配置文件。
### 使用环境参数 ### 使用环境参数
在启动框架时,额外增加参数 `--env` 可以指定当前的环境,从而使用不同的配置文件。现在框架支持以下几种环境: `production``staging``development` 在启动框架时,额外增加参数 `--env` 可以指定当前的环境,从而使用不同的配置文件。现在框架支持以下几种环境: `development``staging``production`
```bash ```bash
vendor/bin/start server --env=development vendor/bin/start server --env=development
@@ -103,7 +103,7 @@ vendor/bin/start server --env=development
### 不同环境配置文件 ### 不同环境配置文件
由于框架默认只带有 `global.php` 文件,所以假设你现在需要区分开发环境和生产环境的配置,将 `global.php` 文件复制或改名为 `global.development.php``global.production.php` 即可。 由于框架默认只带有 `global.php` 文件,所以假设你现在需要区分开发环境和生产环境的配置,将 `global.php` 文件复制并重命名为 `global.development.php``global.production.php` 即可。
### 优先级 ### 优先级
@@ -146,4 +146,4 @@ $r = ZMConfig::get("example_a", "key1"); # $r == "value1"
$time = ZMConfig::get("example_a", "starttime"); # $time == 服务器启动时间 $time = ZMConfig::get("example_a", "starttime"); # $time == 服务器启动时间
``` ```
同时,自定义配置文件也支持环境变量,例如:`example_a.development.json``example_a.production.php` 均可。 同时,自定义配置文件也支持环境变量,例如:`example_a.development.json``example_a.production.php` 均可。

View File

@@ -82,8 +82,19 @@ cd zhamao-framework-starter
./run-docker.sh # 在正式版炸毛框架 v2 发布后可用,测试版暂不放出 ./run-docker.sh # 在正式版炸毛框架 v2 发布后可用,测试版暂不放出
``` ```
!!! tip "提示"
如果国内 Composer 下载过慢,可以使用阿里云的 Composer 镜像加速。
```bash
# 仅对当前的项目使用阿里云加速
composer config repo.packagist composer https://mirrors.aliyun.com/composer/
# 对全局的 Composer 使用阿里云加速
composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/
```
## 启动框架 ## 启动框架
本地环境启动方式: 本地环境启动方式:
```bash ```bash
cd zhamao-framework-starter cd zhamao-framework-starter

View File

@@ -8,7 +8,7 @@
一切都安装成功后,你就已经做好了进行简单配置以运行一个最小的 **机器人问答模块** 的准备。 一切都安装成功后,你就已经做好了进行简单配置以运行一个最小的 **机器人问答模块** 的准备。
炸毛框架和机器人客户端是什么关系呢?炸毛框架就好比我们传统的一系列例如 Spring 框架、ThinkPHP 框架等,是服务端,而机器人客户端是一个 HTTP / WebSocket 客户端,时刻准备着连接到炸毛框架 炸毛框架和机器人客户端是什么关系呢?炸毛框架就好比我们传统的一系列例如 Spring 框架、ThinkPHP 框架等,是服务端,而机器人客户端是一个 HTTP / WebSocket 客户端,时刻准备着连接到炸毛框架。
## 机器人客户端 ## 机器人客户端
@@ -30,51 +30,6 @@ OneBot 机器人部分的选择详情见 [OneBot 实例](/guide/OneBot实例/)
由于 go-cqhttp 项目还处于开发期,而且配置文件格式也发生了多次变化,但大体内容没有变(比如编写此文档时发布的版本中配置文件格式变成了 `hjson` 取代了原来的 `json` 由于 go-cqhttp 项目还处于开发期,而且配置文件格式也发生了多次变化,但大体内容没有变(比如编写此文档时发布的版本中配置文件格式变成了 `hjson` 取代了原来的 `json`
=== "config.json旧格式"
``` json hl_lines="2 3 30 31"
{
"uin": 你的QQ号,
"password": "你的密码",
"encrypt_password": false,
"password_encrypted": "",
"enable_db": true,
"access_token": "",
"relogin": {
"enabled": true,
"relogin_delay": 3,
"max_relogin_times": 0
},
"ignore_invalid_cqcode": false,
"force_fragmented": true,
"heartbeat_interval": 0,
"http_config": {
"enabled": false,
"host": "0.0.0.0",
"port": 5700,
"timeout": 0,
"post_urls": {}
},
"ws_config": {
"enabled": false,
"host": "0.0.0.0",
"port": 6700
},
"ws_reverse_servers": [
{
"enabled": true,
"reverse_url": "ws://127.0.0.1:20001/",
"reverse_api_url": "",
"reverse_event_url": "",
"reverse_reconnect_interval": 3000
}
],
"post_message_format": "string",
"debug": false,
"log_level": ""
}
```
=== "config.hjson新格式" === "config.hjson新格式"
``` json hl_lines="3 5 81 84" ``` json hl_lines="3 5 81 84"
@@ -193,6 +148,51 @@ OneBot 机器人部分的选择详情见 [OneBot 实例](/guide/OneBot实例/)
} }
``` ```
=== "config.json旧格式"
``` json hl_lines="2 3 30 31"
{
"uin": 你的QQ号,
"password": "你的密码",
"encrypt_password": false,
"password_encrypted": "",
"enable_db": true,
"access_token": "",
"relogin": {
"enabled": true,
"relogin_delay": 3,
"max_relogin_times": 0
},
"ignore_invalid_cqcode": false,
"force_fragmented": true,
"heartbeat_interval": 0,
"http_config": {
"enabled": false,
"host": "0.0.0.0",
"port": 5700,
"timeout": 0,
"post_urls": {}
},
"ws_config": {
"enabled": false,
"host": "0.0.0.0",
"port": 6700
},
"ws_reverse_servers": [
{
"enabled": true,
"reverse_url": "ws://127.0.0.1:20001/",
"reverse_api_url": "",
"reverse_event_url": "",
"reverse_reconnect_interval": 3000
}
],
"post_message_format": "string",
"debug": false,
"log_level": ""
}
```
其中 ws://127.0.0.1:20001/ 中的 127.0.0.1 和 20001 应分别对应炸毛框架配置的 HOST 和 PORT 其中 ws://127.0.0.1:20001/ 中的 127.0.0.1 和 20001 应分别对应炸毛框架配置的 HOST 和 PORT
## 第一次对话 ## 第一次对话
@@ -229,8 +229,8 @@ public function repeat() {
( 你好啊 ( 你好啊
) echo ) echo
( 请输入你要回复的内容 ( 请输入你要回复的内容
) 哦豁,完蛋 ) 哦豁
( 哦豁,完蛋 ( 哦豁
</chat-box> </chat-box>
> 如果你只回复 `echo` 的话,它会先和你进入一个会话状态,并问你 `请输入你要回复的内容`,这时你再次说一些内容例如 `哦豁`,会回复你 `哦豁`。效果和直接输入 `echo 哦豁` 是一致的,这是炸毛框架内的一个封装好的命令参数对话询问功能。有关参数询问功能,请看后面的进阶模块。 > 如果你只回复 `echo` 的话,它会先和你进入一个会话状态,并问你 `请输入你要回复的内容`,这时你再次说一些内容例如 `哦豁`,会回复你 `哦豁`。效果和直接输入 `echo 哦豁` 是一致的,这是炸毛框架内的一个封装好的命令参数对话询问功能。有关参数询问功能,请看后面的进阶模块。

View File

@@ -8,14 +8,13 @@
编写文档需要较大精力,你也可以参与到本文档的建设中来,比如找错字,增加或更正内容,每页文档可直接点击右上方铅笔图标直接跳转至 GitHub 进行编辑,编辑后自动 Fork 并生成 Pull Request以此来贡献此文档 编写文档需要较大精力,你也可以参与到本文档的建设中来,比如找错字,增加或更正内容,每页文档可直接点击右上方铅笔图标直接跳转至 GitHub 进行编辑,编辑后自动 Fork 并生成 Pull Request以此来贡献此文档
炸毛框架使用 PHP 编写,采用 Swoole 扩展为基础,主要面向 API 服务聊天机器人OneBot 标准的机器人对接),包含 WebSocket、HTTP 等监听和请求库,用户代码采用模块化处理,使用注解可以方便地编写各类功能。
炸毛框架使用 PHP 编写,采用 Swoole 扩展为基础,主要面向 API 服务聊天机器人CQHTTP 对接),包含 websocket、http 等监听和请求库,用户代码采用模块化处理,使用注解可以方便地编写各类功能 框架主要用途为 HTTP/WS 服务器,机器人搭建框架。尤其对于聊天机器人消息处理较为方便和全面,提供了众多会话机制和内部调用机制,可以以各种方式设计你自己的模块
框架主要用途为 HTTP 服务器,机器人搭建框架。尤其对于 QQ 机器人消息处理较为方便和全面,提供了众多会话机制和内部调用机制,可以以各种方式设计你自己的模块。
在 HTTP 和 WebSocket 服务器上PHP 的扩展 Swoole 提供了高性能的支持,使其效率可媲美 nginx 静态网页处理的效率。 在 HTTP 和 WebSocket 服务器上PHP 的扩展 Swoole 提供了高性能的支持,使其效率可媲美 nginx 静态网页处理的效率。
此外QQ 机器人方面此框架基于 OneBot 标准的反向 WebSocket 连接,比传统 HTTP 通信更快,未来也会兼容微信公众号开发者模式 此外QQ 机器人方面此框架基于 OneBot 标准的反向 WebSocket 连接,比传统 HTTP 通信更快。
```php ```php
/** /**
@@ -39,9 +38,9 @@ public function index() {
首先,你需要了解你需要知道哪些事情才能开始着手使用框架: 首先,你需要了解你需要知道哪些事情才能开始着手使用框架:
1. Linux 命令行(会跑 Linux 程序) 1. Linux 命令行(会跑 Linux 程序)
2. php 7.2+ 开发环境 2. php 7.2+ 开发环境(项目会持续支持最新的 PHP 版本)
3. HTTP 协议(可选) 3. HTTP/WebSocket 协议
4. OneBot 机器人聊天接口标准(可选) 4. OneBot 机器人聊天接口标准
需要值得注意的是,本教程中所涉及的内容均为尽可能翻译为白话的方式进行描述,但对于框架的组件或事件等需要单独拆分说明文档的部分则需要足够详细,所以本教程提供一个快速上手的教程,并且会将最典型的安装方式写到快速教程篇。 需要值得注意的是,本教程中所涉及的内容均为尽可能翻译为白话的方式进行描述,但对于框架的组件或事件等需要单独拆分说明文档的部分则需要足够详细,所以本教程提供一个快速上手的教程,并且会将最典型的安装方式写到快速教程篇。

View File

@@ -1,8 +1,47 @@
# 更新日志v2 版本) # 更新日志v2 版本)
## v2.2.8
> 更新时间2021.3.2
- 更新MOTD 显示的方式,更加直观和炫酷
## v2.2.7
> 更新时间2021.2.27
- 修复2.2.6 版本下 `reply()` 方法在群里调用会 at 成员的 bug
- 修复:空 `access_token` 的情况下会无法连入的 bug
- 修复:使用 Closure 闭包函数自行编写逻辑的判断返回 false 无法阻断连接的 bug
## v2.2.6
> 更新时间2021.2.26
- 新增:`uuidgen()` 全局函数,快速生成 uuid
- 修复MySQL `rawQuery()` 在参数为非数组时会报 Warning 的 bug
- 新增:示例模块的 API 示例:一言查询
- 优化:删减部分无用代码
- 更改:`ctx()->reply()` 方法改为调用隐藏方法:`.handle_quick_operation`
- 修复:`ctx()->finalReply()` 一直以来的 bug未阻断事件
- 新增:`access_token` 配置项支持闭包函数自行设计判断方式和逻辑
- 新增:全局函数 `working_dir()`
## v2.2.5
> 更新时间2021.2.20
- 新增:`saveToJson()``loadFromJson()` 方法DataProvider 类)
- 修复:`@OnSave` 注解事件无法工作的 bug
- 调整:自定义计时器创建时的性能调优
- 新增WorkerCache 方法:`hasKey()`
- 新增SpinLock 方法:`transaction()`(直接在事务中上锁)
- 新增CQ 方法:`getAllCQ()``_custom()`(获取消息中的所有 CQ 码)
- 修复CQ 类中的部分 bug
## v2.2.4 ## v2.2.4
> 更新事件2021.2.7 > 更新时间2021.2.7
- 修复:终端交互导致的 ssh 断掉后 CPU 占用过高的问题 - 修复:终端交互导致的 ssh 断掉后 CPU 占用过高的问题
- 修复WorkerCache 在缺少配置文件下工作异常的问题 - 修复WorkerCache 在缺少配置文件下工作异常的问题

View File

@@ -10,8 +10,8 @@ theme:
favicon: assets/favicon.png favicon: assets/favicon.png
language: zh language: zh
palette: palette:
primary: red primary: indigo
accent: red accent: indigo
features: features:
- navigation.tabs - navigation.tabs
extra_javascript: extra_javascript:
@@ -81,6 +81,7 @@ nav:
- Redis 数据库: component/redis.md - Redis 数据库: component/redis.md
- ZMAtomic 原子计数器: component/atomics.md - ZMAtomic 原子计数器: component/atomics.md
- SpinLock 自旋锁: component/spin-lock.md - SpinLock 自旋锁: component/spin-lock.md
- 文件管理: component/data-provider.md
- 协程池: component/coroutine-pool.md - 协程池: component/coroutine-pool.md
- 单例类: component/singleton-trait.md - 单例类: component/singleton-trait.md
- ZMUtil 杂项: component/zmutil.md - ZMUtil 杂项: component/zmutil.md

View File

@@ -11,6 +11,7 @@ use ZM\Console\Console;
use ZM\Annotation\CQ\CQCommand; use ZM\Annotation\CQ\CQCommand;
use ZM\Annotation\Http\RequestMapping; use ZM\Annotation\Http\RequestMapping;
use ZM\Event\EventDispatcher; use ZM\Event\EventDispatcher;
use ZM\Requests\ZMRequest;
use ZM\Utils\ZMUtil; use ZM\Utils\ZMUtil;
/** /**
@@ -45,6 +46,18 @@ class Hello
return "你好啊,我是由炸毛框架构建的机器人!"; return "你好啊,我是由炸毛框架构建的机器人!";
} }
/**
* 一个最基本的第三方 API 接口使用示例
* @CQCommand("一言")
*/
public function hitokoto() {
$api_result = ZMRequest::get("https://v1.hitokoto.cn/");
if ($api_result === false) return "接口请求出错,请稍后再试!";
$obj = json_decode($api_result, true);
if ($obj === null) return "接口解析出错!可能返回了非法数据!";
return $obj["hitokoto"] . "\n----「" . $obj["from"] . "";
}
/** /**
* 一个简单随机数的功能demo * 一个简单随机数的功能demo
* 问法1随机数 1 20 * 问法1随机数 1 20
@@ -89,7 +102,7 @@ class Hello
* @return string * @return string
*/ */
public function paramGet($param) { public function paramGet($param) {
return "Hello, ".$param["name"]; return "Hello, " . $param["name"];
} }
/** /**

View File

@@ -39,7 +39,6 @@ class RunServerCommand extends Command
return Command::FAILURE; return Command::FAILURE;
} }
} }
if (LOAD_MODE == 0) echo "* This is repository mode.\n";
(new Framework($input->getOptions()))->start(); (new Framework($input->getOptions()))->start();
return Command::SUCCESS; return Command::SUCCESS;
} }

View File

@@ -26,23 +26,21 @@ class ConsoleApplication extends Application
public function initEnv() { public function initEnv() {
$this->selfCheck(); $this->selfCheck();
if (!is_dir(__DIR__ . '/../../vendor')) {
define("LOAD_MODE", 1); // composer项目模式
define("LOAD_MODE_COMPOSER_PATH", getcwd());
} else {
define("LOAD_MODE", 0); // 源码模式
}
//if (LOAD_MODE === 0) $this->add(new BuildCommand()); //只有在git源码模式才能使用打包指令 //if (LOAD_MODE === 0) $this->add(new BuildCommand()); //只有在git源码模式才能使用打包指令
if (LOAD_MODE === 0) define("WORKING_DIR", getcwd()); if (LOAD_MODE === 0) define("WORKING_DIR", getcwd());
elseif (LOAD_MODE == 1) define("WORKING_DIR", realpath(__DIR__ . "/../../")); elseif (LOAD_MODE == 1) define("WORKING_DIR", realpath(__DIR__ . "/../../"));
elseif (LOAD_MODE == 2) echo "Phar mode: " . WORKING_DIR . PHP_EOL;
if (file_exists(DataProvider::getWorkingDir() . "/vendor/autoload.php")) { if (file_exists(DataProvider::getWorkingDir() . "/vendor/autoload.php")) {
/** @noinspection PhpIncludeInspection */ /** @noinspection PhpIncludeInspection */
require_once DataProvider::getWorkingDir() . "/vendor/autoload.php"; require_once DataProvider::getWorkingDir() . "/vendor/autoload.php";
} }
if (LOAD_MODE == 2) { if (LOAD_MODE == 0) {
// Phar 模式2.0 不提供哦
//require_once FRAMEWORK_DIR . "/vendor/autoload.php";
spl_autoload_register('phar_classloader');
} elseif (LOAD_MODE == 0) {
/** @noinspection PhpIncludeInspection
* @noinspection RedundantSuppression
*/
require_once WORKING_DIR . "/vendor/autoload.php";
$composer = json_decode(file_get_contents(DataProvider::getWorkingDir() . "/composer.json"), true); $composer = json_decode(file_get_contents(DataProvider::getWorkingDir() . "/composer.json"), true);
if (!isset($composer["autoload"]["psr-4"]["Module\\"])) { if (!isset($composer["autoload"]["psr-4"]["Module\\"])) {
echo "框架源码模式需要在autoload文件中添加Module目录为自动加载是否添加[Y/n] "; echo "框架源码模式需要在autoload文件中添加Module目录为自动加载是否添加[Y/n] ";
@@ -97,14 +95,9 @@ class ConsoleApplication extends Application
} }
private function selfCheck() { private function selfCheck() {
if (!extension_loaded("swoole")) die("Can not find swoole extension.\nSee: https://github.com/zhamao-robot/zhamao-framework/issues/19"); if (!extension_loaded("swoole")) die("Can not find swoole extension.\nSee: https://github.com/zhamao-robot/zhamao-framework/issues/19\n");
if (version_compare(SWOOLE_VERSION, "4.4.13") == -1) die("You must install swoole version >= 4.4.13 !"); if (version_compare(SWOOLE_VERSION, "4.4.13") == -1) die("You must install swoole version >= 4.4.13 !");
//if (!extension_loaded("gd")) die("Can not find gd extension.\n"); if (version_compare(PHP_VERSION, "7.2") == -1) die("PHP >= 7.2 required.");
//if (!extension_loaded("sockets")) die("Can not find sockets extension.\n");
if (substr(PHP_VERSION, 0, 1) < "7") die("PHP >=7 required.\n");
//if (!function_exists("curl_exec")) die("Can not find curl extension.\n");
//if (!class_exists("ZipArchive")) die("Can not find Zip extension.\n");
//if (!file_exists(CRASH_DIR . "last_error.log")) die("Can not find log file.\n");
return true; return true;
} }
} }

View File

@@ -12,6 +12,7 @@ use swoole_server;
use ZM\ConnectionManager\ConnectionObject; use ZM\ConnectionManager\ConnectionObject;
use ZM\ConnectionManager\ManagerGM; use ZM\ConnectionManager\ManagerGM;
use ZM\Console\Console; use ZM\Console\Console;
use ZM\Event\EventDispatcher;
use ZM\Exception\InvalidArgumentException; use ZM\Exception\InvalidArgumentException;
use ZM\Exception\WaitTimeoutException; use ZM\Exception\WaitTimeoutException;
use ZM\Http\Response; use ZM\Http\Response;
@@ -103,30 +104,31 @@ class Context implements ContextInterface
* @param $msg * @param $msg
* @param bool $yield * @param bool $yield
* @return mixed * @return mixed
* @noinspection PhpMissingBreakStatementInspection
*/ */
public function reply($msg, $yield = false) { public function reply($msg, $yield = false) {
switch ($this->getData()["message_type"]) { switch ($this->getData()["message_type"]) {
case "group": case "group":
$operation["at_sender"] = false;
// no break
case "private": case "private":
case "discuss": case "discuss":
$this->setCache("has_reply", true); $this->setCache("has_reply", true);
$data = $this->getData(); $data = $this->getData();
$conn = $this->getConnection(); $conn = $this->getConnection();
switch ($data["message_type"]) { $operation["reply"] = $msg;
case "group": return (new ZMRobot($conn))->setCallback($yield)->callExtendedAPI(".handle_quick_operation", [
return (new ZMRobot($conn))->setCallback($yield)->sendGroupMsg($data["group_id"], $msg); "context" => $data,
case "private": "operation" => $operation
return (new ZMRobot($conn))->setCallback($yield)->sendPrivateMsg($data["user_id"], $msg); ]);
}
return null;
} }
return false; return false;
} }
public function finalReply($msg, $yield = false) { public function finalReply($msg, $yield = false) {
self::$context[$this->cid]["cache"]["block_continue"] = true; self::$context[$this->cid]["cache"]["block_continue"] = true;
if ($msg == "") return true; if ($msg != "") $this->reply($msg, $yield);
return $this->reply($msg, $yield); EventDispatcher::interrupt();
} }
/** /**

View File

@@ -84,6 +84,7 @@ class DB
* @throws DbException * @throws DbException
*/ */
public static function rawQuery(string $line, $params = [], $fetch_mode = ZM_DEFAULT_FETCH_MODE) { public static function rawQuery(string $line, $params = [], $fetch_mode = ZM_DEFAULT_FETCH_MODE) {
if (!is_array($params)) $params = [$params];
Console::debug("MySQL: " . $line . " | " . implode(", ", $params)); Console::debug("MySQL: " . $line . " | " . implode(", ", $params));
try { try {
$conn = SqlPoolStorage::$sql_pool->get(); $conn = SqlPoolStorage::$sql_pool->get();

View File

@@ -6,6 +6,7 @@
namespace ZM\Event; namespace ZM\Event;
use Closure;
use Co; use Co;
use Error; use Error;
use Exception; use Exception;
@@ -410,9 +411,16 @@ class ServerEventHandler
Console::debug("Calling Swoole \"open\" event from fd=" . $request->fd); Console::debug("Calling Swoole \"open\" event from fd=" . $request->fd);
unset(Context::$context[Co::getCid()]); unset(Context::$context[Co::getCid()]);
$type = strtolower($request->header["x-client-role"] ?? $request->get["type"] ?? ""); $type = strtolower($request->header["x-client-role"] ?? $request->get["type"] ?? "");
$access_token = explode(" ", $request->header["authorization"] ?? $request->get["token"] ?? "")[1] ?? ""; $access_token = explode(" ", $request->header["authorization"] ?? "")[1] ?? $request->get["token"] ?? "";
if (($a = ZMConfig::get("global", "access_token")) != "") { $token = ZMConfig::get("global", "access_token");
if ($access_token !== $a) { if ($token instanceof Closure) {
if (!$token($access_token)) {
$server->close($request->fd);
Console::warning("Unauthorized access_token: " . $access_token);
return;
}
} elseif (is_string($token)) {
if ($access_token !== $token && $token !== "") {
$server->close($request->fd); $server->close($request->fd);
Console::warning("Unauthorized access_token: " . $access_token); Console::warning("Unauthorized access_token: " . $access_token);
return; return;

View File

@@ -9,6 +9,7 @@ use Exception;
use ZM\Annotation\Swoole\OnSetup; use ZM\Annotation\Swoole\OnSetup;
use ZM\Config\ZMConfig; use ZM\Config\ZMConfig;
use ZM\ConnectionManager\ManagerGM; use ZM\ConnectionManager\ManagerGM;
use ZM\Console\TermColor;
use ZM\Event\ServerEventHandler; use ZM\Event\ServerEventHandler;
use ZM\Store\LightCache; use ZM\Store\LightCache;
use ZM\Store\LightCacheInside; use ZM\Store\LightCacheInside;
@@ -87,35 +88,37 @@ class Framework
$this->parseCliArgs(self::$argv); $this->parseCliArgs(self::$argv);
$out = [
"host" => ZMConfig::get("global", "host"),
"port" => ZMConfig::get("global", "port"),
"log_level" => Console::getLevel(),
"version" => ZM_VERSION,
"config" => $args["env"] === null ? 'global.php' : $args["env"]
];
if (APP_VERSION !== "unknown") $out["app_version"] = APP_VERSION;
if (isset(ZMConfig::get("global", "swoole")["task_worker_num"])) {
$out["task_worker_num"] = ZMConfig::get("global", "swoole")["task_worker_num"];
}
if (($num = ZMConfig::get("global", "swoole")["worker_num"] ?? swoole_cpu_num()) != 1) {
$out["worker_num"] = $num;
}
$out["working_dir"] = DataProvider::getWorkingDir();
Console::printProps($out, $tty_width);
self::$server->set($this->server_set); self::$server->set($this->server_set);
if (file_exists(DataProvider::getWorkingDir() . "/config/motd.txt")) {
$motd = file_get_contents(DataProvider::getWorkingDir() . "/config/motd.txt"); // 打印初始信息
} else { $out["listen"] = ZMConfig::get("global", "host") . ":" . ZMConfig::get("global", "port");
$motd = file_get_contents(__DIR__ . "/../../config/motd.txt"); if (!isset(ZMConfig::get("global", "swoole")["worker_num"])) $out["worker"] = swoole_cpu_num() . " (auto)";
else $out["worker"] = ZMConfig::get("global", "swoole")["worker_num"];
$out["env"] = $args["env"] === null ? "default" : $args["env"];
$out["log_level"] = Console::getLevel();
$out["version"] = ZM_VERSION;
if (APP_VERSION !== "unknown") $out["app_version"] = APP_VERSION;
if (isset(ZMConfig::get("global", "swoole")["task_worker_num"])) {
$out["task_worker"] = ZMConfig::get("global", "swoole")["task_worker_num"];
} }
$motd = explode("\n", $motd); if (ZMConfig::get("global", "sql_config")["sql_host"] !== "") {
foreach ($motd as $k => $v) { $conf = ZMConfig::get("global", "sql_config");
$motd[$k] = substr($v, 0, $tty_width); $out["mysql_pool"] = $conf["database"] . "@" . $conf["sql_host"] . ":" . $conf["sql_port"];
} }
$motd = implode("\n", $motd); if (ZMConfig::get("global", "redis_config")["host"] !== "") {
echo $motd; $conf = ZMConfig::get("global", "redis_config");
$out["redis_pool"] = $conf["host"] . ":" . $conf["port"];
}
if (ZMConfig::get("global", "static_file_server")["status"] !== false) {
$out["static_file_server"] = "enabled";
}
$out["working_dir"] = DataProvider::getWorkingDir();
self::printProps($out, $tty_width, $args["log-theme"] === null);
self::printMotd($tty_width);
global $asd; global $asd;
$asd = get_included_files(); $asd = get_included_files();
// 注册 Swoole Server 的事件 // 注册 Swoole Server 的事件
@@ -171,6 +174,20 @@ class Framework
} }
} }
private static function printMotd($tty_width) {
if (file_exists(DataProvider::getWorkingDir() . "/config/motd.txt")) {
$motd = file_get_contents(DataProvider::getWorkingDir() . "/config/motd.txt");
} else {
$motd = file_get_contents(__DIR__ . "/../../config/motd.txt");
}
$motd = explode("\n", $motd);
foreach ($motd as $k => $v) {
$motd[$k] = substr($v, 0, $tty_width);
}
$motd = implode("\n", $motd);
echo $motd;
}
public function start() { public function start() {
self::$server->start(); self::$server->start();
} }
@@ -223,16 +240,7 @@ class Framework
private function parseCliArgs($args) { private function parseCliArgs($args) {
$coroutine_mode = true; $coroutine_mode = true;
global $terminal_id; global $terminal_id;
$terminal_id = call_user_func(function () { $terminal_id = uuidgen();
try {
$data = random_bytes(16);
} catch (Exception $e) {
return "";
}
$data[6] = chr(ord($data[6]) & 0x0f | 0x40);
$data[8] = chr(ord($data[8]) & 0x3f | 0x80);
return strtoupper(vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4)));
});
foreach ($args as $x => $y) { foreach ($args as $x => $y) {
switch ($x) { switch ($x) {
case 'disable-coroutine': case 'disable-coroutine':
@@ -289,6 +297,95 @@ class Framework
if ($coroutine_mode) Runtime::enableCoroutine(true, SWOOLE_HOOK_ALL); if ($coroutine_mode) Runtime::enableCoroutine(true, SWOOLE_HOOK_ALL);
} }
private static function writeNoDouble($k, $v, &$line_data, &$line_width, &$current_line, $colorful, $max_border) {
$tmp_line = $k . ": " . $v;
//Console::info("写入[".$tmp_line."]");
if (strlen($tmp_line) >= $line_width[$current_line]) { //输出的内容太多了,以至于一行都放不下一个,要折行
$title_strlen = strlen($k . ": ");
$content_len = $line_width[$current_line] - $title_strlen;
$line_data[$current_line] = " " . $k . ": ";
if ($colorful) $line_data[$current_line] .= TermColor::color8(32);
$line_data[$current_line] .= substr($v, 0, $content_len);
if ($colorful) $line_data[$current_line] .= TermColor::RESET;
$rest = substr($v, $content_len);
++$current_line; // 带标题的第一行满了,折到第二行
do {
if ($colorful) $line_data[$current_line] = TermColor::color8(32);
$line_data[$current_line] .= " " . substr($rest, 0, $max_border - 2);
if ($colorful) $line_data[$current_line] .= TermColor::RESET;
$rest = substr($rest, $max_border - 2);
++$current_line;
} while ($rest > $max_border - 2); // 循环,直到放完
} else { // 不需要折行
//Console::info("不需要折行");
$line_data[$current_line] = " " . $k . ": ";
if ($colorful) $line_data[$current_line] .= TermColor::color8(32);
$line_data[$current_line] .= $v;
if ($colorful) $line_data[$current_line] .= TermColor::RESET;
if ($max_border >= 57) {
if (strlen($tmp_line) >= intval(($max_border - 2) / 2)) { // 不需要折行,直接输出一个转下一行
//Console::info("不需要折行,直接输出一个转下一行");
++$current_line;
} else { // 输出很小,写到前面并分片
//Console::info("输出很小,写到前面并分片");
$space = intval($max_border / 2) - 2 - strlen($tmp_line);
$line_data[$current_line] .= str_pad("", $space, " ");
$line_data[$current_line] .= "| "; // 添加分片
$line_width[$current_line] -= (strlen($tmp_line) + 3 + $space);
}
} else {
++$current_line;
}
}
}
public static function printProps($out, $tty_width, $colorful = true) {
$max_border = $tty_width < 65 ? $tty_width : 65;
if (LOAD_MODE == 0) echo Console::setColor("* Framework started with source mode.\n", $colorful ? "yellow" : "");
echo str_pad("", $max_border, "=") . PHP_EOL;
$current_line = 0;
$line_width = [];
$line_data = [];
foreach ($out as $k => $v) {
if (!isset($line_width[$current_line])) {
$line_width[$current_line] = $max_border - 2;
}
//Console::info("行宽[$current_line]".$line_width[$current_line]);
if ($max_border >= 57) { // 很宽的时候,一行能放两个短行
if ($line_width[$current_line] == ($max_border - 2)) { //空行
self::writeNoDouble($k, $v, $line_data, $line_width, $current_line, $colorful, $max_border);
} else { // 不是空行,已经有东西了
$tmp_line = $k . ": " . $v;
//Console::info("[$current_line]即将插入后面的东西[".$tmp_line."]");
if (strlen($tmp_line) > $line_width[$current_line]) { // 地方不够,另起一行
$line_data[$current_line] = str_replace("| ", "", $line_data[$current_line]);
++$current_line;
$line_data[$current_line] = " " . $k . ": ";
if ($colorful) $line_data[$current_line] .= TermColor::color8(32);
$line_data[$current_line] .= $v;
if ($colorful) $line_data[$current_line] .= TermColor::RESET;
++$current_line;
} else { // 地方够,直接写到后面并另起一行
$line_data[$current_line] .= $k . ": ";
if ($colorful) $line_data[$current_line] .= TermColor::color8(32);
$line_data[$current_line] .= $v;
if ($colorful) $line_data[$current_line] .= TermColor::RESET;
++$current_line;
}
}
} else { // 不够宽,直接写单行
self::writeNoDouble($k, $v, $line_data, $line_width, $current_line, $colorful, $max_border);
}
}
foreach ($line_data as $v) {
echo $v . PHP_EOL;
}
echo str_pad("", $max_border, "=") . PHP_EOL;
}
public static function getTtyWidth() { public static function getTtyWidth() {
return explode(" ", trim(exec("stty size")))[1]; return explode(" ", trim(exec("stty size")))[1];
} }

View File

@@ -6,7 +6,7 @@ use ZM\Utils\DataProvider;
define("ZM_START_TIME", microtime(true)); define("ZM_START_TIME", microtime(true));
define("ZM_DATA", ZMConfig::get("global", "zm_data")); define("ZM_DATA", ZMConfig::get("global", "zm_data"));
define("ZM_VERSION", json_decode(file_get_contents(__DIR__ . "/../../composer.json"), true)["version"] ?? "unknown"); define("ZM_VERSION", json_decode(file_get_contents(__DIR__ . "/../../composer.json"), true)["version"] ?? "unknown");
define("APP_VERSION", json_decode(file_get_contents(DataProvider::getWorkingDir() . "/composer.json"), true)["version"] ?? "unknown"); define("APP_VERSION", LOAD_MODE == 1 ? (json_decode(file_get_contents(DataProvider::getWorkingDir() . "/composer.json"), true)["version"] ?? "unknown") : "unknown");
define("CRASH_DIR", ZMConfig::get("global", "crash_dir")); define("CRASH_DIR", ZMConfig::get("global", "crash_dir"));
@mkdir(ZM_DATA); @mkdir(ZM_DATA);
@mkdir(CRASH_DIR); @mkdir(CRASH_DIR);

View File

@@ -317,4 +317,23 @@ function getAllFdByConnectType(string $type = 'default'): array {
function zm_atomic($name) { function zm_atomic($name) {
return \ZM\Store\ZMAtomic::get($name); return \ZM\Store\ZMAtomic::get($name);
} }
function uuidgen($uppercase = false) {
try {
$data = random_bytes(16);
} catch (Exception $e) {
return "";
}
$data[6] = chr(ord($data[6]) & 0x0f | 0x40);
$data[8] = chr(ord($data[8]) & 0x3f | 0x80);
return $uppercase ? strtoupper(vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4))) :
vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
}
function working_dir() {
if (LOAD_MODE == 0) return WORKING_DIR;
elseif (LOAD_MODE == 1) return LOAD_MODE_COMPOSER_PATH;
elseif (LOAD_MODE == 2) return realpath('.');
return null;
}