mirror of
https://github.com/zhamao-robot/zhamao-framework.git
synced 2026-07-03 14:55:36 +08:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dea9ed2ccd | ||
|
|
de5744c9e4 | ||
|
|
a23f3d8f16 | ||
|
|
0c24bfdedd | ||
|
|
c0b95c6840 |
18
.github/workflows/deploy-docs.yml
vendored
18
.github/workflows/deploy-docs.yml
vendored
@@ -1,18 +0,0 @@
|
||||
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/
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -10,4 +10,5 @@ composer.lock
|
||||
/bin/.phpunit.result.cache
|
||||
/resources/zhamao.service
|
||||
.phpunit.result.cache
|
||||
.daemon_pid
|
||||
.daemon_pid
|
||||
/runtime/
|
||||
226
build-runtime.sh
Executable file
226
build-runtime.sh
Executable file
@@ -0,0 +1,226 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
_php_ver="7.4.16"
|
||||
_libiconv_ver="1.15"
|
||||
_openssl_ver="1.1.1j"
|
||||
_swoole_ver="4.6.3"
|
||||
_home_dir=$(pwd)"/"
|
||||
|
||||
function checkEnv() {
|
||||
echo -n "检测核心组件... "
|
||||
_msg="请通过包管理安装此依赖!"
|
||||
type git >/dev/null 2>&1 || { echo "失败,git 不存在!"$_msg; return 1; }
|
||||
type gcc >/dev/null 2>&1 || { echo "失败,gcc 不存在!"$_msg; return 1; }
|
||||
type g++ >/dev/null 2>&1 || { echo "失败,g++ 不存在!"$_msg; return 1; }
|
||||
type unzip >/dev/null 2>&1 || { echo "失败,unzip 不存在!"$_msg; return 1; }
|
||||
type autoconf >/dev/null 2>&1 || { echo "失败,autoconf 不存在!"; return 1; }
|
||||
type pkg-config >/dev/null 2>&1 || { echo "失败,pkg-config 不存在!"$_msg; return 1; }
|
||||
type wget >/dev/null 2>&1 || type curl >/dev/null 2>&1 || { echo "失败,curl/wget 不存在!"$_msg; return 1; }
|
||||
echo "完成!"
|
||||
echo "如果下载过程中出现错误,请删除 runtime/ 文件夹重试!"
|
||||
echo "此脚本安装的php/swoole均为最小版本,不含其他扩展(如zip、xml、gd)等!"
|
||||
echo -n "如果编译过程缺少依赖,请通过包管理安装对应的依赖![按回车继续] "
|
||||
# shellcheck disable=SC2034
|
||||
read ents
|
||||
}
|
||||
|
||||
function downloadIt() {
|
||||
downloader="wget"
|
||||
type wget >/dev/null 2>&1 || { downloader="curl"; }
|
||||
if [ "$downloader" = "wget" ]; then
|
||||
_down_prefix="O"
|
||||
else
|
||||
_down_prefix="o"
|
||||
fi
|
||||
_down_symbol=0
|
||||
if [ ! -f "$2" ]; then
|
||||
$downloader "$1" -$_down_prefix "$2" >/dev/null 2>&1 && \
|
||||
echo "完成!" && _down_symbol=1
|
||||
else
|
||||
echo "已存在!" && _down_symbol=1
|
||||
fi
|
||||
if [ $_down_symbol == 0 ]; then
|
||||
echo "失败!请检查网络连接!"
|
||||
rm -rf "$2"
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
function downloadAll() {
|
||||
# 创建文件夹
|
||||
mkdir "$_home_dir""runtime" >/dev/null 2>&1
|
||||
mkdir "$_home_dir""runtime/tmp_download" >/dev/null 2>&1
|
||||
mkdir "$_home_dir""runtime/cellar" >/dev/null 2>&1
|
||||
_down_dir=$_home_dir"runtime/tmp_download/"
|
||||
|
||||
# 下载PHP
|
||||
echo -n "正在下载 php 源码... "
|
||||
downloadIt "http://mirrors.sohu.com/php/php-$_php_ver.tar.gz" "$_down_dir""php.tar.gz" || { exit; }
|
||||
|
||||
# 下载libiconv
|
||||
echo -n "正在下载 libiconv 源码... "
|
||||
downloadIt "https://mirrors.tuna.tsinghua.edu.cn/gnu/libiconv/libiconv-$_libiconv_ver.tar.gz" "$_down_dir""libiconv.tar.gz" || { exit; }
|
||||
|
||||
echo -n "正在下载 openssl 源码... "
|
||||
downloadIt "http://mirrors.cloud.tencent.com/openssl/source/openssl-$_openssl_ver.tar.gz" "$_down_dir""openssl.tar.gz" || { exit; }
|
||||
|
||||
echo -n "正在下载 swoole 源码... "
|
||||
downloadIt "https://dl.zhamao.me/swoole/swoole-$_swoole_ver.tgz" "$_down_dir""swoole.tar.gz" || { exit; }
|
||||
|
||||
echo -n "正在下载 composer ... "
|
||||
downloadIt "https://mirrors.aliyun.com/composer/composer.phar" "$_home_dir""runtime/cellar/composer" || { exit; }
|
||||
|
||||
#echo -n "正在下载 libcurl 源码... "
|
||||
#downloadIt "https://curl.se/download/curl-7.75.0.tar.gz" "$_down_dir""libcurl.tar.gz" || { exit; }
|
||||
}
|
||||
|
||||
function compileIt() {
|
||||
_down_dir="$_home_dir""runtime/tmp_download/"
|
||||
_source_dir="$_home_dir""runtime/tmp_source/"
|
||||
_cellar_dir="$_home_dir""runtime/cellar/"
|
||||
case $1 in
|
||||
"libiconv")
|
||||
if [ -f "$_cellar_dir""libiconv/bin/iconv" ]; then
|
||||
echo "已编译!" && return
|
||||
fi
|
||||
tar -xf "$_down_dir""libiconv.tar.gz" -C "$_source_dir" && \
|
||||
cd "$_source_dir""libiconv-"$_libiconv_ver && \
|
||||
./configure --prefix="$_cellar_dir""libiconv" >/dev/null 2>&1 && \
|
||||
make -j4 >/dev/null 2>&1 && \
|
||||
make install >/dev/null 2>&1 && \
|
||||
echo "完成!"
|
||||
;;
|
||||
"libzip")
|
||||
if [ -f "$_cellar_dir""libzip/bin/libzip" ]; then
|
||||
echo "已编译!" && return
|
||||
fi
|
||||
tar -xf "$_down_dir""libzip.tar.gz" -C "$_source_dir" && \
|
||||
cd "$_source_dir""libzip-1.7.3" && \
|
||||
./configure --prefix="$_cellar_dir""libzip" && \
|
||||
make -j4 && \
|
||||
make install && \
|
||||
echo "完成!"
|
||||
;;
|
||||
"libcurl")
|
||||
if [ -f "$_cellar_dir""libcurl/bin/libcurl" ]; then
|
||||
echo "已编译!" && return
|
||||
fi
|
||||
tar -xf "$_down_dir""libcurl.tar.gz" -C "$_source_dir" && \
|
||||
cd "$_source_dir""libcurl-7.75.0" && \
|
||||
./configure --prefix="$_cellar_dir""libcurl" && \
|
||||
make -j4 && \
|
||||
make install && \
|
||||
echo "完成!"
|
||||
;;
|
||||
"php")
|
||||
if [ -f "$_cellar_dir""php/bin/php" ]; then
|
||||
echo "已编译!" && return
|
||||
fi
|
||||
tar -xf "$_down_dir""php.tar.gz" -C "$_source_dir" && \
|
||||
cd "$_source_dir""php-"$_php_ver && \
|
||||
./buildconf --force && \
|
||||
PKG_CONFIG_PATH="$_cellar_dir""openssl/lib/pkgconfig" ./configure --prefix="$_cellar_dir""php" \
|
||||
--with-config-file-path="$_home_dir""runtime/etc" \
|
||||
--disable-fpm \
|
||||
--enable-cli \
|
||||
--enable-posix \
|
||||
--enable-ctype \
|
||||
--enable-mysqlnd \
|
||||
--enable-pdo \
|
||||
--enable-pcntl \
|
||||
--with-openssl="$_cellar_dir""openssl" \
|
||||
--enable-sockets \
|
||||
--disable-xml \
|
||||
--disable-xmlreader \
|
||||
--disable-xmlwriter \
|
||||
--without-libxml \
|
||||
--disable-dom \
|
||||
--without-sqlite3 \
|
||||
--without-pdo-sqlite \
|
||||
--disable-simplexml \
|
||||
--with-pdo-mysql=mysqlnd \
|
||||
--with-zlib \
|
||||
--with-iconv="$_cellar_dir""libiconv" \
|
||||
--enable-phar && \
|
||||
make -j4 && \
|
||||
make install && \
|
||||
cp "$_source_dir""php-$_php_ver/php.ini-production" "$_home_dir""runtime/etc/php.ini" && \
|
||||
echo "完成!"
|
||||
;;
|
||||
"openssl")
|
||||
if [ -f "$_cellar_dir""openssl/bin/openssl" ]; then
|
||||
echo "已编译!" && return
|
||||
fi
|
||||
tar -xf "$_down_dir""openssl.tar.gz" -C "$_source_dir" && \
|
||||
cd "$_source_dir""openssl-""$_openssl_ver" && \
|
||||
./config --prefix="$_cellar_dir""openssl" && \
|
||||
make -j4 && \
|
||||
make install && \
|
||||
echo "完成!"
|
||||
;;
|
||||
"swoole")
|
||||
"$_home_dir"runtime/cellar/php/bin/php --ri swoole >/dev/null 2>&1
|
||||
# shellcheck disable=SC2181
|
||||
if [ $? == 0 ]; then
|
||||
echo "已编译!" && return
|
||||
fi
|
||||
tar -xf "$_down_dir""swoole.tar.gz" -C "$_source_dir" && \
|
||||
cd "$_source_dir""swoole-""$_swoole_ver" && \
|
||||
PATH="$_cellar_dir""php/bin:$PATH" phpize && \
|
||||
PATH="$_cellar_dir""php/bin:$PATH" ./configure --prefix="$_cellar_dir""php" \
|
||||
--enable-sockets \
|
||||
--enable-http2 \
|
||||
--enable-openssl \
|
||||
--with-openssl-dir="$_cellar_dir""openssl" \
|
||||
--enable-mysqlnd && \
|
||||
make -j4 && \
|
||||
make install && \
|
||||
echo "extension=swoole.so" >> "$_home_dir""runtime/etc/php.ini" && \
|
||||
echo "完成!"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
function compileAll() {
|
||||
_down_dir=$_home_dir"runtime/tmp_download/"
|
||||
_source_dir=$_home_dir"runtime/tmp_source/"
|
||||
mkdir "$_source_dir" >/dev/null 2>&1
|
||||
mkdir "$_home_dir""runtime/etc" >/dev/null 2>&1
|
||||
|
||||
echo -n "正在编译 libiconv ... "
|
||||
compileIt libiconv || { return 1; }
|
||||
|
||||
#echo -n "正在编译 libcurl ... "
|
||||
#compileIt libcurl || { exit; }
|
||||
|
||||
echo -n "正在编译 openssl ... "
|
||||
compileIt openssl || { return 1; }
|
||||
|
||||
#echo -n "正在编译 libzip ... "
|
||||
#compileIt libzip || { exit; }
|
||||
|
||||
echo -n "正在编译 php ... "
|
||||
compileIt php || { return 1; }
|
||||
|
||||
echo -n "正在编译 swoole ... "
|
||||
compileIt swoole || { return 1; }
|
||||
return 0
|
||||
}
|
||||
|
||||
function linkBin(){
|
||||
mkdir "$_home_dir""runtime/bin" >/dev/null 2>&1
|
||||
ln -s "$_home_dir""runtime/cellar/php/bin/php" "$_home_dir""runtime/bin/php" >/dev/null 2>&1
|
||||
echo "runtime/cellar/php/bin/php runtime/cellar/composer \$@" > "$_home_dir""runtime/bin/composer" && chmod +x "$_home_dir""runtime/bin/composer"
|
||||
echo "Done!"
|
||||
runtime/bin/composer config repo.packagist composer https://mirrors.aliyun.com/composer/
|
||||
}
|
||||
|
||||
checkEnv && \
|
||||
downloadAll && \
|
||||
compileAll && \
|
||||
linkBin && \
|
||||
echo "成功部署所有环境!" && \
|
||||
echo -e "composer更新依赖:\t\"runtime/bin/composer update\"" && \
|
||||
echo -e "启动框架(源码模式):\t\"runtime/bin/php bin/start server\"" && \
|
||||
echo -e "启动框架(普通模式):\t\"runtime/bin/php vendor/bin/start server\""
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"name": "zhamao/framework",
|
||||
"description": "High performance QQ robot and web server development framework",
|
||||
"description": "High performance chat robot and web server development framework",
|
||||
"minimum-stability": "stable",
|
||||
"license": "Apache-2.0",
|
||||
"version": "2.2.8",
|
||||
"version": "2.2.10",
|
||||
"extra": {
|
||||
"exclude_annotate": [
|
||||
"src/ZM"
|
||||
@@ -11,12 +11,8 @@
|
||||
},
|
||||
"authors": [
|
||||
{
|
||||
"name": "whale",
|
||||
"email": "crazysnowcc@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "swift",
|
||||
"email": "hugo_swift@yahoo.com"
|
||||
"name": "jerry",
|
||||
"email": "admin@zhamao.me"
|
||||
}
|
||||
],
|
||||
"prefer-stable": true,
|
||||
@@ -52,7 +48,6 @@
|
||||
]
|
||||
},
|
||||
"require-dev": {
|
||||
"swoole/ide-helper": "@dev",
|
||||
"phpunit/phpunit": "^9.5"
|
||||
"swoole/ide-helper": "@dev"
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,7 @@ $config['crash_dir'] = $config['zm_data'] . 'crash/';
|
||||
/** 对应swoole的server->set参数 */
|
||||
$config['swoole'] = [
|
||||
'log_file' => $config['crash_dir'] . 'swoole_error.log',
|
||||
'worker_num' => swoole_cpu_num(), //如果你只有一个 OneBot 实例连接到框架并且代码没有复杂的CPU密集计算,则可把这里改为1使用全局变量
|
||||
//'worker_num' => swoole_cpu_num(), //如果你只有一个 OneBot 实例连接到框架并且代码没有复杂的CPU密集计算,则可把这里改为1使用全局变量
|
||||
'dispatch_mode' => 2, //包分配原则,见 https://wiki.swoole.com/#/server/setting?id=dispatch_mode
|
||||
'max_coroutine' => 300000,
|
||||
//'task_worker_num' => 4,
|
||||
|
||||
59
docs/component/access-token.md
Normal file
59
docs/component/access-token.md
Normal file
@@ -0,0 +1,59 @@
|
||||
# Token 验证
|
||||
|
||||
为了保障安全,框架支持给接入的 WebSocket 连接验证 Token,如果不设置 Token 同时又将框架的端口暴露在公网将会非常危险。
|
||||
|
||||
炸毛框架兼容 OneBot 标准的机器人客户端,所以自带一个 Token 验证器。
|
||||
|
||||
关于 Access Token 方面的标准规范,请参考下面内容:
|
||||
|
||||
- [OneBot - 鉴权](https://github.com/howmanybots/onebot/blob/master/v11/specs/communication/authorization.md)
|
||||
- [go-cqhttp - 配置](https://github.com/Mrs4s/go-cqhttp/blob/master/docs/config.md)
|
||||
|
||||
> 以 go-cqhttp 举例,如果要设置验证,则将 go-cqhttp 配置文件中的 `access_token` 项填入内容即可。
|
||||
|
||||
## 验证位置
|
||||
|
||||
框架对 Token 的验证是内置的,在事件 `open`(WebSocket 连接接入时)触发。
|
||||
|
||||
如果是兼容 OneBot 标准的客户端接入,则一切都是兼容的。
|
||||
|
||||
如果是自定义的其他 WebSocket 客户端也想接入框架,那么其他 WebSocket 客户端也需要进行相应的设置才能利用此 Token 验证。
|
||||
|
||||
如果验证成功(Token 符合要求)则分发事件 `@OnOpenEvent`,否则此事件不触发,同时断开 WebSocket 连接。
|
||||
|
||||
## 标准验证(字符串形式)
|
||||
|
||||
默认的情况下,在框架的全局配置文件 `global.php` 中,对配置项 `access_token` 填入与 OneBot 客户端相同的 `access_token` 即可实现鉴权。下面是一个最基本的和 go-cqhttp 设置鉴权配置:
|
||||
|
||||
go-cqhttp 的配置段:
|
||||
|
||||
```hjson
|
||||
// 访问密钥, 强烈推荐在公网的服务器设置
|
||||
access_token: "emhhbWFvLXJvYm90"
|
||||
```
|
||||
|
||||
框架的配置文件配置段:
|
||||
|
||||
```php
|
||||
/** onebot连接约定的token */
|
||||
$config["access_token"] = 'emhhbWFvLXJvYm90';
|
||||
```
|
||||
|
||||
然后重启框架和 go-cqhttp 即可。(其他 OneBot 客户端同理)
|
||||
|
||||
## 自定义验证(Token 验证)
|
||||
|
||||
有些情况下,使用一个单一的字符串可能无法满足你对 Token 验证的安全需求,需要自定义一些判断模式才能满足,所以框架的 `access_token` 配置项支持动态的闭包函数自行编写判断逻辑,例如下面的一个例子,我可以让框架同时允许接入多个不同 token 的 WebSocket 连接:
|
||||
|
||||
```php
|
||||
/** onebot连接约定的token */
|
||||
$config["access_token"] = function($token){
|
||||
$allow = ['emhhbWFvLXJvYm90','aXMtdmVyeS1nb29k'];
|
||||
if (in_array($token, $allow)) return true;
|
||||
else return false;
|
||||
};
|
||||
```
|
||||
|
||||
## 自定义验证(open 事件)
|
||||
|
||||
当然,这里设置了自定义方式,其实你也可以在下一层的 `@OnOpenEvent` 注解事件中进行自定义内容和判断,具体见 `@OnOpenEvent` 的相关章节。
|
||||
@@ -51,7 +51,7 @@ public function hello() {
|
||||
* @CQCommand("测试fd")
|
||||
*/
|
||||
public function testfd() {
|
||||
ctx()->reply("当前机器人连接的fd是:".ctx()->getFd()",机器人QQ是:".ctx()->getRobotId());
|
||||
ctx()->reply("当前机器人连接的fd是:".ctx()->getFd().",机器人QQ是:".ctx()->getRobotId());
|
||||
}
|
||||
```
|
||||
|
||||
@@ -421,4 +421,5 @@ public function argTest1() {
|
||||
<chat-box>
|
||||
) test abc 334 argtest
|
||||
( 参数内容:abc, 334, argtest
|
||||
</chat-box>
|
||||
</chat-box>
|
||||
|
||||
|
||||
@@ -34,4 +34,21 @@ DataProvider 是框架内提供的一个简易的文件管理类。
|
||||
|
||||
定义:`loadFromJson($filename)`
|
||||
|
||||
文件名同上 `saveToJson()` 的定义,解析后的返回值为原先的内容或 `null`(如果文件不存在或 json 解析失败)。
|
||||
文件名同上 `saveToJson()` 的定义,解析后的返回值为原先的内容或 `null`(如果文件不存在或 json 解析失败)。
|
||||
|
||||
## 其他文件读取
|
||||
|
||||
框架比较贴近原生的 PHP,所以推荐直接使用原生的方法来读写文件(`file_get_contents` 和 `file_put_contents`)。但有一点要注意,框架内最好使用**工作目录或者绝对路径**。
|
||||
|
||||
```php
|
||||
// 读取框架工作目录的文件 composer.json 文件
|
||||
$r = file_get_contents(working_dir() . "/composer.json");
|
||||
|
||||
// 写入 Linux 临时目录下的文件
|
||||
file_put_contents("/tmp/test.txt", "hello world");
|
||||
```
|
||||
|
||||
!!! warning "注意"
|
||||
|
||||
在默认的情况里,框架的根目录均为可写可读的,在读写文件时务必要注意目录的位置和权限。使用 `working_dir()` 获取目录后面需要加 `/` 再追加自己的文件名或子目录名。
|
||||
|
||||
|
||||
@@ -10,29 +10,29 @@
|
||||
|
||||
框架的全局配置文件在 `config/global.php` 文件中。下面是配置文件的各个选项,请根据自己的需要自行配置。
|
||||
|
||||
| 配置名称 | 说明 | 默认值 |
|
||||
| :--------------------------- | ------------------------------------------------ | ---------------------------- |
|
||||
| `host` | 框架监听的地址 | 0.0.0.0 |
|
||||
| `port` | 框架监听的端口 | 20001 |
|
||||
| `http_reverse_link` | 框架开到公网或外部的 HTTP 反代链接 | 见配置文件 |
|
||||
| `zm_data` | 框架的配置文件、日志文件等文件目录 | `./` 下的 `zm_data/` |
|
||||
| `debug_mode` | 框架是否启动 debug 模式 | false |
|
||||
| `crash_dir` | 存放崩溃和运行日志的目录 | `zm_data` 下的 `crash/` |
|
||||
| `swoole` | 对应 Swoole server 中 set 的参数,参考Swoole文档 | 见子表 `swoole` |
|
||||
| `light_cache` | 轻量内置 key-value 缓存 | 见字表 `light_cache` |
|
||||
| `worker_cache` | 跨进程变量级缓存 | 见子表 `worker_cache` |
|
||||
| `sql_config` | MySQL 数据库连接信息 | 见子表 `sql_config` |
|
||||
| `redis_config` | Redis 连接信息 | 见子表 `redis_config` |
|
||||
| `access_token` | OneBot 客户端连接约定的token,留空则无 | 空 |
|
||||
| `http_header` | HTTP 请求自定义返回的header | 见配置文件 |
|
||||
| `http_default_code_page` | HTTP服务器在指定状态码下回复的默认页面 | 见配置文件 |
|
||||
| `init_atomics` | 框架启动时初始化的原子计数器列表 | 见配置文件 |
|
||||
| `info_level` | 终端日志显示等级(0-4) | 2 |
|
||||
| `context_class` | 上下文所定义的类,待上下文完善后见对应文档 | `\ZM\Context\Context::class` |
|
||||
| `static_file_server` | 静态文件服务器配置项 | 见子表 `static_file_server` |
|
||||
| `server_event_handler_class` | 注册 Swoole Server 事件注解的类列表 | 见配置文件 |
|
||||
| `command_register_class` | 注册自定义命令行选项指令的类 | 见配置文件 |
|
||||
| `modules` | 服务器启用的外部第三方和内部插件 | `['onebot' => true]` |
|
||||
| 配置名称 | 说明 | 默认值 |
|
||||
| :--------------------------- | ------------------------------------------------------------ | ---------------------------- |
|
||||
| `host` | 框架监听的地址 | 0.0.0.0 |
|
||||
| `port` | 框架监听的端口 | 20001 |
|
||||
| `http_reverse_link` | 框架开到公网或外部的 HTTP 反代链接 | 见配置文件 |
|
||||
| `zm_data` | 框架的配置文件、日志文件等文件目录 | `./` 下的 `zm_data/` |
|
||||
| `debug_mode` | 框架是否启动 debug 模式 | false |
|
||||
| `crash_dir` | 存放崩溃和运行日志的目录 | `zm_data` 下的 `crash/` |
|
||||
| `swoole` | 对应 Swoole server 中 set 的参数,参考Swoole文档 | 见子表 `swoole` |
|
||||
| `light_cache` | 轻量内置 key-value 缓存 | 见字表 `light_cache` |
|
||||
| `worker_cache` | 跨进程变量级缓存 | 见子表 `worker_cache` |
|
||||
| `sql_config` | MySQL 数据库连接信息 | 见子表 `sql_config` |
|
||||
| `redis_config` | Redis 连接信息 | 见子表 `redis_config` |
|
||||
| `access_token` | OneBot 客户端连接约定的token,留空则无,相关设置见 [组件 - Access Token 验证](component/access-token) | 空 |
|
||||
| `http_header` | HTTP 请求自定义返回的header | 见配置文件 |
|
||||
| `http_default_code_page` | HTTP服务器在指定状态码下回复的默认页面 | 见配置文件 |
|
||||
| `init_atomics` | 框架启动时初始化的原子计数器列表 | 见配置文件 |
|
||||
| `info_level` | 终端日志显示等级(0-4) | 2 |
|
||||
| `context_class` | 上下文所定义的类,待上下文完善后见对应文档 | `\ZM\Context\Context::class` |
|
||||
| `static_file_server` | 静态文件服务器配置项 | 见子表 `static_file_server` |
|
||||
| `server_event_handler_class` | 注册 Swoole Server 事件注解的类列表 | 见配置文件 |
|
||||
| `command_register_class` | 注册自定义命令行选项指令的类 | 见配置文件 |
|
||||
| `modules` | 服务器启用的外部第三方和内部插件 | `['onebot' => true]` |
|
||||
|
||||
### 子表 **swoole**
|
||||
|
||||
|
||||
@@ -1,5 +1,21 @@
|
||||
# 更新日志(v2 版本)
|
||||
|
||||
## v2.2.10
|
||||
|
||||
> 更新时间:2021.3.8
|
||||
|
||||
- 新增:用户态 php 编译脚本 `build-runtime.sh`
|
||||
- 移除:无用的调试信息
|
||||
- 新增:`--show-php-ver` 启动参数
|
||||
|
||||
## v2.2.9
|
||||
|
||||
> 更新时间:2021.3.6
|
||||
|
||||
- 更新:`reply()` 方法传入数组则变为快速相应的 API 操作
|
||||
- 修复:在 Worker 进程下调用 `ZMUtil::reload()` 会导致一些奇怪的 bug
|
||||
- 修复:`reply()` 时会 at 私聊成员的 bug(由 go-cqhttp 导致)
|
||||
|
||||
## v2.2.8
|
||||
|
||||
> 更新时间:2021.3.2
|
||||
|
||||
@@ -88,6 +88,7 @@ nav:
|
||||
- 全局方法: component/global-functions.md
|
||||
- HTTP 和 WebSocket 客户端: component/zmrequest.md
|
||||
- Console 终端: component/console.md
|
||||
- Token 验证: component/access-token.md
|
||||
- 进阶开发:
|
||||
- 进阶开发: advanced/index.md
|
||||
- 框架剖析: advanced/framework-structure.md
|
||||
|
||||
@@ -26,6 +26,7 @@ class RunServerCommand extends Command
|
||||
new InputOption("disable-coroutine", null, null, "关闭协程Hook"),
|
||||
new InputOption("daemon", null, null, "以守护进程的方式运行框架"),
|
||||
new InputOption("watch", null, null, "监听 src/ 目录的文件变化并热更新"),
|
||||
new InputOption("show-php-ver", null, null, "启动时显示PHP版本"),
|
||||
new InputOption("env", null, InputOption::VALUE_REQUIRED, "设置环境类型 (production, development, staging)"),
|
||||
]);
|
||||
$this->setDescription("Run zhamao-framework | 启动框架");
|
||||
|
||||
@@ -104,25 +104,31 @@ class Context implements ContextInterface
|
||||
* @param $msg
|
||||
* @param bool $yield
|
||||
* @return mixed
|
||||
* @noinspection PhpMissingBreakStatementInspection
|
||||
*/
|
||||
public function reply($msg, $yield = false) {
|
||||
switch ($this->getData()["message_type"]) {
|
||||
case "group":
|
||||
$operation["at_sender"] = false;
|
||||
// no break
|
||||
case "private":
|
||||
case "discuss":
|
||||
$this->setCache("has_reply", true);
|
||||
$data = $this->getData();
|
||||
$conn = $this->getConnection();
|
||||
$operation["reply"] = $msg;
|
||||
return (new ZMRobot($conn))->setCallback($yield)->callExtendedAPI(".handle_quick_operation", [
|
||||
"context" => $data,
|
||||
"operation" => $operation
|
||||
]);
|
||||
$data = $this->getData();
|
||||
$conn = $this->getConnection();
|
||||
if (!is_array($msg)) {
|
||||
switch ($this->getData()["message_type"]) {
|
||||
case "group":
|
||||
case "private":
|
||||
case "discuss":
|
||||
$this->setCache("has_reply", true);
|
||||
$operation["reply"] = $msg;
|
||||
$operation["at_sender"] = false;
|
||||
return (new ZMRobot($conn))->setCallback($yield)->callExtendedAPI(".handle_quick_operation", [
|
||||
"context" => $data,
|
||||
"operation" => $operation
|
||||
]);
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
$operation = $msg;
|
||||
return (new ZMRobot($conn))->setCallback(false)->callExtendedAPI(".handle_quick_operation", [
|
||||
"context" => $data,
|
||||
"operation" => $operation
|
||||
]);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function finalReply($msg, $yield = false) {
|
||||
|
||||
@@ -80,11 +80,16 @@ class ServerEventHandler
|
||||
});
|
||||
}
|
||||
Process::signal(SIGINT, function () use ($r) {
|
||||
echo "\r";
|
||||
Console::warning("Server interrupted(SIGINT) on Master.");
|
||||
if ((Framework::$server->inotify ?? null) !== null)
|
||||
/** @noinspection PhpUndefinedFieldInspection */ Event::del(Framework::$server->inotify);
|
||||
ZMUtil::stop();
|
||||
if (zm_atomic("_int_is_reload")->get() === 1) {
|
||||
zm_atomic("_int_is_reload")->set(0);
|
||||
ZMUtil::reload();
|
||||
} else {
|
||||
echo "\r";
|
||||
Console::warning("Server interrupted(SIGINT) on Master.");
|
||||
if ((Framework::$server->inotify ?? null) !== null)
|
||||
/** @noinspection PhpUndefinedFieldInspection */ Event::del(Framework::$server->inotify);
|
||||
ZMUtil::stop();
|
||||
}
|
||||
});
|
||||
if (Framework::$argv["daemon"]) {
|
||||
$daemon_data = json_encode([
|
||||
|
||||
@@ -74,8 +74,7 @@ class Framework
|
||||
die($e->getMessage());
|
||||
}
|
||||
try {
|
||||
self::$server = new Server(ZMConfig::get("global", "host"), ZMConfig::get("global", "port"));
|
||||
$this->server_set = ZMConfig::get("global", "swoole");
|
||||
|
||||
Console::init(
|
||||
ZMConfig::get("global", "info_level") ?? 2,
|
||||
self::$server,
|
||||
@@ -86,16 +85,14 @@ class Framework
|
||||
$timezone = ZMConfig::get("global", "timezone") ?? "Asia/Shanghai";
|
||||
date_default_timezone_set($timezone);
|
||||
|
||||
$this->server_set = ZMConfig::get("global", "swoole");
|
||||
$this->parseCliArgs(self::$argv);
|
||||
|
||||
|
||||
self::$server->set($this->server_set);
|
||||
|
||||
// 打印初始信息
|
||||
$out["listen"] = ZMConfig::get("global", "host") . ":" . ZMConfig::get("global", "port");
|
||||
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["environment"] = $args["env"] === null ? "default" : $args["env"];
|
||||
$out["log_level"] = Console::getLevel();
|
||||
$out["version"] = ZM_VERSION;
|
||||
if (APP_VERSION !== "unknown") $out["app_version"] = APP_VERSION;
|
||||
@@ -104,7 +101,7 @@ class Framework
|
||||
}
|
||||
if (ZMConfig::get("global", "sql_config")["sql_host"] !== "") {
|
||||
$conf = ZMConfig::get("global", "sql_config");
|
||||
$out["mysql_pool"] = $conf["database"] . "@" . $conf["sql_host"] . ":" . $conf["sql_port"];
|
||||
$out["mysql_pool"] = $conf["sql_database"] . "@" . $conf["sql_host"] . ":" . $conf["sql_port"];
|
||||
}
|
||||
if (ZMConfig::get("global", "redis_config")["host"] !== "") {
|
||||
$conf = ZMConfig::get("global", "redis_config");
|
||||
@@ -113,10 +110,17 @@ class Framework
|
||||
if (ZMConfig::get("global", "static_file_server")["status"] !== false) {
|
||||
$out["static_file_server"] = "enabled";
|
||||
}
|
||||
if (self::$argv["show-php-ver"] !== false) {
|
||||
$out["php_version"] = PHP_VERSION;
|
||||
}
|
||||
|
||||
$out["working_dir"] = DataProvider::getWorkingDir();
|
||||
self::printProps($out, $tty_width, $args["log-theme"] === null);
|
||||
|
||||
self::$server = new Server(ZMConfig::get("global", "host"), ZMConfig::get("global", "port"));
|
||||
|
||||
self::$server->set($this->server_set);
|
||||
|
||||
self::printMotd($tty_width);
|
||||
|
||||
global $asd;
|
||||
@@ -170,6 +174,7 @@ class Framework
|
||||
} catch (Exception $e) {
|
||||
Console::error("Framework初始化出现错误,请检查!");
|
||||
Console::error($e->getMessage());
|
||||
Console::debug($e);
|
||||
die;
|
||||
}
|
||||
}
|
||||
@@ -288,6 +293,7 @@ class Framework
|
||||
Console::$theme = $y;
|
||||
}
|
||||
break;
|
||||
case 'show-php-ver':
|
||||
default:
|
||||
//Console::info("Calculating ".$x);
|
||||
//dump($y);
|
||||
@@ -350,6 +356,7 @@ class Framework
|
||||
$line_width = [];
|
||||
$line_data = [];
|
||||
foreach ($out as $k => $v) {
|
||||
start:
|
||||
if (!isset($line_width[$current_line])) {
|
||||
$line_width[$current_line] = $max_border - 2;
|
||||
}
|
||||
@@ -363,11 +370,7 @@ class Framework
|
||||
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;
|
||||
goto start;
|
||||
} else { // 地方够,直接写到后面并另起一行
|
||||
$line_data[$current_line] .= $k . ": ";
|
||||
if ($colorful) $line_data[$current_line] .= TermColor::color8(32);
|
||||
|
||||
@@ -28,6 +28,7 @@ class ZMAtomic
|
||||
self::$atomics[$k] = new Atomic($v);
|
||||
}
|
||||
self::$atomics["stop_signal"] = new Atomic(0);
|
||||
self::$atomics["_int_is_reload"] = new Atomic(0);
|
||||
self::$atomics["wait_msg_id"] = new Atomic(0);
|
||||
self::$atomics["_event_id"] = new Atomic(0);
|
||||
for ($i = 0; $i < 10; ++$i) {
|
||||
|
||||
@@ -11,13 +11,16 @@ use Swoole\Timer;
|
||||
use ZM\Console\Console;
|
||||
use ZM\Store\LightCache;
|
||||
use ZM\Store\LightCacheInside;
|
||||
use ZM\Store\Lock\SpinLock;
|
||||
use ZM\Store\ZMAtomic;
|
||||
use ZM\Store\ZMBuf;
|
||||
|
||||
class ZMUtil
|
||||
{
|
||||
public static function stop() {
|
||||
if (SpinLock::tryLock("_stop_signal") === false) return;
|
||||
Console::warning(Console::setColor("Stopping server...", "red"));
|
||||
if (Console::getLevel() >= 4) Console::trace();
|
||||
LightCache::savePersistence();
|
||||
if (ZMBuf::$terminal !== null)
|
||||
Event::del(ZMBuf::$terminal);
|
||||
@@ -31,6 +34,12 @@ class ZMUtil
|
||||
}
|
||||
|
||||
public static function reload($delay = 800) {
|
||||
if (server()->worker_id !== -1) {
|
||||
Console::info(server()->worker_id);
|
||||
zm_atomic("_int_is_reload")->set(1);
|
||||
system("kill -INT " . intval(server()->master_pid));
|
||||
return;
|
||||
}
|
||||
Console::info(Console::setColor("Reloading server...", "gold"));
|
||||
usleep($delay * 1000);
|
||||
foreach ((LightCacheInside::get("wait_api", "wait_api") ?? []) as $k => $v) {
|
||||
|
||||
Reference in New Issue
Block a user